Bump imapengine from 1.25.4 to 1.25.6 and dependencies

This commit is contained in:
johnnyq
2026-08-26 13:02:29 -04:00
parent 71cfdf5e39
commit 3d9a41bec4
54 changed files with 978 additions and 543 deletions

112
libs/composer.lock generated
View File

@@ -77,16 +77,16 @@
},
{
"name": "directorytree/imapengine",
"version": "v1.25.4",
"version": "v1.25.6",
"source": {
"type": "git",
"url": "https://github.com/DirectoryTree/ImapEngine.git",
"reference": "f2a626c34d0f3b9774a4e729893e84e6360605f6"
"reference": "20655ae7dbe045a64d6c72237ee5ae322af49f67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DirectoryTree/ImapEngine/zipball/f2a626c34d0f3b9774a4e729893e84e6360605f6",
"reference": "f2a626c34d0f3b9774a4e729893e84e6360605f6",
"url": "https://api.github.com/repos/DirectoryTree/ImapEngine/zipball/20655ae7dbe045a64d6c72237ee5ae322af49f67",
"reference": "20655ae7dbe045a64d6c72237ee5ae322af49f67",
"shasum": ""
},
"require": {
@@ -118,7 +118,7 @@
"role": "Developer"
}
],
"description": "A fully-featured IMAP library -- without the PHP extension",
"description": "A fully-featured PHP native IMAP library",
"homepage": "https://github.com/directorytree/imapengine",
"keywords": [
"engine",
@@ -127,7 +127,7 @@
],
"support": {
"issues": "https://github.com/DirectoryTree/ImapEngine/issues",
"source": "https://github.com/DirectoryTree/ImapEngine/tree/v1.25.4"
"source": "https://github.com/DirectoryTree/ImapEngine/tree/v1.25.6"
},
"funding": [
{
@@ -135,7 +135,7 @@
"type": "github"
}
],
"time": "2026-07-30T21:01:32+00:00"
"time": "2026-08-24T15:21:35+00:00"
},
{
"name": "doctrine/lexer",
@@ -283,16 +283,16 @@
},
{
"name": "guzzlehttp/psr7",
"version": "3.0.0",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "b094ded77ee97a6027ad6cf0e8c7b9f88381814c"
"reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b094ded77ee97a6027ad6cf0e8c7b9f88381814c",
"reference": "b094ded77ee97a6027ad6cf0e8c7b9f88381814c",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a3059ba1a84c9139c4ae03cf0f45bea276c97c74",
"reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74",
"shasum": ""
},
"require": {
@@ -382,7 +382,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/3.0.0"
"source": "https://github.com/guzzle/psr7/tree/3.1.0"
},
"funding": [
{
@@ -398,11 +398,11 @@
"type": "tidelift"
}
],
"time": "2026-07-20T13:48:31+00:00"
"time": "2026-08-24T11:02:13+00:00"
},
{
"name": "illuminate/collections",
"version": "v12.64.0",
"version": "v12.68.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/collections.git",
@@ -462,7 +462,7 @@
},
{
"name": "illuminate/conditionable",
"version": "v12.64.0",
"version": "v12.68.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/conditionable.git",
@@ -508,7 +508,7 @@
},
{
"name": "illuminate/contracts",
"version": "v12.64.0",
"version": "v12.68.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/contracts.git",
@@ -556,7 +556,7 @@
},
{
"name": "illuminate/macroable",
"version": "v12.64.0",
"version": "v12.68.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/macroable.git",
@@ -602,16 +602,16 @@
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.15",
"version": "v2.0.16",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "dccd8bcb851bb03fcc005df650b708b57cc52661"
"reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/dccd8bcb851bb03fcc005df650b708b57cc52661",
"reference": "dccd8bcb851bb03fcc005df650b708b57cc52661",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
"reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
"shasum": ""
},
"require": {
@@ -659,20 +659,20 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2026-07-21T16:49:22+00:00"
"time": "2026-08-18T20:28:54+00:00"
},
{
"name": "nesbot/carbon",
"version": "3.13.1",
"version": "3.13.2",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
"reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2"
"reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/2937ad3d1d2c506fd2bc97d571438a95641f44e2",
"reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/a1c54919f5fff9800cd03c32bd01defd5a4061cb",
"reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb",
"shasum": ""
},
"require": {
@@ -764,7 +764,7 @@
"type": "tidelift"
}
],
"time": "2026-07-09T18:23:49+00:00"
"time": "2026-08-08T11:40:35+00:00"
},
{
"name": "php-di/invoker",
@@ -1355,16 +1355,16 @@
},
{
"name": "symfony/mime",
"version": "v7.4.15",
"version": "v7.4.17",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b"
"reference": "bf328d82105831db3e409195db0540ff57f27c80"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
"reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
"url": "https://api.github.com/repos/symfony/mime/zipball/bf328d82105831db3e409195db0540ff57f27c80",
"reference": "bf328d82105831db3e409195db0540ff57f27c80",
"shasum": ""
},
"require": {
@@ -1388,7 +1388,7 @@
"symfony/process": "^6.4|^7.0|^8.0",
"symfony/property-access": "^6.4|^7.0|^8.0",
"symfony/property-info": "^6.4|^7.0|^8.0",
"symfony/serializer": "^6.4.3|^7.0.3|^8.0"
"symfony/serializer": "^6.4.44|^7.4.17|^8.1.5"
},
"type": "library",
"autoload": {
@@ -1420,7 +1420,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.4.15"
"source": "https://github.com/symfony/mime/tree/v7.4.17"
},
"funding": [
{
@@ -1440,7 +1440,7 @@
"type": "tidelift"
}
],
"time": "2026-07-29T07:59:49+00:00"
"time": "2026-08-22T09:04:42+00:00"
},
{
"name": "symfony/polyfill-iconv",
@@ -1528,16 +1528,16 @@
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.38.1",
"version": "v1.42.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "dc21118016c039a66235cf93d96b435ffb282412"
"reference": "51b5ff5ba85452b31ec6f55490b08148612339d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412",
"reference": "dc21118016c039a66235cf93d96b435ffb282412",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/51b5ff5ba85452b31ec6f55490b08148612339d9",
"reference": "51b5ff5ba85452b31ec6f55490b08148612339d9",
"shasum": ""
},
"require": {
@@ -1591,7 +1591,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1"
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.42.0"
},
"funding": [
{
@@ -1611,20 +1611,20 @@
"type": "tidelift"
}
],
"time": "2026-05-25T15:22:23+00:00"
"time": "2026-08-24T10:51:20+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.38.0",
"version": "v1.42.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
"reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502",
"reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502",
"shasum": ""
},
"require": {
@@ -1676,7 +1676,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.42.0"
},
"funding": [
{
@@ -1696,7 +1696,7 @@
"type": "tidelift"
}
],
"time": "2026-05-25T13:48:31+00:00"
"time": "2026-08-07T06:33:24+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@@ -2189,16 +2189,16 @@
},
{
"name": "symfony/translation",
"version": "v7.4.14",
"version": "v7.4.17",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281"
"reference": "2ee1e4a3b32a528a642babe041ff7c440b213b4b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"url": "https://api.github.com/repos/symfony/translation/zipball/2ee1e4a3b32a528a642babe041ff7c440b213b4b",
"reference": "2ee1e4a3b32a528a642babe041ff7c440b213b4b",
"shasum": ""
},
"require": {
@@ -2265,7 +2265,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/translation/tree/v7.4.14"
"source": "https://github.com/symfony/translation/tree/v7.4.17"
},
"funding": [
{
@@ -2285,7 +2285,7 @@
"type": "tidelift"
}
],
"time": "2026-06-06T09:33:19+00:00"
"time": "2026-08-21T17:40:08+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -2581,13 +2581,13 @@
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"platform": {},
"platform-dev": {},
"platform-overrides": {
"php": "8.2.0"
},
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.9.0"
}

View File

@@ -2,6 +2,21 @@
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30::getLoader();

View File

@@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) {
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
@@ -98,10 +108,12 @@ if (PHP_VERSION_ID < 80000) {
}
}
if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
exit(0);
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
}
}
include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';
return include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';

View File

@@ -42,35 +42,37 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
/** @var ?string */
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
@@ -78,8 +80,7 @@ class ClassLoader
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
* @var array<string, string>
*/
private $classMap = array();
@@ -87,29 +88,29 @@ class ClassLoader
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
/** @var string|null */
private $apcuPrefix;
/**
* @var self[]
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return string[]
* @return array<string, list<string>>
*/
public function getPrefixes()
{
@@ -121,8 +122,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
@@ -130,8 +130,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, string>
* @return list<string>
*/
public function getFallbackDirs()
{
@@ -139,8 +138,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, string>
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
@@ -148,8 +146,7 @@ class ClassLoader
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
@@ -157,8 +154,7 @@ class ClassLoader
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
@@ -175,24 +171,25 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
$paths
);
}
@@ -201,19 +198,19 @@ class ClassLoader
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
$paths
);
}
}
@@ -222,9 +219,9 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
@@ -232,17 +229,18 @@ class ClassLoader
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@@ -252,18 +250,18 @@ class ClassLoader
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
$paths
);
}
}
@@ -272,8 +270,8 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
@@ -290,8 +288,8 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
@@ -425,7 +423,8 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
@@ -476,9 +475,9 @@ class ClassLoader
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return self[]
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
@@ -555,18 +554,26 @@ class ClassLoader
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

View File

@@ -21,15 +21,28 @@ use Composer\Semver\VersionParser;
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
* @internal
*/
private static $selfDir = null;
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool
*/
private static $installedIsLocalDir;
/**
* @var bool|null
*/
@@ -37,7 +50,7 @@ class InstalledVersions
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
@@ -96,7 +109,7 @@ class InstalledVersions
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
@@ -117,7 +130,7 @@ class InstalledVersions
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
@@ -241,7 +254,7 @@ class InstalledVersions
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
@@ -255,7 +268,7 @@ class InstalledVersions
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@@ -278,7 +291,7 @@ class InstalledVersions
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
@@ -301,17 +314,35 @@ class InstalledVersions
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
// so we have to assume it does not, and that may result in duplicate data being returned when listing
// all installed packages for example
self::$installedIsLocalDir = false;
}
/**
* @return string
*/
private static function getSelfDir()
{
if (self::$selfDir === null) {
self::$selfDir = strtr(__DIR__, '\\', '/');
}
return self::$selfDir;
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
@@ -320,17 +351,27 @@ class InstalledVersions
}
$installed = array();
$copiedLocalDir = false;
if (self::$canGetVendors) {
$selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
$vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
self::$installedByVendor[$vendorDir] = $required;
$installed[] = $required;
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
self::$installed = $required;
self::$installedIsLocalDir = true;
}
}
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
$copiedLocalDir = true;
}
}
}
@@ -338,12 +379,17 @@ class InstalledVersions
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}
return $installed;
}

View File

@@ -2,7 +2,7 @@
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -2,23 +2,23 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
'2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'5897ea0ac4cccf14d323035e65887801' => $vendorDir . '/symfony/polyfill-php82/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'b33e3d135e5d9e47d845c576147bda89' => $vendorDir . '/php-di/php-di/src/functions.php',
'2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php',
'9d2b9fc6db0f153a0a149fefb182415e' => $vendorDir . '/symfony/polyfill-php84/bootstrap.php',
'606a39d89246991a373564698c2d8383' => $vendorDir . '/symfony/polyfill-php85/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
'23f09fe3194f8c2f70923f90d6702129' => $vendorDir . '/illuminate/collections/functions.php',
'60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php',
);

View File

@@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
@@ -29,7 +29,7 @@ return array(
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'),
'Invoker\\' => array($vendorDir . '/php-di/invoker/src'),
'Illuminate\\Support\\' => array($vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/conditionable', $vendorDir . '/illuminate/macroable'),
'Illuminate\\Support\\' => array($vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/conditionable'),
'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),

View File

@@ -25,56 +25,26 @@ class ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitbadf1d01c367c06fb591106ea3486c30::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitbadf1d01c367c06fb591106ea3486c30::getInitializer($loader));
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitbadf1d01c367c06fb591106ea3486c30::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirebadf1d01c367c06fb591106ea3486c30($fileIdentifier, $file);
$filesToLoad = \Composer\Autoload\ComposerStaticInitbadf1d01c367c06fb591106ea3486c30::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
}
return $loader;
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequirebadf1d01c367c06fb591106ea3486c30($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

View File

@@ -8,30 +8,30 @@ class ComposerStaticInitbadf1d01c367c06fb591106ea3486c30
{
public static $files = array (
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
'2203a247e6fda86070a5e4e07aed533a' => __DIR__ . '/..' . '/symfony/clock/Resources/now.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'5897ea0ac4cccf14d323035e65887801' => __DIR__ . '/..' . '/symfony/polyfill-php82/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'b33e3d135e5d9e47d845c576147bda89' => __DIR__ . '/..' . '/php-di/php-di/src/functions.php',
'2203a247e6fda86070a5e4e07aed533a' => __DIR__ . '/..' . '/symfony/clock/Resources/now.php',
'9d2b9fc6db0f153a0a149fefb182415e' => __DIR__ . '/..' . '/symfony/polyfill-php84/bootstrap.php',
'606a39d89246991a373564698c2d8383' => __DIR__ . '/..' . '/symfony/polyfill-php85/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
'23f09fe3194f8c2f70923f90d6702129' => __DIR__ . '/..' . '/illuminate/collections/functions.php',
'60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php',
);
public static $prefixLengthsPsr4 = array (
'Z' =>
'Z' =>
array (
'ZBateson\\StreamDecorators\\' => 26,
'ZBateson\\MbWrapper\\' => 19,
'ZBateson\\MailMimeParser\\' => 24,
),
'S' =>
'S' =>
array (
'Symfony\\Polyfill\\Php85\\' => 23,
'Symfony\\Polyfill\\Php84\\' => 23,
@@ -47,7 +47,7 @@ class ComposerStaticInitbadf1d01c367c06fb591106ea3486c30
'Symfony\\Component\\Mime\\' => 23,
'Symfony\\Component\\Clock\\' => 24,
),
'P' =>
'P' =>
array (
'Psr\\SimpleCache\\' => 16,
'Psr\\Log\\' => 8,
@@ -55,31 +55,31 @@ class ComposerStaticInitbadf1d01c367c06fb591106ea3486c30
'Psr\\Container\\' => 14,
'Psr\\Clock\\' => 10,
),
'L' =>
'L' =>
array (
'Laravel\\SerializableClosure\\' => 28,
),
'I' =>
'I' =>
array (
'Invoker\\' => 8,
'Illuminate\\Support\\' => 19,
'Illuminate\\Contracts\\' => 21,
),
'G' =>
'G' =>
array (
'GuzzleHttp\\Psr7\\' => 16,
),
'E' =>
'E' =>
array (
'Egulias\\EmailValidator\\' => 23,
),
'D' =>
'D' =>
array (
'Doctrine\\Common\\Lexer\\' => 22,
'DirectoryTree\\ImapEngine\\' => 25,
'DI\\' => 3,
),
'C' =>
'C' =>
array (
'Carbon\\Doctrine\\' => 16,
'Carbon\\' => 7,
@@ -87,134 +87,134 @@ class ComposerStaticInitbadf1d01c367c06fb591106ea3486c30
);
public static $prefixDirsPsr4 = array (
'ZBateson\\StreamDecorators\\' =>
'ZBateson\\StreamDecorators\\' =>
array (
0 => __DIR__ . '/..' . '/zbateson/stream-decorators/src',
),
'ZBateson\\MbWrapper\\' =>
'ZBateson\\MbWrapper\\' =>
array (
0 => __DIR__ . '/..' . '/zbateson/mb-wrapper/src',
),
'ZBateson\\MailMimeParser\\' =>
'ZBateson\\MailMimeParser\\' =>
array (
0 => __DIR__ . '/..' . '/zbateson/mail-mime-parser/src',
),
'Symfony\\Polyfill\\Php85\\' =>
'Symfony\\Polyfill\\Php85\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php85',
),
'Symfony\\Polyfill\\Php84\\' =>
'Symfony\\Polyfill\\Php84\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php84',
),
'Symfony\\Polyfill\\Php83\\' =>
'Symfony\\Polyfill\\Php83\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php83',
),
'Symfony\\Polyfill\\Php82\\' =>
'Symfony\\Polyfill\\Php82\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php82',
),
'Symfony\\Polyfill\\Php80\\' =>
'Symfony\\Polyfill\\Php80\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
'Symfony\\Polyfill\\Mbstring\\' =>
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
),
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
),
'Symfony\\Polyfill\\Intl\\Idn\\' =>
'Symfony\\Polyfill\\Intl\\Idn\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
),
'Symfony\\Polyfill\\Iconv\\' =>
'Symfony\\Polyfill\\Iconv\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-iconv',
),
'Symfony\\Contracts\\Translation\\' =>
'Symfony\\Contracts\\Translation\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/translation-contracts',
),
'Symfony\\Component\\Translation\\' =>
'Symfony\\Component\\Translation\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/translation',
),
'Symfony\\Component\\Mime\\' =>
'Symfony\\Component\\Mime\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/mime',
),
'Symfony\\Component\\Clock\\' =>
'Symfony\\Component\\Clock\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/clock',
),
'Psr\\SimpleCache\\' =>
'Psr\\SimpleCache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
),
'Psr\\Log\\' =>
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/src',
),
'Psr\\Http\\Message\\' =>
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-factory/src',
1 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Psr\\Container\\' =>
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/psr/container/src',
),
'Psr\\Clock\\' =>
'Psr\\Clock\\' =>
array (
0 => __DIR__ . '/..' . '/psr/clock/src',
),
'Laravel\\SerializableClosure\\' =>
'Laravel\\SerializableClosure\\' =>
array (
0 => __DIR__ . '/..' . '/laravel/serializable-closure/src',
),
'Invoker\\' =>
'Invoker\\' =>
array (
0 => __DIR__ . '/..' . '/php-di/invoker/src',
),
'Illuminate\\Support\\' =>
'Illuminate\\Support\\' =>
array (
0 => __DIR__ . '/..' . '/illuminate/collections',
1 => __DIR__ . '/..' . '/illuminate/conditionable',
2 => __DIR__ . '/..' . '/illuminate/macroable',
1 => __DIR__ . '/..' . '/illuminate/macroable',
2 => __DIR__ . '/..' . '/illuminate/conditionable',
),
'Illuminate\\Contracts\\' =>
'Illuminate\\Contracts\\' =>
array (
0 => __DIR__ . '/..' . '/illuminate/contracts',
),
'GuzzleHttp\\Psr7\\' =>
'GuzzleHttp\\Psr7\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
),
'Egulias\\EmailValidator\\' =>
'Egulias\\EmailValidator\\' =>
array (
0 => __DIR__ . '/..' . '/egulias/email-validator/src',
),
'Doctrine\\Common\\Lexer\\' =>
'Doctrine\\Common\\Lexer\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/lexer/src',
),
'DirectoryTree\\ImapEngine\\' =>
'DirectoryTree\\ImapEngine\\' =>
array (
0 => __DIR__ . '/..' . '/directorytree/imapengine/src',
),
'DI\\' =>
'DI\\' =>
array (
0 => __DIR__ . '/..' . '/php-di/php-di/src',
),
'Carbon\\Doctrine\\' =>
'Carbon\\Doctrine\\' =>
array (
0 => __DIR__ . '/..' . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine',
),
'Carbon\\' =>
'Carbon\\' =>
array (
0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
),

View File

@@ -74,17 +74,17 @@
},
{
"name": "directorytree/imapengine",
"version": "v1.25.4",
"version_normalized": "1.25.4.0",
"version": "v1.25.6",
"version_normalized": "1.25.6.0",
"source": {
"type": "git",
"url": "https://github.com/DirectoryTree/ImapEngine.git",
"reference": "f2a626c34d0f3b9774a4e729893e84e6360605f6"
"reference": "20655ae7dbe045a64d6c72237ee5ae322af49f67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DirectoryTree/ImapEngine/zipball/f2a626c34d0f3b9774a4e729893e84e6360605f6",
"reference": "f2a626c34d0f3b9774a4e729893e84e6360605f6",
"url": "https://api.github.com/repos/DirectoryTree/ImapEngine/zipball/20655ae7dbe045a64d6c72237ee5ae322af49f67",
"reference": "20655ae7dbe045a64d6c72237ee5ae322af49f67",
"shasum": ""
},
"require": {
@@ -99,7 +99,7 @@
"pestphp/pest": "^2.0|^3.0|^4.0",
"spatie/ray": "^1.0"
},
"time": "2026-07-30T21:01:32+00:00",
"time": "2026-08-24T15:21:35+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -118,7 +118,7 @@
"role": "Developer"
}
],
"description": "A fully-featured IMAP library -- without the PHP extension",
"description": "A fully-featured PHP native IMAP library",
"homepage": "https://github.com/directorytree/imapengine",
"keywords": [
"engine",
@@ -127,7 +127,7 @@
],
"support": {
"issues": "https://github.com/DirectoryTree/ImapEngine/issues",
"source": "https://github.com/DirectoryTree/ImapEngine/tree/v1.25.4"
"source": "https://github.com/DirectoryTree/ImapEngine/tree/v1.25.6"
},
"funding": [
{
@@ -289,17 +289,17 @@
},
{
"name": "guzzlehttp/psr7",
"version": "3.0.0",
"version_normalized": "3.0.0.0",
"version": "3.1.0",
"version_normalized": "3.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "b094ded77ee97a6027ad6cf0e8c7b9f88381814c"
"reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b094ded77ee97a6027ad6cf0e8c7b9f88381814c",
"reference": "b094ded77ee97a6027ad6cf0e8c7b9f88381814c",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a3059ba1a84c9139c4ae03cf0f45bea276c97c74",
"reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74",
"shasum": ""
},
"require": {
@@ -323,7 +323,7 @@
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"time": "2026-07-20T13:48:31+00:00",
"time": "2026-08-24T11:02:13+00:00",
"type": "library",
"extra": {
"bamarni-bin": {
@@ -391,7 +391,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/3.0.0"
"source": "https://github.com/guzzle/psr7/tree/3.1.0"
},
"funding": [
{
@@ -411,8 +411,8 @@
},
{
"name": "illuminate/collections",
"version": "v12.64.0",
"version_normalized": "12.64.0.0",
"version": "v12.68.0",
"version_normalized": "12.68.0.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/collections.git",
@@ -474,8 +474,8 @@
},
{
"name": "illuminate/conditionable",
"version": "v12.64.0",
"version_normalized": "12.64.0.0",
"version": "v12.68.0",
"version_normalized": "12.68.0.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/conditionable.git",
@@ -523,8 +523,8 @@
},
{
"name": "illuminate/contracts",
"version": "v12.64.0",
"version_normalized": "12.64.0.0",
"version": "v12.68.0",
"version_normalized": "12.68.0.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/contracts.git",
@@ -574,8 +574,8 @@
},
{
"name": "illuminate/macroable",
"version": "v12.64.0",
"version_normalized": "12.64.0.0",
"version": "v12.68.0",
"version_normalized": "12.68.0.0",
"source": {
"type": "git",
"url": "https://github.com/illuminate/macroable.git",
@@ -623,17 +623,17 @@
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.15",
"version_normalized": "2.0.15.0",
"version": "v2.0.16",
"version_normalized": "2.0.16.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "dccd8bcb851bb03fcc005df650b708b57cc52661"
"reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/dccd8bcb851bb03fcc005df650b708b57cc52661",
"reference": "dccd8bcb851bb03fcc005df650b708b57cc52661",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
"reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
"shasum": ""
},
"require": {
@@ -646,7 +646,7 @@
"phpstan/phpstan": "^2.0",
"symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
},
"time": "2026-07-21T16:49:22+00:00",
"time": "2026-08-18T20:28:54+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -687,17 +687,17 @@
},
{
"name": "nesbot/carbon",
"version": "3.13.1",
"version_normalized": "3.13.1.0",
"version": "3.13.2",
"version_normalized": "3.13.2.0",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
"reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2"
"reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/2937ad3d1d2c506fd2bc97d571438a95641f44e2",
"reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/a1c54919f5fff9800cd03c32bd01defd5a4061cb",
"reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb",
"shasum": ""
},
"require": {
@@ -723,7 +723,7 @@
"phpunit/phpunit": "^10.5.53",
"squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0"
},
"time": "2026-07-09T18:23:49+00:00",
"time": "2026-08-08T11:40:35+00:00",
"bin": [
"bin/carbon"
],
@@ -1412,17 +1412,17 @@
},
{
"name": "symfony/mime",
"version": "v7.4.15",
"version_normalized": "7.4.15.0",
"version": "v7.4.17",
"version_normalized": "7.4.17.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b"
"reference": "bf328d82105831db3e409195db0540ff57f27c80"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
"reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
"url": "https://api.github.com/repos/symfony/mime/zipball/bf328d82105831db3e409195db0540ff57f27c80",
"reference": "bf328d82105831db3e409195db0540ff57f27c80",
"shasum": ""
},
"require": {
@@ -1446,9 +1446,9 @@
"symfony/process": "^6.4|^7.0|^8.0",
"symfony/property-access": "^6.4|^7.0|^8.0",
"symfony/property-info": "^6.4|^7.0|^8.0",
"symfony/serializer": "^6.4.3|^7.0.3|^8.0"
"symfony/serializer": "^6.4.44|^7.4.17|^8.1.5"
},
"time": "2026-07-29T07:59:49+00:00",
"time": "2026-08-22T09:04:42+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1480,7 +1480,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.4.15"
"source": "https://github.com/symfony/mime/tree/v7.4.17"
},
"funding": [
{
@@ -1591,17 +1591,17 @@
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.38.1",
"version_normalized": "1.38.1.0",
"version": "v1.42.0",
"version_normalized": "1.42.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "dc21118016c039a66235cf93d96b435ffb282412"
"reference": "51b5ff5ba85452b31ec6f55490b08148612339d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412",
"reference": "dc21118016c039a66235cf93d96b435ffb282412",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/51b5ff5ba85452b31ec6f55490b08148612339d9",
"reference": "51b5ff5ba85452b31ec6f55490b08148612339d9",
"shasum": ""
},
"require": {
@@ -1611,7 +1611,7 @@
"suggest": {
"ext-intl": "For best performance"
},
"time": "2026-05-25T15:22:23+00:00",
"time": "2026-08-24T10:51:20+00:00",
"type": "library",
"extra": {
"thanks": {
@@ -1657,7 +1657,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1"
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.42.0"
},
"funding": [
{
@@ -1681,17 +1681,17 @@
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.38.0",
"version_normalized": "1.38.0.0",
"version": "v1.42.0",
"version_normalized": "1.42.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
"reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502",
"reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502",
"shasum": ""
},
"require": {
@@ -1700,7 +1700,7 @@
"suggest": {
"ext-intl": "For best performance"
},
"time": "2026-05-25T13:48:31+00:00",
"time": "2026-08-07T06:33:24+00:00",
"type": "library",
"extra": {
"thanks": {
@@ -1745,7 +1745,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.42.0"
},
"funding": [
{
@@ -2276,17 +2276,17 @@
},
{
"name": "symfony/translation",
"version": "v7.4.14",
"version_normalized": "7.4.14.0",
"version": "v7.4.17",
"version_normalized": "7.4.17.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281"
"reference": "2ee1e4a3b32a528a642babe041ff7c440b213b4b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"url": "https://api.github.com/repos/symfony/translation/zipball/2ee1e4a3b32a528a642babe041ff7c440b213b4b",
"reference": "2ee1e4a3b32a528a642babe041ff7c440b213b4b",
"shasum": ""
},
"require": {
@@ -2324,7 +2324,7 @@
"symfony/service-contracts": "^2.5|^3",
"symfony/yaml": "^6.4|^7.0|^8.0"
},
"time": "2026-06-06T09:33:19+00:00",
"time": "2026-08-21T17:40:08+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2355,7 +2355,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/translation/tree/v7.4.14"
"source": "https://github.com/symfony/translation/tree/v7.4.17"
},
"funding": [
{

View File

@@ -1,148 +1,148 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '71cfdf5e39d3e23e029f3d6c8d7bfc84212a82b3',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'cf4446f405d95f3fc538a2d3e0aec33d52f075fc',
'name' => '__root__',
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '71cfdf5e39d3e23e029f3d6c8d7bfc84212a82b3',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'cf4446f405d95f3fc538a2d3e0aec33d52f075fc',
'dev_requirement' => false,
),
'carbonphp/carbon-doctrine-types' => array(
'pretty_version' => '3.2.0',
'version' => '3.2.0.0',
'reference' => '18ba5ddfec8976260ead6e866180bd5d2f71aa1d',
'type' => 'library',
'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types',
'aliases' => array(),
'reference' => '18ba5ddfec8976260ead6e866180bd5d2f71aa1d',
'dev_requirement' => false,
),
'directorytree/imapengine' => array(
'pretty_version' => 'v1.25.4',
'version' => '1.25.4.0',
'pretty_version' => 'v1.25.6',
'version' => '1.25.6.0',
'reference' => '20655ae7dbe045a64d6c72237ee5ae322af49f67',
'type' => 'library',
'install_path' => __DIR__ . '/../directorytree/imapengine',
'aliases' => array(),
'reference' => 'f2a626c34d0f3b9774a4e729893e84e6360605f6',
'dev_requirement' => false,
),
'doctrine/lexer' => array(
'pretty_version' => '3.0.1',
'version' => '3.0.1.0',
'reference' => '31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/lexer',
'aliases' => array(),
'reference' => '31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd',
'dev_requirement' => false,
),
'egulias/email-validator' => array(
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'reference' => 'd42c8731f0624ad6bdc8d3e5e9a4524f68801cfa',
'type' => 'library',
'install_path' => __DIR__ . '/../egulias/email-validator',
'aliases' => array(),
'reference' => 'd42c8731f0624ad6bdc8d3e5e9a4524f68801cfa',
'dev_requirement' => false,
),
'guzzlehttp/psr7' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'pretty_version' => '3.1.0',
'version' => '3.1.0.0',
'reference' => 'a3059ba1a84c9139c4ae03cf0f45bea276c97c74',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(),
'reference' => 'b094ded77ee97a6027ad6cf0e8c7b9f88381814c',
'dev_requirement' => false,
),
'illuminate/collections' => array(
'pretty_version' => 'v12.64.0',
'version' => '12.64.0.0',
'pretty_version' => 'v12.68.0',
'version' => '12.68.0.0',
'reference' => '83313b009c4afb6f02dbc090bdb67809756eefa2',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/collections',
'aliases' => array(),
'reference' => '83313b009c4afb6f02dbc090bdb67809756eefa2',
'dev_requirement' => false,
),
'illuminate/conditionable' => array(
'pretty_version' => 'v12.64.0',
'version' => '12.64.0.0',
'pretty_version' => 'v12.68.0',
'version' => '12.68.0.0',
'reference' => 'ec677967c1f2faf90b8428919124d2184a4c9b49',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/conditionable',
'aliases' => array(),
'reference' => 'ec677967c1f2faf90b8428919124d2184a4c9b49',
'dev_requirement' => false,
),
'illuminate/contracts' => array(
'pretty_version' => 'v12.64.0',
'version' => '12.64.0.0',
'pretty_version' => 'v12.68.0',
'version' => '12.68.0.0',
'reference' => 'c16fd7ba7d8e6b8f336639ceb6b7e1ff6ed0efb5',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/contracts',
'aliases' => array(),
'reference' => 'c16fd7ba7d8e6b8f336639ceb6b7e1ff6ed0efb5',
'dev_requirement' => false,
),
'illuminate/macroable' => array(
'pretty_version' => 'v12.64.0',
'version' => '12.64.0.0',
'pretty_version' => 'v12.68.0',
'version' => '12.68.0.0',
'reference' => 'e295d62d89dcdb87e2b1bd70dd14d074a0ed73cc',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/macroable',
'aliases' => array(),
'reference' => 'e295d62d89dcdb87e2b1bd70dd14d074a0ed73cc',
'dev_requirement' => false,
),
'laravel/serializable-closure' => array(
'pretty_version' => 'v2.0.15',
'version' => '2.0.15.0',
'pretty_version' => 'v2.0.16',
'version' => '2.0.16.0',
'reference' => '7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed',
'type' => 'library',
'install_path' => __DIR__ . '/../laravel/serializable-closure',
'aliases' => array(),
'reference' => 'dccd8bcb851bb03fcc005df650b708b57cc52661',
'dev_requirement' => false,
),
'nesbot/carbon' => array(
'pretty_version' => '3.13.1',
'version' => '3.13.1.0',
'pretty_version' => '3.13.2',
'version' => '3.13.2.0',
'reference' => 'a1c54919f5fff9800cd03c32bd01defd5a4061cb',
'type' => 'library',
'install_path' => __DIR__ . '/../nesbot/carbon',
'aliases' => array(),
'reference' => '2937ad3d1d2c506fd2bc97d571438a95641f44e2',
'dev_requirement' => false,
),
'php-di/invoker' => array(
'pretty_version' => '2.3.7',
'version' => '2.3.7.0',
'reference' => '3c1ddfdef181431fbc4be83378f6d036d59e81e1',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/invoker',
'aliases' => array(),
'reference' => '3c1ddfdef181431fbc4be83378f6d036d59e81e1',
'dev_requirement' => false,
),
'php-di/php-di' => array(
'pretty_version' => '7.1.1',
'version' => '7.1.1.0',
'reference' => 'f88054cc052e40dbe7b383c8817c19442d480352',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/php-di',
'aliases' => array(),
'reference' => 'f88054cc052e40dbe7b383c8817c19442d480352',
'dev_requirement' => false,
),
'psr/clock' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/clock',
'aliases' => array(),
'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
'dev_requirement' => false,
),
'psr/clock-implementation' => array(
@@ -154,10 +154,10 @@
'psr/container' => array(
'pretty_version' => '2.0.2',
'version' => '2.0.2.0',
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/container',
'aliases' => array(),
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
'dev_requirement' => false,
),
'psr/container-implementation' => array(
@@ -169,10 +169,10 @@
'psr/http-factory' => array(
'pretty_version' => '1.1.0',
'version' => '1.1.0.0',
'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(),
'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
'dev_requirement' => false,
),
'psr/http-factory-implementation' => array(
@@ -184,10 +184,10 @@
'psr/http-message' => array(
'pretty_version' => '2.0',
'version' => '2.0.0.0',
'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'dev_requirement' => false,
),
'psr/http-message-implementation' => array(
@@ -199,145 +199,145 @@
'psr/log' => array(
'pretty_version' => '3.0.2',
'version' => '3.0.2.0',
'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
'dev_requirement' => false,
),
'psr/simple-cache' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/simple-cache',
'aliases' => array(),
'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865',
'dev_requirement' => false,
),
'symfony/clock' => array(
'pretty_version' => 'v7.4.8',
'version' => '7.4.8.0',
'reference' => '674fa3b98e21531dd040e613479f5f6fa8f32111',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/clock',
'aliases' => array(),
'reference' => '674fa3b98e21531dd040e613479f5f6fa8f32111',
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v3.7.1',
'version' => '3.7.1.0',
'reference' => 'f3202fa1b5097b0af062dc978b32ecf63404e31d',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
'reference' => 'f3202fa1b5097b0af062dc978b32ecf63404e31d',
'dev_requirement' => false,
),
'symfony/mime' => array(
'pretty_version' => 'v7.4.15',
'version' => '7.4.15.0',
'pretty_version' => 'v7.4.17',
'version' => '7.4.17.0',
'reference' => 'bf328d82105831db3e409195db0540ff57f27c80',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/mime',
'aliases' => array(),
'reference' => '0c1daf58bc931628df0bea26840d1fc8b9a3d34b',
'dev_requirement' => false,
),
'symfony/polyfill-iconv' => array(
'pretty_version' => 'v1.37.0',
'version' => '1.37.0.0',
'reference' => '2c5729fd241b4b22f6e4b436bc3354a4f262df57',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-iconv',
'aliases' => array(),
'reference' => '2c5729fd241b4b22f6e4b436bc3354a4f262df57',
'dev_requirement' => false,
),
'symfony/polyfill-intl-idn' => array(
'pretty_version' => 'v1.38.1',
'version' => '1.38.1.0',
'pretty_version' => 'v1.42.0',
'version' => '1.42.0.0',
'reference' => '51b5ff5ba85452b31ec6f55490b08148612339d9',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
'aliases' => array(),
'reference' => 'dc21118016c039a66235cf93d96b435ffb282412',
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.38.0',
'version' => '1.38.0.0',
'pretty_version' => 'v1.42.0',
'version' => '1.42.0.0',
'reference' => 'aa20edea75bd9c48cfecc8360922e5a6e5c44502',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'reference' => '2d446c214bdbe5b71bde5011b060a05fece3ae6b',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.38.2',
'version' => '1.38.2.0',
'reference' => 'd3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'reference' => 'd3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.37.0',
'version' => '1.37.0.0',
'reference' => 'dfb55726c3a76ea3b6459fcfda1ec2d80a682411',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => 'dfb55726c3a76ea3b6459fcfda1ec2d80a682411',
'dev_requirement' => false,
),
'symfony/polyfill-php82' => array(
'pretty_version' => 'v1.38.1',
'version' => '1.38.1.0',
'reference' => '002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php82',
'aliases' => array(),
'reference' => '002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b',
'dev_requirement' => false,
),
'symfony/polyfill-php83' => array(
'pretty_version' => 'v1.41.0',
'version' => '1.41.0.0',
'reference' => '5ea99087fb99c273a9b9236ed4c31e78b16103c6',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php83',
'aliases' => array(),
'reference' => '5ea99087fb99c273a9b9236ed4c31e78b16103c6',
'dev_requirement' => false,
),
'symfony/polyfill-php84' => array(
'pretty_version' => 'v1.38.1',
'version' => '1.38.1.0',
'reference' => 'f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php84',
'aliases' => array(),
'reference' => 'f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa',
'dev_requirement' => false,
),
'symfony/polyfill-php85' => array(
'pretty_version' => 'v1.41.0',
'version' => '1.41.0.0',
'reference' => '255fab485aaa1006ed411040c42aecd7b5302d7a',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php85',
'aliases' => array(),
'reference' => '255fab485aaa1006ed411040c42aecd7b5302d7a',
'dev_requirement' => false,
),
'symfony/translation' => array(
'pretty_version' => 'v7.4.14',
'version' => '7.4.14.0',
'pretty_version' => 'v7.4.17',
'version' => '7.4.17.0',
'reference' => '2ee1e4a3b32a528a642babe041ff7c440b213b4b',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation',
'aliases' => array(),
'reference' => 'a1af4dacb24eb7ef4f1ca71b94da8ddbce572281',
'dev_requirement' => false,
),
'symfony/translation-contracts' => array(
'pretty_version' => 'v3.7.1',
'version' => '3.7.1.0',
'reference' => 'ccb206b98faccc511ebae8e5fad50f2dc0b30621',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation-contracts',
'aliases' => array(),
'reference' => 'ccb206b98faccc511ebae8e5fad50f2dc0b30621',
'dev_requirement' => false,
),
'symfony/translation-implementation' => array(
@@ -349,28 +349,28 @@
'zbateson/mail-mime-parser' => array(
'pretty_version' => '4.0.3',
'version' => '4.0.3.0',
'reference' => 'f0f7ea3d5fc07471187e6369a7f32dbfea6220e0',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/mail-mime-parser',
'aliases' => array(),
'reference' => 'f0f7ea3d5fc07471187e6369a7f32dbfea6220e0',
'dev_requirement' => false,
),
'zbateson/mb-wrapper' => array(
'pretty_version' => '3.0.1',
'version' => '3.0.1.0',
'reference' => '36dd227ed698d9f5fe995ea75dbcc594a2d880bb',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/mb-wrapper',
'aliases' => array(),
'reference' => '36dd227ed698d9f5fe995ea75dbcc594a2d880bb',
'dev_requirement' => false,
),
'zbateson/stream-decorators' => array(
'pretty_version' => '3.0.2',
'version' => '3.0.2.0',
'reference' => '4776323764ec592de417ef54f3c8fc96fd242849',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/stream-decorators',
'aliases' => array(),
'reference' => '4776323764ec592de417ef54f3c8fc96fd242849',
'dev_requirement' => false,
),
),

View File

@@ -19,8 +19,7 @@ if ($issues) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
throw new \RuntimeException(
'Composer detected issues in your platform: ' . implode(' ', $issues)
);
}

View File

@@ -1,7 +1,7 @@
{
"name": "directorytree/imapengine",
"type": "library",
"description": "A fully-featured IMAP library -- without the PHP extension",
"description": "A fully-featured PHP native IMAP library",
"keywords": [
"imap",
"mail",

View File

@@ -9,7 +9,7 @@ class ImapCommand implements Stringable
/**
* The compiled command lines.
*
* @var string[]
* @var ImapCommandLine[]|null
*/
protected ?array $compiled = null;
@@ -49,7 +49,7 @@ class ImapCommand implements Stringable
/**
* Compile the command into lines for transmission.
*
* @return string[]
* @return ImapCommandLine[]
*/
public function compile(): array
{
@@ -66,9 +66,12 @@ class ImapCommand implements Stringable
// For tokens provided as arrays, the first element is a placeholder
// (for example, "{20}") that signals a literal value will follow.
// The second element holds the actual literal content.
[$placeholder, $literal] = $token;
[$length, $literal] = $token;
$lines[] = "{$line} {$placeholder}";
$lines[] = new ImapCommandLine(
value: "{$line} {$length}",
synchronizing: ! str_contains($length, '+'),
);
$line = $literal;
} else {
@@ -76,7 +79,7 @@ class ImapCommand implements Stringable
}
}
$lines[] = $line;
$lines[] = new ImapCommandLine($line);
return $this->compiled = $lines;
}

View File

@@ -0,0 +1,24 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use Stringable;
class ImapCommandLine implements Stringable
{
/**
* Constructor.
*/
public function __construct(
public string $value,
public bool $synchronizing = false,
) {}
/**
* Get the command line as a string.
*/
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -259,7 +259,7 @@ class ImapConnection implements ConnectionInterface
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->firstWhere(
return $this->result->responses()->untagged()->first(
fn (UntaggedResponse $response) => $response->type()->is('STATUS')
);
}
@@ -621,8 +621,7 @@ class ImapConnection implements ConnectionInterface
public function send(string $name, array $tokens = [], ?string &$tag = null): void
{
if (! $tag) {
$this->sequence++;
$tag = 'TAG'.$this->sequence;
$tag = 'TAG'.++$this->sequence;
}
$command = new ImapCommand($tag, $name, $tokens);
@@ -633,7 +632,11 @@ class ImapConnection implements ConnectionInterface
$this->setResult(new Result($command));
foreach ($command->compile() as $line) {
$this->write($line);
$this->write($line->value);
if ($line->synchronizing) {
$this->assertContinuationResponse($command);
}
}
}
@@ -739,6 +742,23 @@ class ImapConnection implements ConnectionInterface
return $response;
}
/**
* Assert the server is ready to receive literal data.
*/
protected function assertContinuationResponse(ImapCommand $command): void
{
$this->assertNextResponse(
fn (Response $response) => (
$response instanceof ContinuationResponse || (
$response instanceof TaggedResponse
&& $response->tag()->is($command->tag())
)
),
fn (Response $response) => $response instanceof ContinuationResponse,
fn (Response $response) => ImapCommandException::make($command, $response),
);
}
/**
* Assert the next response matches the given filter and assertion.
*

View File

@@ -363,7 +363,7 @@ class ImapTokenizer
/**
* Reads an atom token.
*
* ATOMs are sequences of printable ASCII characters that do not contain delimiters.
* ATOMs are sequences of non-control characters that do not contain delimiters.
*/
protected function readAtom(): Atom
{
@@ -496,20 +496,16 @@ class ImapTokenizer
*/
protected function isValidAtomCharacter(string $char): bool
{
// Get the ASCII code.
$code = ord($char);
// Allow only printable ASCII (32-126).
if ($code < 32 || $code > 126) {
// Reject control characters and DEL while tolerating the
// 8-bit response text returned by some IMAP servers.
if ($code < 0x20 || $code === 0x7F) {
return false;
}
// Delimiters are not allowed inside ATOMs.
if ($this->isDelimiter($char)) {
return false;
}
return true;
return ! $this->isDelimiter($char);
}
/**

View File

@@ -229,4 +229,14 @@ class FakeStream implements StreamInterface
Assert::assertTrue($found, "Failed asserting that the string '{$string}' was written to the stream.");
}
/**
* Assert that the given data was not written to the stream.
*/
public function assertNotWritten(string $string): void
{
foreach ($this->written as $written) {
Assert::assertStringNotContainsString($string, $written);
}
}
}

View File

@@ -3,6 +3,7 @@
namespace DirectoryTree\ImapEngine\Support;
use BackedEnum;
use Illuminate\Support\Collection;
class Str
{
@@ -75,18 +76,12 @@ class Str
}
/**
* Make a range set for use in a search command.
* Make an IMAP sequence set.
*/
public static function set(int|string|array $from, int|float|string|null $to = null): string
{
// If $from is an array with multiple elements, return them as a comma-separated list.
if (is_array($from) && count($from) > 1) {
return implode(',', $from);
}
// If $from is an array with a single element, return that element.
if (is_array($from) && count($from) === 1) {
return (string) reset($from);
if (is_array($from)) {
return static::toSequenceSet($from);
}
// At this point, $from is an integer. No upper bound provided, return $from as a string.
@@ -103,6 +98,44 @@ class Str
return $from.':'.$to;
}
/**
* Convert the values into an IMAP sequence set.
*
* @param array<int, int|string> $values
*/
protected static function toSequenceSet(array $values): string
{
return Collection::make(array_values($values))
->chunkWhile(function (int|string $value, int $key, Collection $range) {
$previous = $range->last();
if (! is_numeric($value) || ! is_numeric($previous)) {
return false;
}
$difference = (int) $value - (int) $previous;
$direction = (int) $previous <=> (int) $range->first();
return in_array($difference, [-1, 1], true)
&& ($range->count() === 1 || $difference === $direction);
})
->map(fn (Collection $range) => static::toSequenceRange(
$range->first(),
$range->last(),
))
->implode(',');
}
/**
* Convert the values into an IMAP sequence range.
*/
protected static function toSequenceRange(int|string $start, int|string $end): string
{
return (string) $start === (string) $end
? (string) $start
: $start.':'.$end;
}
/**
* Make a credentials string for use in the AUTHENTICATE command.
*/

View File

@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 3.1.0 - 2026-08-24
### Added
- Add `Utils::redactUriForMessage()` and `Utils::redactUriStringForMessage()` for URI diagnostics
- Add support for PHP 8.6
### Changed
- Omit rejected header values and sensitive URI components from automatic exception messages
## 3.0.1 - 2026-08-24
### Fixed
- Prefix relative paths that begin with a colon segment with `./` instead of throwing
- Apply the `/.` prefix for authority-less `//` paths to percent-encoding normalizations as well
- Keep colon-leading first path segments when reading the paths of scheme-less non-native URIs
- Stop throwing when removing the default `file` host strands a `//` path, prefixing it with `/.`
## 3.0.0 - 2026-07-20
### Added

View File

@@ -19,8 +19,8 @@ composer require guzzlehttp/psr7
| Version | Status | PHP Version |
|---------|--------------|--------------|
| 3.0 | Latest | >=7.4,<8.6 |
| 2.13 | Maintenance | >=7.2.5,<8.6 |
| 3.1 | Latest | >=7.4,<8.7 |
| 2.13 | Maintenance | >=7.2.5,<8.7 |
| 1.9 | End of Life | >=5.4,<8.2 |
## Quick Start

View File

@@ -32,6 +32,28 @@ userinfo.
A URI that does not parse has no trustworthy authority boundary, so everything
between any scheme and its last `@` is redacted as a safe-side fallback.
## `GuzzleHttp\Psr7\Utils::redactUriForMessage`
`public static function redactUriForMessage(UriInterface $uri): string`
Formats a URI for automatic diagnostics.
The whole userinfo component is replaced by "***", and the query and
fragment are removed. The scheme, host, port, and path are preserved.
The returned string is diagnostic-escaped and the formatter never
throws.
## `GuzzleHttp\Psr7\Utils::redactUriStringForMessage`
`public static function redactUriStringForMessage(string $uri): string`
Formats a raw URI for automatic diagnostics, including malformed input.
The whole userinfo component is replaced by "***", and the query and
fragment are removed. The scheme, host, port, and path are preserved
where their boundaries can be determined safely. The returned string is
diagnostic-escaped and the formatter never throws.
## `GuzzleHttp\Psr7\Utils::uriFor`
`public static function uriFor(string|UriInterface $uri): UriInterface`

View File

@@ -102,6 +102,14 @@ IDNA is treated as a client concern. Consumers that need DNS IDNs must perform
the conversion themselves, for example via Guzzle's `idn_conversion` request
option.
Validity here is a statement about URI syntax only. It does not imply that the
host resolves or that a client will interpret it exactly as written. A PSR-7 URI
object does not know which resolver or transport will consume it, so it cannot
establish destination identity. Consumers that restrict the destination must
enforce that policy at the client or transport boundary and ensure the
connection uses an approved address; checking the spelling or a separate DNS
lookup is insufficient.
### `GuzzleHttp\Psr7\Rfc3986::isValidPort`
`public static function isValidPort(string $port): bool`
@@ -248,6 +256,10 @@ the current request URI.
Converts the relative URI into a new URI that is resolved against the base URI.
When the resolved path is a relative-path reference whose first segment contains
a colon, which would be mistaken for a scheme name (RFC 3986 Section 4.2), it is
prefixed with `./`, e.g. `./a:b`.
### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
`public static function removeDotSegments(string $path): string`
@@ -313,6 +325,13 @@ means the URIs `/?#` and `/` are treated equivalent which is not necessarily
true according to RFC 3986. But that difference is highly uncommon in reality.
So this potential normalization is implied in PSR-7 as well.
A path the URI cannot hold, such as a `//`-leading path without an authority or
a relative-path reference whose first segment contains a colon, is prefixed with
`/.` or `./` respectively instead of throwing, as `UriResolver::resolve()` does.
The percent-encoding normalizations only do so where they rewrote the path. For
example, decoding `a%41:` yields `./aA:`, since `aA:` would be an absolute URI
with the scheme `aa`.
The following normalizations are available:
- `UriNormalizer::PRESERVING_NORMALIZATIONS`
@@ -369,7 +388,11 @@ The following normalizations are available:
`file:///myfile`, and `file://localhost/myfile` are equivalent according to
RFC 3986.
Example: `file://localhost/myfile``file:///myfile`
When removing the host leaves a URI without an authority whose path begins
with `//`, the path is serialized with a `/.` prefix.
Example: `file://localhost/myfile``file:///myfile`,
`file://localhost//x``file:///.//x`
- `UriNormalizer::REMOVE_DEFAULT_PORT`

View File

@@ -78,7 +78,7 @@ trait MessageTrait
public function withHeader(string $name, $value): MessageInterface
{
$this->assertHeader($name);
$value = $this->normalizeHeaderValue($value);
$value = $this->normalizeHeaderValue($name, $value);
$normalized = Utils::asciiToLower($name);
$new = clone $this;
@@ -97,7 +97,7 @@ trait MessageTrait
public function withAddedHeader(string $name, $value): MessageInterface
{
$this->assertHeader($name);
$value = $this->normalizeHeaderValue($value);
$value = $this->normalizeHeaderValue($name, $value);
$normalized = Utils::asciiToLower($name);
$new = clone $this;
@@ -166,7 +166,7 @@ trait MessageTrait
$header = (string) $header;
$this->assertHeader($header);
$value = $this->normalizeHeaderValue($value);
$value = $this->normalizeHeaderValue($header, $value);
$normalized = Utils::asciiToLower($header);
if (isset($this->headerNames[$normalized])) {
$header = $this->headerNames[$normalized];
@@ -183,17 +183,17 @@ trait MessageTrait
*
* @return string[]
*/
private function normalizeHeaderValue($value): array
private function normalizeHeaderValue(string $header, $value): array
{
if (is_array($value) && $value === []) {
throw new \InvalidArgumentException('Header value must be a non-empty array or string.');
}
if (!is_array($value)) {
return $this->trimAndValidateHeaderValues([$value]);
return $this->trimAndValidateHeaderValues($header, [$value]);
}
return $this->trimAndValidateHeaderValues($value);
return $this->trimAndValidateHeaderValues($header, $value);
}
/**
@@ -210,9 +210,9 @@ trait MessageTrait
*
* @see https://datatracker.ietf.org/doc/html/rfc9110#section-5.5
*/
private function trimAndValidateHeaderValues(array $values): array
private function trimAndValidateHeaderValues(string $header, array $values): array
{
return array_map(function ($value): string {
return array_map(function ($value) use ($header): string {
if (!is_string($value)) {
throw new \InvalidArgumentException(sprintf(
'Header value must be a string or array of strings but %s provided.',
@@ -221,7 +221,7 @@ trait MessageTrait
}
$trimmed = trim($value, " \t");
$this->assertValue($trimmed);
$this->assertValue($header, $trimmed);
return $trimmed;
}, array_values($values));
@@ -254,7 +254,7 @@ trait MessageTrait
* obs-text = %x80-FF
* obs-fold = CRLF 1*( SP / HTAB )
*/
private function assertValue(string $value): void
private function assertValue(string $header, string $value): void
{
// The regular expression intentionally does not support the obs-fold
// production, because as per RFC 9112#5.2:
@@ -269,7 +269,11 @@ trait MessageTrait
// obscure feature of HTTP/1.1 and thus not accepting folding is not
// likely to break any legitimate use case.
if (!Rfc9110::isFieldValue($value)) {
throw new \InvalidArgumentException(sprintf('Invalid header value: %s', DiagnosticValue::escape($value)));
$reason = strpbrk($value, "\r\n") !== false
? 'must not contain CR or LF characters'
: 'contains an invalid control character';
throw new \InvalidArgumentException(sprintf('Header "%s" %s.', DiagnosticValue::escape($header), $reason));
}
}
}

View File

@@ -72,7 +72,7 @@ final class MultipartStream implements StreamInterface
$key = (string) $key;
self::validatePartHeaderName($key);
self::validatePartHeaderValue($value);
self::validatePartHeaderValue($key, $value);
$str .= "{$key}: {$value}\r\n";
}
@@ -252,7 +252,7 @@ final class MultipartStream implements StreamInterface
throw new \InvalidArgumentException('Multipart part header value must be a string.');
}
self::validatePartHeaderValue($value);
self::validatePartHeaderValue($key, $value);
$normalized[$key] = $value;
}
@@ -267,10 +267,14 @@ final class MultipartStream implements StreamInterface
}
}
private static function validatePartHeaderValue(string $value): void
private static function validatePartHeaderValue(string $name, string $value): void
{
if (!Rfc9110::isFieldValue($value)) {
throw new \InvalidArgumentException(sprintf('Invalid multipart part header value: %s', DiagnosticValue::escape($value)));
$reason = strpbrk($value, "\r\n") !== false
? 'must not contain CR or LF characters'
: 'contains an invalid control character';
throw new \InvalidArgumentException(sprintf('Multipart part header "%s" %s.', DiagnosticValue::escape($name), $reason));
}
}

View File

@@ -150,7 +150,7 @@ class Request implements RequestInterface
$host .= ':'.$port;
}
$this->assertValue($host);
$this->assertValue('Host', $host);
return $host;
}

View File

@@ -69,7 +69,7 @@ class Uri implements UriInterface, \JsonSerializable
if ($uri !== '') {
$parts = UriParser::parse($uri);
if ($parts === false) {
throw new MalformedUriException(\sprintf('Unable to parse URI: %s', DiagnosticValue::escape($uri)));
throw new MalformedUriException(\sprintf('Unable to parse URI: %s', Utils::redactUriStringForMessage($uri)));
}
try {
$this->applyParts($parts);
@@ -411,7 +411,10 @@ class Uri implements UriInterface, \JsonSerializable
* components, including the leading slash the string form adds to a
* rootless path when an authority is present; for subclasses and other
* implementations the path is split from the string form per RFC 3986
* Appendix B, without validating or decoding any other component.
* Appendix B, without validating or decoding any other component. The
* scheme is only split off when the instance reports one, as a relative
* reference can begin with a segment containing a colon that the Appendix
* B expression would otherwise read as a scheme.
*
* @throws \RuntimeException If the path cannot be split from the string form.
*
@@ -429,7 +432,10 @@ class Uri implements UriInterface, \JsonSerializable
return $uri->path;
}
$count = preg_match('%^(?:[^:/?#]+:)?(?://[^/?#]*)?([^?#]*)%', (string) $uri, $matches);
$pattern = $uri->getScheme() === ''
? '%^(?://[^/?#]*)?([^?#]*)%'
: '%^(?:[^:/?#]+:)?(?://[^/?#]*)?([^?#]*)%';
$count = preg_match($pattern, (string) $uri, $matches);
if ($count === false) {
throw new \RuntimeException('Unable to read the URI path: '.preg_last_error_msg());

View File

@@ -86,7 +86,11 @@ final class UriNormalizer
* second format in the Uri class. See
* `GuzzleHttp\Psr7\Uri::composeComponents`.
*
* When removing the host leaves a URI without an authority whose path
* begins with `//`, the path is serialized with a `/.` prefix.
*
* Example: file://localhost/myfile → file:///myfile
* Example: file://localhost//x → file:///.//x
*/
public const REMOVE_DEFAULT_HOST = 8;
@@ -166,6 +170,13 @@ final class UriNormalizer
* uncommon in reality. So this potential normalization is implied in PSR-7
* as well.
*
* A path the URI cannot hold, such as a `//`-leading path without an
* authority or a relative-path reference whose first segment contains a
* colon, is prefixed with `/.` or `./` respectively instead of throwing, as
* `UriResolver::resolve()` does. The percent-encoding normalizations only
* do so where they rewrote the path. For example, decoding `a%41:` yields
* `./aA:`, since `aA:` would be an absolute URI with the scheme `aa`.
*
* @param UriInterface $uri The URI to normalize
* @param int $flags A bitmask of normalizations to apply, see constants
*
@@ -188,6 +199,14 @@ final class UriNormalizer
}
if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
if ($uri->getUserInfo() === '' && $uri->getPort() === null) {
$path = Uri::rawPath($uri);
if (str_starts_with($path, '//')) {
// "/." keeps a "//" path unambiguous once the authority is gone
$uri = $uri->withPath('/.'.$path);
}
}
$uri = $uri->withHost('');
}
@@ -260,8 +279,7 @@ final class UriNormalizer
$uri = self::withNormalizedUserInfo($uri, $regex, $callback);
$uri = self::withNormalizedHost($uri, $regex, $callback);
return $uri
->withPath(self::normalizePercentEncodingInComponent(Uri::rawPath($uri), $regex, $callback))
return self::withGuardedPath($uri, self::normalizePercentEncodingInComponent(Uri::rawPath($uri), $regex, $callback))
->withQuery(self::normalizePercentEncodingInComponent($uri->getQuery(), $regex, $callback))
->withFragment(self::normalizePercentEncodingInComponent($uri->getFragment(), $regex, $callback));
}
@@ -284,12 +302,24 @@ final class UriNormalizer
$uri = self::withNormalizedUserInfo($uri, $regex, $callback);
$uri = self::withNormalizedHost($uri, $regex, $hostCallback);
return $uri
->withPath(self::normalizePercentEncodingInComponent(Uri::rawPath($uri), $regex, $callback))
return self::withGuardedPath($uri, self::normalizePercentEncodingInComponent(Uri::rawPath($uri), $regex, $callback))
->withQuery(self::normalizePercentEncodingInComponent($uri->getQuery(), $regex, $callback))
->withFragment(self::normalizePercentEncodingInComponent($uri->getFragment(), $regex, $callback));
}
/**
* Writes the given path only when it differs from the current one, guarded
* so the write cannot throw.
*/
private static function withGuardedPath(UriInterface $uri, string $path): UriInterface
{
if ($path === Uri::rawPath($uri)) {
return $uri;
}
return $uri->withPath(UriResolver::guardedPath($uri, $path));
}
/**
* @param callable(array): string $callback
*/

View File

@@ -65,8 +65,8 @@ final class UriResolver
}
/**
* Returns the path, prefixed with "/." when it would otherwise start the
* URI's string form with an authority-like "//".
* Returns the path, prefixed with "/." or "./" when the URI could not
* otherwise hold it.
*
* A URI without an authority cannot hold a path beginning with "//" (RFC
* 3986 Section 3.3), but removeDotSegments() can produce one. The "/."
@@ -76,28 +76,47 @@ final class UriResolver
* written, so the path cannot be mistaken for an authority and the prefix
* is not added.
*
* A relative-path reference cannot begin with a segment containing a colon
* (RFC 3986 Section 4.2), as it would be mistaken for a scheme name, but
* reference resolution and percent-encoding normalization can produce one.
* The "./" prefix the RFC prescribes resolves back to the same path.
*
* @see https://url.spec.whatwg.org/#url-serializing
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
*
* @internal
*/
public static function guardedPath(UriInterface $uri, string $path): string
{
if (!str_starts_with($path, '//') || $uri->getAuthority() !== '') {
if ($uri->getAuthority() !== '') {
return $path;
}
if ($uri instanceof Uri && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')) {
return $path;
if (str_starts_with($path, '//')) {
if ($uri instanceof Uri && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')) {
return $path;
}
return '/.'.$path;
}
return '/.'.$path;
if ($uri->getScheme() === '' && str_contains(explode('/', $path, 2)[0], ':')) {
return './'.$path;
}
return $path;
}
/**
* Converts the relative URI into a new URI that is resolved against the
* base URI.
*
* When the resolved path is a relative-path reference whose first segment
* contains a colon, which would be mistaken for a scheme name (RFC 3986
* Section 4.2), it is prefixed with `./`, e.g. `./a:b`.
*
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
*/
public static function resolve(UriInterface $base, UriInterface $rel): UriInterface
{

View File

@@ -583,6 +583,56 @@ final class Utils
return \str_replace(\substr($authority, 0, $atPosition).'@', '***@', $subject);
}
/**
* Formats a URI for automatic diagnostics.
*
* The whole userinfo component is replaced by "***", and the query and
* fragment are removed. The scheme, host, port, and path are preserved.
* The returned string is diagnostic-escaped and the formatter never
* throws.
*/
public static function redactUriForMessage(
#[\SensitiveParameter]
UriInterface $uri
): string {
try {
$raw = (string) $uri;
} catch (\Throwable $e) {
return '[unavailable URI]';
}
try {
if ($uri->getUserInfo() !== '') {
$uri = $uri->withUserInfo('***');
}
return DiagnosticValue::escape((string) $uri->withQuery('')->withFragment(''));
} catch (\Throwable $e) {
return self::redactUriStringForMessage($raw);
}
}
/**
* Formats a raw URI for automatic diagnostics, including malformed input.
*
* The whole userinfo component is replaced by "***", and the query and
* fragment are removed. The scheme, host, port, and path are preserved
* where their boundaries can be determined safely. The returned string is
* diagnostic-escaped and the formatter never throws.
*/
public static function redactUriStringForMessage(
#[\SensitiveParameter]
string $uri
): string {
try {
$uri = self::redactUserInfoInString($uri, $uri);
return DiagnosticValue::escape(\substr($uri, 0, \strcspn($uri, '?#')));
} catch (\Throwable $e) {
return '[unavailable URI]';
}
}
/**
* Create a new stream based on the input type.
*

View File

@@ -57,6 +57,8 @@ class Signed implements Serializable
* Get the serializable representation of the closure.
*
* @return array
*
* @throws \Laravel\SerializableClosure\Exceptions\MissingSecretKeyException
*/
public function __serialize()
{
@@ -76,10 +78,15 @@ class Signed implements Serializable
* @return void
*
* @throws \Laravel\SerializableClosure\Exceptions\InvalidSignatureException
* @throws \Laravel\SerializableClosure\Exceptions\MissingSecretKeyException
*/
public function __unserialize($signature)
{
if (static::$signer && ! static::$signer->verify($signature)) {
if (! static::$signer) {
throw new MissingSecretKeyException();
}
if (! static::$signer->verify($signature)) {
throw new InvalidSignatureException();
}

View File

@@ -130,11 +130,11 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
<table>
<tr>
<td><a title="Онлайн казино 777 Україна" href="https://777.ua/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино 777" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/7e572d50-1ce8-4d69-ae12-86cc80371373/ok-ua-777.png" width="96" height="96"></a></td>
<td><a title="Нова українська букмекерська контора" href="https://betking.com.ua/sports-book/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Ставки на спорт" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/c56d2fe2-f9fb-4d63-947c-77575f4b15c6/stavki.jpg" width="96" height="96"></a></td>
<td><a title="Non GamStop Bookies UK" href="https://netto.co.uk/betting-sites-not-on-gamstop/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Non GamStop Bookies UK" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/51bfaa05-02b3-4cd9-b1a4-9d0d8f34cbae/%D0%97%D0%BD%D1%96%D0%BC%D0%BE%D0%BA%20%D0%B5%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%202025-07-04%20%D0%BE%2015.21.16%20(1)%20(1)%20(1).jpg" width="126" height="96"></a></td>
<td><a title="Trusted last mile route planning and route optimization" href="https://route4me.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Route4Me Route Planner" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/237386c3-48a2-47c6-97ac-5f888cdb4cda/Route4MeIconLogo.png" width="96" height="96"></a></td>
<td><a title="Bei Releaf erhalten Sie schnell und diskret Ihr Cannabis Rezept online. Unsere Ärzte prüfen Ihre Angaben und stellen bei Eignung das Rezept aus. Anschließend können Sie legal und sicher medizinisches Cannabis über unsere Partnerapotheken kaufen." href="https://releaf.com/de?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Releaf Medizinischer Cannabis Shop" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/b686d646-5029-4b4c-8cab-9645ab2679de/9da596d1-f48a-41ec-947d-a64dd8e7529c.png" width="96" height="96"></a></td>
<td><a title="We specialize in the online gambling industry, helping players access reliable and verified information about the best online casinos and pokies in Australia. Our team tests casinos and games, collects user reviews from Trustpilot, and organizes them in o" href="https://au.trustpilot.com/review/bestpayidpokies.net?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="PayID Pokies" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/985a0ae7-54c3-4680-8816-bc8d656f7562/payidpokies.png" width="96" height="96"></a></td>
<td><a title="Нова українська букмекерська контора" href="https://betking.com.ua/sports-book/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Ставки на спорт" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/c56d2fe2-f9fb-4d63-947c-77575f4b15c6/stavki.jpg" width="96" height="96"></a></td>
<td><a title="Best non Gamstop sites in the UK" href="https://www.pieria.co.uk/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Best non Gamstop sites in the UK" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/34e340b8-e1de-4932-8a76-1b3ce2ec7ee8/logo_white%20bg%20(8).png" width="96" height="96"></a></td>
<td><a title="#1 Guide To Online Gambling In Canada" href="https://casinohex.org/canada/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="CasinoHex Canada" src="https://opencollective-production.s3.us-west-1.amazonaws.com/79fdbcc0-a997-11eb-abbc-25e48b63c6dc.jpg" width="127.5" height="96"></a></td>
</tr>
@@ -156,70 +156,70 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
<td><a title="OnlineCasinosSpelen" href="https://onlinecasinosspelen.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="OnlineCasinosSpelen" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/47e87426-6a55-4f69-9fb5-4e5032dc35a8/5d10dd22-320e-47d4-84e6-d144874f1f5f.png" width="64" height="64"></a></td>
<td><a title="We test dozens of casinos every month and select the coolest ones for Australian players." href="https://au.trustpilot.com/review/payid-casino.net?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="PayID Pokies Sites" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/ecd8a5f4-fd86-4903-a512-cbfaff35e7ef/payidpokiessites.png" width="69" height="64"></a></td>
<td><a title="We collect all reviews about the best online pokies." href="https://au.trustpilot.com/review/aussiepokies.net?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Online Pokies in Australia" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/44d84116-8762-4167-9433-a9e5da542e29/pokies.png" width="64" height="64"></a></td>
<td><a title="Plinko Game" href="https://www.trustpilot.com/review/plinkoplay.top?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Plinko Game" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/5e953df9-1876-45b6-9ffc-f2009804935b/plinkoo.png" width="64" height="64"></a></td>
<td><a title="marketing in germany" href="https://de.trustpilot.com/review/paysafecardcasinos.org?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Paysafecard Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/1b2f45ea-02bc-4a78-a84a-355f68c8a293/paysafe.png" width="64" height="64"></a></td>
<td><a title="Games" href="https://au.trustpilot.com/review/bestaustraliaonlinepokies.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Australian Online Pokies" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/7ca6ca2e-6542-4736-8952-b9adb7f4c9cf/39aac4bc53ff11f198d9ce36ed903bf5_1.jpg" width="64" height="64"></a></td>
<td><a title="Top 10 Online Casino Australia Eu Com" href="https://au.trustpilot.com/review/top-10-online-casino-australia.eu.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Top 10 Online Casino Australia Eu Com" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/c87ba708-3f1c-4062-82a8-797c713646b2/trustpilot_logo%20(4).png" width="64" height="64"></a></td>
<td><a title="Buitenlandse Online Casino" href="https://nl.trustpilot.com/review/buitenlandsecasino.vip?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Buitenlandse Online Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/9465308a-8e44-4606-af5d-5a81a3c6567b/ChatGPT%20Image%20Nov%2015%2C%202025%2C%2002_53_25%20PM.png" width="64" height="64"></a></td>
<td><a title="Plinko Game" href="https://www.trustpilot.com/review/plinkoplay.top?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Plinko Game" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/5e953df9-1876-45b6-9ffc-f2009804935b/plinkoo.png" width="64" height="64"></a></td>
<td><a title="Porównanie kasyn online w Polsce. Darmowe automaty online." href="https://onlinekasyno-polis.pl/" target="_blank"><img alt="Online Kasyno Polis" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/12fe53d4-b2e4-4601-b9ea-7b652c414a38/274px%20274px-2.png" width="64" height="64"></a></td>
<td><a title="Betwinner is an online bookmaker offering sports betting, casino games, and more." href="https://guidebook.betwinner.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Guidebook.BetWinner" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/82cab29a-7002-4924-83bf-2eecb03d07c4/0x0.png" width="64" height="64"></a></td>
<td><a title="Casinomithandyrechnung" href="https://at.trustpilot.com/review/casinomithandyrechnung.net?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casinomithandyrechnung" src="https://images.opencollective.com/casinomithandyrechnung/avatar/256.png" width="64" height="64"></a></td>
</tr>
<tr>
<td><a title="lolajack" href="https://nl.trustpilot.com/review/lolajack-casino-nl.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="lolajack" src="https://images.opencollective.com/lolajack/avatar/256.png" width="64" height="64"></a></td>
<td><a title="TopRating.casino ➢ Гід по онлайн-казино в Україні" href="https://toprating.casino/ua/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Top Rating casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/fec14fc4-85b1-4fdc-971e-e8aabfc60926/toprating%20logo.jpg" width="64" height="64"></a></td>
<td><a title="Uudet Nettikasinot" href="https://fi.parhaatuudetkasinot.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="parhaatuudetkasinot.com" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/09e109d6-8ad2-4ade-ab24-4427028c8e38/260bfa9d-6a5c-494d-9ec0-a624327429ae.png" width="64" height="64"></a></td>
</tr>
<tr>
<td><a title="New Casino Bonuses" href="https://newcasinobonuses.gb.net/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="New Casino Bonuses" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/38e356d8-3597-406a-a263-1c348723aa17/new_casino_bonuses_logo.png" width="64" height="64"></a></td>
<td><a title="Best online sports betting and casino company." href="https://global.fun88.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Fun88" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/1688ebca-4984-4b9b-a24d-8fdd1233892f/fun88-logo.png" width="64" height="64"></a></td>
<td><a title="Top 50 Online Casinos UK" href="https://eastvillafc.co.uk?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="East Villa FC" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/2560f1b5-20d6-4cf4-83cd-2457bf111fc3/trophy-fav-blue.png" width="64" height="64"></a></td>
<td><a title="บริษัทรับแทงบอลออนไลน์ที่ดีที่สุดในประเทศไทย" href="https://www.fun88thf.com/th/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Fun88" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/bbf7174c-fc01-4ce6-86b5-f621e350969d/fun88-logo.png" width="64" height="64"></a></td>
<td><a title="Nhà cái cá cược thể thao trực tuyến tốt nhất tại Việt Nam" href="https://www.fun88vnt.com/vn/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Fun88" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/ed704e1d-3894-48bb-83e3-6094b2c68a5c/fun88-logo.png" width="64" height="64"></a></td>
<td><a title="บริษัทรับแทงบอลออนไลน์ที่ดีที่สุดในประเทศไทย" href="https://www.fun88thclub.com/th/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Fun88" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/bbf7174c-fc01-4ce6-86b5-f621e350969d/fun88-logo.png" width="64" height="64"></a></td>
<td><a title="Nhà cái cá cược thể thao trực tuyến tốt nhất tại Việt Nam" href="https://www.fun88vnf.com/vn/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Fun88" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/ed704e1d-3894-48bb-83e3-6094b2c68a5c/fun88-logo.png" width="64" height="64"></a></td>
<td><a title="Casino ohne oasis" href="https://de.trustpilot.com/review/onlinecasinoohneoasis.me?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino ohne oasis" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/f59e1e3a-6378-4aa7-91f5-6934a58bc21d/oasis.png" width="64" height="64"></a></td>
<td><a title="Najlepsi Bukmacherzy" href="https://najlepsibukmacherzy.pl/ranking-legalnych-bukmacherow/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Najlepsi Bukmacherzy" src="https://opencollective-production.s3.us-west-1.amazonaws.com/81588eb0-608a-11eb-ad1c-b119533ac7bb.png" width="64" height="64"></a></td>
<td><a title="Paysafecard Casino" href="https://de.trustpilot.com/review/onlinecasinospaysafecard.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Paysafecard Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/641d34bb-f65f-4df5-a6ec-ad5ab35df1e0/paysafe.png" width="64" height="64"></a></td>
<td><a title="WNB Casino Bewertung" href="https://wnb-casino.de/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="WNB Casino Bewertung" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/1adcf96c-dba5-40c0-aa5f-cc352e98b376/WNB%20Casino%20Bewertung.png" width="64" height="64"></a></td>
</tr>
<tr>
<td><a title="Casinos ohne OASIS" href="https://de.trustpilot.com/review/ohneoasis.de?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casinos ohne OASIS" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/49904abe-0786-4b30-a9c8-6c508441f7c7/ohneoasis.de.png" width="65" height="64"></a></td>
<td><a title="Casino mit Schneller Auszahlung" href="https://de.trustpilot.com/review/casinoschnellauszahlung.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino mit Schneller Auszahlung" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/031e76f8-0041-4c7c-a942-2d8f21b5a54b/casinoschnellauszahlung.com%202.png" width="64" height="64"></a></td>
<td><a title="casino en ligne avec Girandières" href="https://www.girandieres.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="casino en ligne avec Girandières" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/62161b95-62bd-4feb-af7e-48a807508286/girandieres_logo_5.png" width="64" height="64"></a></td>
<td><a title="Online Casino Zonder Registratie" href="https://nl.trustpilot.com/review/zonderregistratiecasinos.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Online Casino Zonder Registratie" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/cf382ddc-627b-4bd9-8870-43ee28d2a628/casino-zonder-registratie.png" width="85" height="64"></a></td>
<td><a title="Real Money Pokies" href="https://onlinecasinoskiwi.co.nz/real-money-pokies/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Real Money Pokies" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/d0f7382e-32ea-4425-a8c4-3019f9ed501c/NZ_logo%20(6)%20(2).jpg" width="42" height="42"></a></td>
<td><a title="Онлайн казино casino.ua" href="https://casino.ua/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино casino.ua" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/32790ee6-245b-45bd-acf7-7a661fe2cf9f/logo.png" width="42" height="42"></a></td>
<td><a title="Get professional support for Carbon" href="https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&amp;utm_medium=referral&amp;utm_campaign=docs" target="_blank"><img alt="Tidelift" src="https://carbonphp.github.io/carbon/sponsors/tidelift-brand.png" width="84" height="42"></a></td>
<td><a title="iDealeCasinos" href="https://idealecasinos.nl/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="iDealeCasinos" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/907841d3-435e-44b4-9684-c33fd8635ece/ideale-casinos-square-white-logo-300.png" width="42" height="42"></a></td>
<td><a title="Offshore bookmakers review site." href="https://www.sportsbookreviewsonline.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Sportsbook Reviews Online" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/6d499f24-d669-4fc6-bb5f-b87184aa7963/sportsbookreviewsonline_com.png" width="42" height="42"></a></td>
<td><a title="Discover the Top 10 Online Casino Australia sites for fast payouts, big bonuses, and top real money pokies" href="https://au.trustpilot.com/review/top-10-online-casino-australia.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Top 10 Casino Australia" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/cd3f2230-ca38-41e1-9f31-f3069b910be3/top-10-online-casino-aus.jpg" width="42" height="42"></a></td>
<td><a title="Find the best Interac online casinos in Canada with secure payments, exclusive bonuses, and fast withdrawals." href="https://interac-casino.com/en-ca/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Interac-casino.com - Canada" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/865d9613-74db-45b6-9f33-3ba992682259/2025-09-10%2019.33.08.jpg" width="42" height="42"></a></td>
<td><a title="Casino-portugal.pt" href="https://casino-portugal.pt/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino-portugal.pt" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/870b4bd0-b6aa-4129-9827-e8ac02cfde56/167bdc1d-0a19-414d-a288-cfc3278b388f.png" width="42" height="42"></a></td>
<td><a title="Buitenlandse Online Casino" href="https://nl.trustpilot.com/review/buitenlandsecasino.vip?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Buitenlandse Online Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/9465308a-8e44-4606-af5d-5a81a3c6567b/ChatGPT%20Image%20Nov%2015%2C%202025%2C%2002_53_25%20PM.png" width="64" height="64"></a></td>
</tr>
<tr>
<td><a title="Wildflower" href="https://www.trustpilot.com/review/wildflower.uno?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Wildflower" src="https://images.opencollective.com/wildflower_cases/avatar/256.png" width="42" height="42"></a></td>
<td><a title="Real Money Pokies" href="https://onlinecasinoskiwi.co.nz/real-money-pokies/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Real Money Pokies" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/d0f7382e-32ea-4425-a8c4-3019f9ed501c/NZ_logo%20(6)%20(2).jpg" width="42" height="42"></a></td>
<td><a title="Get professional support for Carbon" href="https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&amp;utm_medium=referral&amp;utm_campaign=docs" target="_blank"><img alt="Tidelift" src="https://carbonphp.github.io/carbon/sponsors/tidelift-brand.png" width="84" height="42"></a></td>
<td><a title="Online Casino Zonder Registratie" href="https://nl.trustpilot.com/review/zonderregistratiecasinos.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Online Casino Zonder Registratie" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/cf382ddc-627b-4bd9-8870-43ee28d2a628/casino-zonder-registratie.png" width="56" height="42"></a></td>
<td><a title="Offshore bookmakers review site." href="https://www.sportsbookreviewsonline.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Sportsbook Reviews Online" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/6d499f24-d669-4fc6-bb5f-b87184aa7963/sportsbookreviewsonline_com.png" width="42" height="42"></a></td>
<td><a title="lolajack" href="https://nl.trustpilot.com/review/lolajack-casino-nl.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="lolajack" src="https://images.opencollective.com/lolajack/avatar/256.png" width="42" height="42"></a></td>
<td><a title="Casinomithandyrechnung" href="https://at.trustpilot.com/review/casinomithandyrechnung.net?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casinomithandyrechnung" src="https://images.opencollective.com/casinomithandyrechnung/avatar/256.png" width="42" height="42"></a></td>
<td><a title="Casino-portugal.pt" href="https://casino-portugal.pt/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino-portugal.pt" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/870b4bd0-b6aa-4129-9827-e8ac02cfde56/167bdc1d-0a19-414d-a288-cfc3278b388f.png" width="42" height="42"></a></td>
<td><a title="Casino mit Schneller Auszahlung" href="https://de.trustpilot.com/review/casinoschnellauszahlung.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino mit Schneller Auszahlung" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/031e76f8-0041-4c7c-a942-2d8f21b5a54b/casinoschnellauszahlung.com%202.png" width="42" height="42"></a></td>
<td><a title="Онлайн казино casino.ua" href="https://casino.ua/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Онлайн казино casino.ua" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/32790ee6-245b-45bd-acf7-7a661fe2cf9f/logo.png" width="42" height="42"></a></td>
<td><a title="Ставки на спорт Favbet" href="https://www.favbet.ua/uk/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Ставки на спорт Favbet" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/d86d313e-7b17-42fa-8b76-3f17fbf681a2/favbet-logo.jpg" width="42" height="42"></a></td>
<td><a title="Znajdź najlepsze zakłady bukmacherskie w Polsce w 2023 roku. Probukmacher.pl to Twoje kompendium wiedzy na temat bukmacherów!" href="https://www.probukmacher.pl?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Probukmacher" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/caf50271-4560-4ffe-a434-ea15239168db/Screenshot_1.png" width="58" height="42"></a></td>
<td><a title="Best Casinos not on Gamstop in the UK 2025" href="https://www.vso.org.uk/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="best non Gamstop casinos" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/3f48874e-f2f6-4062-a2a2-1500677ee3d9/125%D1%85125%20(1).jpg" width="42" height="42"></a></td>
<td><a title="Find the best Interac online casinos in Canada with secure payments, exclusive bonuses, and fast withdrawals." href="https://interac-casino.com/en-ca/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Interac-casino.com - Canada" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/865d9613-74db-45b6-9f33-3ba992682259/2025-09-10%2019.33.08.jpg" width="42" height="42"></a></td>
</tr>
<tr>
<td><a title="iDealeCasinos" href="https://idealecasinos.nl/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="iDealeCasinos" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/907841d3-435e-44b4-9684-c33fd8635ece/ideale-casinos-square-white-logo-300.png" width="42" height="42"></a></td>
<td><a title="inkedin" href="https://inkedin.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="inkedin" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/6cb31863-b725-49c5-820c-2c2be7d54adf/cc930e12-04de-4574-9cca-171e07d277c3.png" width="42" height="42"></a></td>
<td><a title="Instant withdrawal casino" href="https://au.trustpilot.com/review/quickcashoutcasinos.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Instant withdrawal casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/e4be26b6-a57a-4758-ad56-bd31c759465e/quickcashoutcasinos2.png" width="49" height="42"></a></td>
<td><a title="Best Casinos not on Gamstop in the UK 2025" href="https://www.vso.org.uk/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="best non Gamstop casinos" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/3f48874e-f2f6-4062-a2a2-1500677ee3d9/125%D1%85125%20(1).jpg" width="42" height="42"></a></td>
<td><a title="Discover the Top 10 Online Casino Australia sites for fast payouts, big bonuses, and top real money pokies" href="https://au.trustpilot.com/review/top-10-online-casino-australia.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Top 10 Casino Australia" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/cd3f2230-ca38-41e1-9f31-f3069b910be3/top-10-online-casino-aus.jpg" width="42" height="42"></a></td>
<td><a title="Post-production resource scheduling app" href="https://freispace.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="freispace" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/7d553b00-78ff-4442-91dd-33c220e82ac4/freispace-icon-inverted.png" width="42" height="42"></a></td>
<td><a title="bestecasinozondercruks" href="https://nl.trustpilot.com/review/bestecasinozondercruks.online?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="bestecasinozondercruks" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/4948eca9-abd5-49ea-bb70-1ea6053f8663/ChatGPT%20Image%20Dec%2027%2C%202025%2C%2010_22_47%20PM-min.png" width="42" height="42"></a></td>
<td><a title="Онлайн казино та БК (ставки на спорт) в Україні" href="https://betking.com.ua/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="betking онлайн казино" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/08587758-582c-4136-aba5-2519230960d3/betking.jpg" width="42" height="42"></a></td>
<td><a title="Run Weekly" href="https://www.runweekly.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Run Weekly" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/4d7e1fad-f160-4b64-ab41-d493023f9369/2de8e56c-e9fa-497c-8882-3736ec946664.png" width="42" height="42"></a></td>
<td><a title="Are you looking for the latest online casino sites to play in the UK? How about ones that actually deliver on their promises? Every year new online casinos pops up, but are they worth your time? Check out https://helpdirect.org.uk" href="https://helpdirect.org.uk/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="New Casino Sites UK (2025) Help Direct" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/d5e8c98f-7df3-40e8-bfe1-3c1583222eab/help-direct-logo-white-trans.png" width="42" height="42"></a></td>
<td><a title="UnAIMyText" href="https://unaimytext.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="UnAIMyText" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/fa5b36d5-69e6-44a4-bbdf-b5d264399365/icon_resized.png" width="42" height="42"></a></td>
</tr>
<tr>
<td><a title="TopKasynoOnline PL" href="https://pl.topkasynoonline.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="TopKasynoOnline PL" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/6cdee969-d731-44d6-b8ff-fc4f6d4dab9c/TKOlogo.png" width="42" height="42"></a></td>
<td><a title="seguidores en instagram gratis" href="https://buylikesservices.com/es/free-instagram-followers/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="seguidores en instagram gratis" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/f743a9fa-9d13-4e08-be43-8735fa2b3b6e/buylikesservices-favicon.png" width="42" height="42"></a></td>
<td><a title="Wettanbieter ohne OASIS" href="https://de.trustpilot.com/review/wettanbieterohneoasis.io?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Wettanbieter ohne OASIS" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/44468112-6a16-4cc7-aa0a-d30020fa78a5/wettenoasis2.png" width="40" height="42"></a></td>
<td><a title="Casino ohne Lizenz" href="https://de.trustpilot.com/review/casinoohnelizenz.org?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino ohne Lizenz" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/f89d7bf4-c7d9-4ad1-a9e5-a904ba63126f/casinoohnelizenz.org.png" width="42" height="42"></a></td>
<td><a title="Casino ohne Limit" href="https://de.trustpilot.com/review/limitfreicasino.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino ohne Limit" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/4525f686-fc19-47c6-9087-d5f41ecd6cfd/limitfreicasino.com.png" width="42" height="42"></a></td>
<td><a title="Casino ohne Verifizierung" href="https://de.trustpilot.com/review/ohneverifizierungcasino.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino ohne Verifizierung" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/2255004a-1c42-4399-83bc-32c21a65b66a/ohneverifizierungcasino.com.png" width="42" height="42"></a></td>
<td><a title="Casino ohne Anmeldung" href="https://de.trustpilot.com/review/ohneanmeldungcasino.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Casino ohne Anmeldung" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/0be23739-2dd0-4f3f-85a3-8d1d67164681/ohneanmeldungcasino.com.png" width="42" height="42"></a></td>
<td><a title="Crypto Casino" href="https://de.trustpilot.com/review/cryptocasinozone.net?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Crypto Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/a5a37da5-46ad-4940-8d58-7cf4326bd53d/cryptocasinozone.net.png" width="42" height="42"></a></td>
<td><a title="Paypal Casino" href="https://de.trustpilot.com/review/paypalcasinosite.com?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Paypal Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/2c3c6c04-e009-4f93-a080-4f5f83b875b9/paypalcasinosite.com.png" width="42" height="42"></a></td>
<td><a title="FairGo Casino Australia" href="https://fairgocasinoaus.com/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img alt="Fair Go Casino" src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/df75ec48-972e-4d57-ad30-c73be2460a83/fair-go-casino.png" width="42" height="42"></a></td>
</tr>
<tr>
<td><a title="ssddanbrown" href="https://github.com/ssddanbrown" target="_blank"><img alt="ssddanbrown" src="https://avatars.githubusercontent.com/u/8343178?s=128&v=4" width="42" height="42"></a></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</details><!-- </open-collective-sponsors> -->

View File

@@ -103,8 +103,7 @@ return [
'sameElse' => 'L',
],
'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
'mmm_suffix' => '.',
'months_short' => ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.', 'sep.', 'oct.', 'nov.', 'dic.'],
'ordinal' => ':numberº',
'weekdays' => ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
'weekdays_short' => ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],

View File

@@ -65,8 +65,7 @@ return [
return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de');
},
'months' => ['jannewaris', 'febrewaris', 'maart', 'april', 'maaie', 'juny', 'july', 'augustus', 'septimber', 'oktober', 'novimber', 'desimber'],
'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
'mmm_suffix' => '.',
'months_short' => ['jan.', 'feb.', 'mrt.', 'apr.', 'mai.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'des.'],
'weekdays' => ['snein', 'moandei', 'tiisdei', 'woansdei', 'tongersdei', 'freed', 'sneon'],
'weekdays_short' => ['si.', 'mo.', 'ti.', 'wo.', 'to.', 'fr.', 'so.'],
'weekdays_min' => ['Si', 'Mo', 'Ti', 'Wo', 'To', 'Fr', 'So'],

View File

@@ -101,8 +101,7 @@ return [
return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de');
},
'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
'mmm_suffix' => '.',
'months_short' => ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'],
'weekdays' => ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
'weekdays_short' => ['zo.', 'ma.', 'di.', 'wo.', 'do.', 'vr.', 'za.'],
'weekdays_min' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],

View File

@@ -236,7 +236,9 @@ trait Creator
?string $locale = null,
DateTimeZone|string|int|null $timezone = null,
): static {
return static::rawParse(static::translateTimeString($time, $locale, static::DEFAULT_LOCALE), $timezone);
$text = static::translateTimeString($time, $locale, static::DEFAULT_LOCALE);
return static::rawParse(str_replace("'", '', $text), $timezone);
}
/**

View File

@@ -2127,15 +2127,9 @@ trait Date
'SSSSSSSSS' => static fn (CarbonInterface $date) => self::floorZeroPad($date->micro * 1000, 9),
'M' => 'month',
'MM' => ['rawFormat', ['m']],
'MMM' => static function (CarbonInterface $date, $originalFormat = null) {
$month = $date->getTranslatedShortMonthName($originalFormat);
$suffix = $date->getTranslationMessage('mmm_suffix');
if ($suffix && $month !== $date->monthName) {
$month .= $suffix;
}
return $month;
},
'MMM' => static fn (CarbonInterface $date, $originalFormat = null) => $date->getTranslatedShortMonthName(
$originalFormat,
),
'MMMM' => static fn (CarbonInterface $date, $originalFormat = null) => $date->getTranslatedMonthName(
$originalFormat,
),

View File

@@ -291,13 +291,17 @@ trait Localization
}
}
$$translationKey = array_merge(
$monthTranslations = array_merge(
$mode & CarbonInterface::TRANSLATE_MONTHS
? $processList(self::getTranslationArray($months, static::MONTHS_PER_YEAR, $timeString))
: [],
$mode & CarbonInterface::TRANSLATE_MONTHS
? $processList(self::getTranslationArray($messages['months_short'] ?? [], static::MONTHS_PER_YEAR, $timeString))
: [],
);
$$translationKey = array_merge(
$monthTranslations,
$mode & CarbonInterface::TRANSLATE_DAYS
? $processList(self::getTranslationArray($weekdays, static::DAYS_PER_WEEK, $timeString))
: [],
@@ -338,24 +342,50 @@ trait Localization
]),
$fromTranslations,
);
$monthNameCount = \count($monthTranslations);
$firstNumberOffset = preg_match('/^\D+\d/', $timeString, $match)
? \strlen($match[0]) // -1 to remove the first digit, +1 to add the extra wrapping spaced added just below
: INF;
return substr(preg_replace_callback('/(?<=[\d\s+.\/,_-])('.implode('|', $fromTranslations).')(?=[\d\s+.\/,_-])/iu', function ($match) use ($fromTranslations, $toTranslations) {
[$chunk] = $match;
return substr(preg_replace_callback(
'/(?<=[\d\s+.\/,_-])('.implode('|', $fromTranslations).')(?=[\d\s+.\/,_-])/iu',
function ($match) use ($fromTranslations, $toTranslations, $monthNameCount, $firstNumberOffset) {
[[$chunk, $offset]] = $match;
$index = array_search($chunk, $fromTranslations);
$indexes = self::getMatchingWordIndexes($fromTranslations, $chunk);
if ($index !== false) {
return $toTranslations[$index] ?? '';
}
if ($indexes !== []) {
// Before the first number in the string, prefer day names over month names
$bestIndexes = \count($indexes) > 1 && $offset < $firstNumberOffset
? array_values(array_filter($indexes, static fn ($index) => $index > $monthNameCount))
: [];
$index = $bestIndexes[0] ?? $indexes[0];
foreach ($fromTranslations as $index => $word) {
if (preg_match("/^$word\$/iu", $chunk)) {
return $toTranslations[$index] ?? '';
}
}
return $chunk; // @codeCoverageIgnore
}, " $timeString "), 1, -1);
return $chunk; // @codeCoverageIgnore
},
" $timeString ",
flags: PREG_OFFSET_CAPTURE,
), 1, -1);
}
private static function getMatchingWordIndexes(array $fromTranslations, string $search): array
{
$indexes = array_keys($fromTranslations, $search, true);
if ($indexes !== []) {
return $indexes;
}
foreach ($fromTranslations as $index => $word) {
if (preg_match("/^$word\$/iu", $search)) {
$indexes[] = $index;
}
}
return $indexes;
}
/**

View File

@@ -54,7 +54,7 @@ final class Address
throw new InvalidArgumentException('Email address contains control characters.');
}
if (!self::$validator->isValid($this->address, class_exists(MessageIDValidation::class) ? new MessageIDValidation() : new RFCValidation())) {
if (!self::isValidAddrSpec($this->address)) {
throw new RfcComplianceException(\sprintf('Email "%s" does not comply with addr-spec of RFC 2822.', $address));
}
}
@@ -87,7 +87,7 @@ final class Address
return '';
}
return \sprintf('"%s"', preg_replace('/"/u', '\"', $this->getName()));
return \sprintf('"%s"', preg_replace('/["\\\\]/', '\\\\$0', $this->getName()));
}
public static function create(self|string $address): self
@@ -141,4 +141,19 @@ final class Address
{
return (bool) preg_match('/[\x80-\xFF].*@/', $this->address);
}
private static function isValidAddrSpec(string $address): bool
{
// the message id validation is needed as this class also holds the ids of the Message-ID,
// In-Reply-To and References headers, but it accepts an unquoted "@" in the local part
if (!self::$validator->isValid($address, class_exists(MessageIDValidation::class) ? new MessageIDValidation() : new RFCValidation())) {
return false;
}
if (substr_count($address, '@') < 2) {
return true;
}
return self::$validator->isValid(substr($address, 0, strrpos($address, '@')).'@example.com', new RFCValidation());
}
}

View File

@@ -482,6 +482,7 @@ class Email extends Message
}
$otherParts = $relatedParts = [];
$cidReplacements = [];
foreach ($this->attachments as $part) {
foreach ($names as $name) {
if ($name !== $part->getName() && (!$part->hasContentId() || $name !== $part->getContentId())) {
@@ -491,9 +492,7 @@ class Email extends Message
continue 2;
}
if ($name !== $part->getContentId()) {
$html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html);
}
$cidReplacements['cid:'.$name] = 'cid:'.$part->getContentId();
$relatedParts[$name] = $part;
$part->setName($part->getName() ?? $part->getContentId())->asInline();
@@ -502,6 +501,11 @@ class Email extends Message
$otherParts[] = $part;
}
if ($cidReplacements) {
// all references are replaced at once as strtr() matches the longest name first and
// never replaces inside already substituted text, unlike successive str_replace() calls
$html = strtr($html, $cidReplacements);
}
if (null !== $htmlPart) {
$htmlPart = new TextPart($html, $this->htmlCharset, 'html');
}

View File

@@ -169,7 +169,7 @@ final class Headers
$header->setMaxLineLength($this->lineLength);
$name = strtolower($header->getName());
if (\in_array($name, self::UNIQUE_HEADERS, true) && isset($this->headers[$name]) && \count($this->headers[$name]) > 0) {
if (\in_array($name, self::UNIQUE_HEADERS, true) && isset($this->headers[$name]) && $this->headers[$name]) {
throw new LogicException(\sprintf('Impossible to set header "%s" as it\'s already defined and must be unique.', $header->getName()));
}

View File

@@ -67,7 +67,7 @@ class RawMessage
if (\is_resource($this->message)) {
rewind($this->message);
while ($line = fgets($this->message)) {
while (false !== $line = fgets($this->message)) {
yield $line;
}

View File

@@ -29,7 +29,7 @@
"symfony/process": "^6.4|^7.0|^8.0",
"symfony/property-access": "^6.4|^7.0|^8.0",
"symfony/property-info": "^6.4|^7.0|^8.0",
"symfony/serializer": "^6.4.3|^7.0.3|^8.0"
"symfony/serializer": "^6.4.44|^7.4.17|^8.1.5"
},
"conflict": {
"egulias/email-validator": "~3.0.0",

View File

@@ -61,6 +61,21 @@ final class Idn
public const DELIMITER = '-';
public const MAX_INT = 2147483647;
/**
* Punycode decoding does work quadratic in the payload length. Valid ACE
* labels are limited to 63 bytes, so payloads beyond this (generous) bound
* are rejected without being decoded to keep the work bounded.
*/
private const MAX_DECODE_PAYLOAD_SIZE = 1024;
/**
* Encoding is quadratic the same way: it is O(n*r) in the length of a label
* and in its number of distinct code points. ext-intl never runs it on a
* domain this large, because it fails before converting when the result
* would not fit its own output buffer.
*/
private const MAX_CODE_POINTS = 255;
/**
* Contains the numeric value of a basic code point (for use in representing integers) in the
* range 0 to BASE-1, or -1 if b is does not represent a value.
@@ -153,6 +168,14 @@ final class Idn
@trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', \E_USER_DEPRECATED);
}
// The ASCII form is at least as long as the number of code points in the
// domain, so beyond this many code points no result can fit in the output
// buffer ext-intl uses: it returns false there without reporting details,
// and so do we, rather than Punycode-encoding a label that cannot be used.
if (self::MAX_CODE_POINTS < \strlen((string) $domainName) && self::MAX_CODE_POINTS < self::countCodePoints((string) $domainName)) {
return false;
}
$options = [
'CheckHyphens' => true,
'CheckBidi' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 !== ($options & self::IDNA_CHECK_BIDI),
@@ -358,6 +381,16 @@ final class Idn
continue;
}
// The decoder does work quadratic in the payload length. Valid
// labels are at most 63 bytes long, so a payload beyond this
// bound is always invalid input: reject it without decoding,
// like ext-intl does, to avoid spending unbounded time on it.
if (\strlen($label) - 4 > self::MAX_DECODE_PAYLOAD_SIZE) {
$info->errors |= self::ERROR_PUNYCODE;
continue;
}
// Step 4.2. Attempt to convert the rest of the label to Unicode according to Punycode [RFC3492]. If
// that conversion fails, record that there was an error, and continue
// with the next label. Otherwise replace the original label in the string by the results of the
@@ -766,6 +799,24 @@ final class Idn
return $output;
}
/**
* Counts the code points of a UTF-8 string, malformed bytes included.
*
* This is the expression Mbstring::mb_strlen() falls back to in
* symfony/polyfill-mbstring, inlined because this package does not depend on it.
* Counting only the bytes that can start a sequence would be shorter, but then a
* run of continuation bytes would pad a label without being counted, which is the
* one thing this bound has to prevent.
*
* @param string $input
*
* @return int
*/
private static function countCodePoints($input)
{
return preg_match_all('/[\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]?|[\xE0-\xEF][\x80-\xBF]{0,2}|[\xF0-\xF7][\x80-\xBF]{0,3}|[\xF8-\xFB][\x80-\xBF]{0,4}|[\xFC-\xFD][\x80-\xBF]{0,5}|[\x80-\xBF\xFE\xFF]/s', $input);
}
/**
* @see https://tools.ietf.org/html/rfc3492#section-6.1
*

View File

@@ -129,7 +129,8 @@ class Normalizer
return false;
}
throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form');
// the doubled article was fixed in PHP 8.6
throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a '.(80600 > \PHP_VERSION_ID ? 'a ' : '').'valid normalization form');
}
if ('' === $s) {

View File

@@ -153,7 +153,7 @@ class XliffLintCommand extends Command
libxml_clear_errors();
libxml_use_internal_errors($internal);
return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors];
return ['file' => $file, 'valid' => !$errors, 'messages' => $errors];
}
private function display(SymfonyStyle $io, array $files): int

View File

@@ -30,6 +30,8 @@ class PoFileDumper extends FileDumper
$output .= "\n";
$newLine = false;
$isIntlDomain = str_ends_with($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX);
foreach ($messages->all($domain) as $source => $target) {
if ($newLine) {
$output .= "\n";
@@ -48,9 +50,10 @@ class PoFileDumper extends FileDumper
$output .= $this->formatComments(implode(' ', (array) $metadata['sources']), ':');
}
$sourceRules = $this->getStandardRules($source);
$targetRules = $this->getStandardRules($target);
if (2 == \count($sourceRules) && [] !== $targetRules) {
// in an ICU domain the pipe is an ordinary character, pluralization is expressed by the message itself
$sourceRules = $isIntlDomain ? [] : $this->getStandardRules($source);
$targetRules = $isIntlDomain ? [] : $this->getStandardRules($target);
if (2 == \count($sourceRules) && $targetRules) {
$output .= \sprintf('msgid "%s"'."\n", $this->escape($sourceRules[0]));
$output .= \sprintf('msgid_plural "%s"'."\n", $this->escape($sourceRules[1]));
foreach ($targetRules as $i => $targetRule) {

View File

@@ -13,6 +13,7 @@ namespace Symfony\Component\Translation\Extractor\Visitor;
use PhpParser\Node;
use PhpParser\NodeVisitor;
use Symfony\Component\Translation\TranslatableMessage;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
@@ -39,7 +40,9 @@ final class TranslatableMessageVisitor extends AbstractVisitor implements NodeVi
return null;
}
if (!\in_array('TranslatableMessage', $className->getParts(), true)) {
// the name resolver gives a fully qualified name; templates without a "use"
// statement resolve to the global namespace, which is accepted as well
if (!\in_array($className->toString(), [TranslatableMessage::class, 'TranslatableMessage'], true)) {
return null;
}

View File

@@ -31,23 +31,19 @@ class CsvFileLoader extends FileLoader
{
$messages = [];
try {
$file = new \SplFileObject($resource, 'rb');
} catch (\RuntimeException $e) {
throw new NotFoundResourceException(\sprintf('Error opening file "%s".', $resource), 0, $e);
if (!$file = @fopen($resource, 'r')) {
throw new NotFoundResourceException(\sprintf('Error opening file "%s".', $resource));
}
$file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY | \SplFileObject::DROP_NEW_LINE);
$file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
foreach ($file as $data) {
if (false === $data) {
continue;
}
if (!str_starts_with($data[0], '#') && isset($data[1]) && 2 === \count($data)) {
$messages[$data[0]] = $data[1];
try {
while (false !== $data = fgetcsv($file, null, $this->delimiter, $this->enclosure, $this->escape)) {
// empty lines are read as [null]
if (isset($data[1]) && 2 === \count($data) && !str_starts_with($data[0], '#')) {
$messages[$data[0]] = $data[1];
}
}
} finally {
fclose($file);
}
return $messages;

View File

@@ -55,6 +55,7 @@ class PoFileLoader extends FileLoader
* - No support for comments spanning multiple lines.
* - Translator and extracted comments are treated as being the same type.
* - Message IDs are allowed to have other encodings as just US-ASCII.
* - Contexts (msgctxt) are parsed but discarded.
*
* Items with an empty id are ignored.
*/
@@ -65,6 +66,7 @@ class PoFileLoader extends FileLoader
$defaults = [
'ids' => [],
'translated' => null,
'context' => null,
];
$messages = [];
@@ -76,23 +78,28 @@ class PoFileLoader extends FileLoader
if ('' === $line) {
// Whitespace indicated current item is done
if (!\in_array('fuzzy', $flags, true)) {
$this->addMessage($messages, $item);
}
$item = $defaults;
$flags = [];
$this->saveItem($messages, $item, $flags, $defaults);
} elseif (str_starts_with($line, '#,')) {
// flags belong to the next entry, so the previous one ends here
if (null !== $item['translated']) {
$this->saveItem($messages, $item, $flags, $defaults);
}
$flags = array_map('trim', explode(',', substr($line, 2)));
} elseif (str_starts_with($line, 'msgctxt "')) {
if (null !== $item['translated']) {
$this->saveItem($messages, $item, $flags, $defaults);
}
$item['context'] = substr($line, 9, -1);
} elseif (str_starts_with($line, 'msgid "')) {
// We start a new msg so save previous
// TODO: this fails when comments or contexts are added
$this->addMessage($messages, $item);
$item = $defaults;
if ($item['ids']) {
$this->saveItem($messages, $item, $flags, $defaults);
}
$item['ids']['singular'] = substr($line, 7, -1);
} elseif (str_starts_with($line, 'msgstr "')) {
$item['translated'] = substr($line, 8, -1);
} elseif ('"' === $line[0]) {
$continues = isset($item['translated']) ? 'translated' : 'ids';
$continues = isset($item['translated']) ? 'translated' : ($item['ids'] ? 'ids' : 'context');
if (\is_array($item[$continues])) {
end($item[$continues]);
@@ -108,14 +115,21 @@ class PoFileLoader extends FileLoader
}
}
// save last item
if (!\in_array('fuzzy', $flags, true)) {
$this->addMessage($messages, $item);
}
$this->saveItem($messages, $item, $flags, $defaults);
fclose($stream);
return $messages;
}
private function saveItem(array &$messages, array &$item, array &$flags, array $defaults): void
{
if (!\in_array('fuzzy', $flags, true)) {
$this->addMessage($messages, $item);
}
$item = $defaults;
$flags = [];
}
/**
* Save a translation item to the messages.
*

View File

@@ -218,7 +218,7 @@ function printTable($translations, $verboseOutput, bool $includeCompletedLanguag
if ($translation['translated'] > $translation['total']) {
textColorRed();
} elseif (count($translation['mismatches']) > 0) {
} elseif ($translation['mismatches']) {
textColorRed();
} elseif ($translation['is_completed']) {
textColorGreen();
@@ -235,7 +235,7 @@ function printTable($translations, $verboseOutput, bool $includeCompletedLanguag
textColorNormal();
$shouldBeClosed = false;
if (true === $verboseOutput && count($translation['missingKeys']) > 0) {
if ($verboseOutput && $translation['missingKeys']) {
echo '| Missing Translations:'.\PHP_EOL;
foreach ($translation['missingKeys'] as $id => $content) {
@@ -243,7 +243,7 @@ function printTable($translations, $verboseOutput, bool $includeCompletedLanguag
}
$shouldBeClosed = true;
}
if (true === $verboseOutput && count($translation['mismatches']) > 0) {
if ($verboseOutput && $translation['mismatches']) {
echo '| Mismatches between trans-unit id and source:'.\PHP_EOL;
foreach ($translation['mismatches'] as $id => $content) {

View File

@@ -158,11 +158,18 @@ class XliffUtils
*/
private static function fixXmlLocation(string $schemaSource, string $xmlUri): string
{
$path = __DIR__.'/../Resources/schemas/xml.xsd';
static $newPath;
if (0 === stripos($path, 'phar://')) {
if ($tmpfile = tempnam(sys_get_temp_dir(), 'symfony')) {
if (null === $newPath) {
$path = __DIR__.'/../Resources/schemas/xml.xsd';
if (0 !== stripos($path, 'phar://')) {
$newPath = self::getFileUrl($path);
} elseif ($tmpfile = tempnam(sys_get_temp_dir(), 'symfony')) {
copy($path, $tmpfile);
register_shutdown_function(static function () use ($tmpfile) {
@unlink($tmpfile);
});
$newPath = self::getFileUrl($tmpfile);
} else {
$parts = explode('/', '\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path);
@@ -170,8 +177,6 @@ class XliffUtils
$drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : '';
$newPath = 'phar:///'.$drive.implode('/', array_map('rawurlencode', $parts));
}
} else {
$newPath = self::getFileUrl($path);
}
return str_replace($xmlUri, $newPath, $schemaSource);