From 1ee24079a6e45705ffbbab22e6078d57ccfb3585 Mon Sep 17 00:00:00 2001
From: Gokujo <19776684+Gokujo@users.noreply.github.com>
Date: Thu, 1 Oct 2026 10:33:16 +0000
Subject: [PATCH] chore(deps): composer update
---
upload/api/composer.lock | 94 +--
upload/api/vendor/composer/installed.json | 168 ++--
upload/api/vendor/composer/installed.php | 76 +-
.../src/Serializers/Native.php | 36 +-
.../src/Support/SelfReference.php | 4 +-
.../api/vendor/monolog/monolog/CHANGELOG.md | 7 +
upload/api/vendor/monolog/monolog/README.md | 2 +-
.../Monolog/Formatter/RedactingFormatter.php | 11 +-
.../Monolog/Handler/RotatingFileHandler.php | 2 +-
.../src/Monolog/Handler/SocketHandler.php | 1 +
.../monolog/monolog/src/Monolog/Logger.php | 14 +-
.../src/Parser/TokenIterator.php | 15 +-
.../src/TypeResolver/StringTypeResolver.php | 24 +-
.../console/Formatter/OutputFormatter.php | 21 +-
upload/api/vendor/symfony/finder/Glob.php | 5 +-
.../Iterator/VcsIgnoredFilterIterator.php | 2 +-
.../polyfill-intl-grapheme/Grapheme.php | 156 +++-
.../polyfill-intl-grapheme/bootstrap.php | 2 +-
.../polyfill-intl-grapheme/bootstrap80.php | 2 +-
.../polyfill-intl-normalizer/Normalizer.php | 48 +-
.../symfony/polyfill-mbstring/Mbstring.php | 756 +++++++++++++++---
upload/api/vendor/symfony/type-info/Type.php | 2 +-
.../symfony/type-info/Type/ArrayShapeType.php | 2 +-
.../type-info/Type/IntersectionType.php | 11 +
.../symfony/type-info/Type/UnionType.php | 11 +
.../type-info/TypeContext/TypeContext.php | 2 +-
.../TypeContext/TypeContextFactory.php | 19 +-
.../symfony/type-info/TypeFactoryTrait.php | 5 +
.../PhpDocAwareReflectionTypeResolver.php | 2 +-
.../TypeResolver/StringTypeResolver.php | 58 +-
upload/api/vendor/symfony/yaml/Inline.php | 33 +-
upload/api/vendor/symfony/yaml/Parser.php | 233 ++++--
.../zircote/swagger-php/CONTRIBUTING.md | 37 +
.../api/vendor/zircote/swagger-php/README.md | 2 +-
.../api/vendor/zircote/swagger-php/ROADMAP.md | 88 +-
.../vendor/zircote/swagger-php/bin/openapi | 2 +-
.../vendor/zircote/swagger-php/composer.json | 2 +-
.../zircote/swagger-php/package-lock.json | 8 +-
.../vendor/zircote/swagger-php/package.json | 2 +-
.../Analysers/DocBlockAnnotationFactory.php | 24 +
.../src/Analysers/DocBlockParser.php | 17 +-
.../src/Analysers/ReflectionAnalyser.php | 60 +-
.../src/Analysers/TokenScanner.php | 2 +-
.../zircote/swagger-php/src/Analysis.php | 110 ++-
.../src/Annotations/AbstractAnnotation.php | 4 +-
.../src/Annotations/Components.php | 8 +-
.../src/Annotations/JsonSchemaTrait.php | 5 +-
.../src/Annotations/ServerVariable.php | 5 +-
.../Assembler/AbstractAttributeTranslator.php | 10 +-
.../OptionalPropertyAttributeTranslator.php | 10 +-
.../src/Attributes/AdditionalProperties.php | 25 +-
.../swagger-php/src/Attributes/Items.php | 25 +-
.../src/Attributes/JsonContent.php | 25 +-
.../swagger-php/src/Attributes/Property.php | 25 +-
.../swagger-php/src/Attributes/Schema.php | 25 +-
.../src/Attributes/ServerVariable.php | 9 +-
.../swagger-php/src/Attributes/XmlContent.php | 27 +-
.../swagger-php/src/Augmenter/Enums.php | 66 +-
.../src/Augmenter/Inheritance/Operations.php | 23 +-
.../swagger-php/src/Augmenter/MediaTypes.php | 9 +-
.../swagger-php/src/Augmenter/PathItems.php | 251 ++----
.../swagger-php/src/Augmenter/Refs.php | 9 +-
.../swagger-php/src/Augmenter/Shortcuts.php | 59 +-
.../swagger-php/src/Augmenter/Types.php | 6 +-
.../zircote/swagger-php/src/Builder.php | 30 +-
.../src/Compiler/OpenApi31Compiler.php | 67 +-
.../src/Console/GenerateCommand.php | 64 +-
.../swagger-php/src/Console/GenerateInput.php | 77 +-
.../zircote/swagger-php/src/Context.php | 40 +-
.../AttributeTranslatorInterface.php | 10 +-
.../zircote/swagger-php/src/Generator.php | 80 +-
.../zircote/swagger-php/src/HybridBridge.php | 46 +-
.../src/Loggers/CollectingLogger.php | 3 +
.../swagger-php/src/Loggers/DefaultLogger.php | 5 +-
.../zircote/swagger-php/src/Pipeline.php | 4 +-
.../src/Processors/AugmentMediaType.php | 4 +-
.../src/Processors/AugmentTags.php | 9 +
.../src/Processors/CleanUnmerged.php | 8 +-
.../src/Processors/CleanUnusedComponents.php | 9 +-
.../src/Processors/Concerns/DocblockTrait.php | 3 +
.../Concerns/MergePropertiesTrait.php | 12 +
.../src/Processors/DocBlockDescriptions.php | 9 +-
.../src/Processors/ExpandEnums.php | 24 +-
.../src/Processors/MergeIntoOpenApi.php | 5 +-
.../src/Processors/MergeJsonContent.php | 4 +-
.../src/Processors/MergeXmlContent.php | 4 +-
.../swagger-php/src/Processors/PathFilter.php | 16 +
.../zircote/swagger-php/src/Resolver.php | 8 +
.../zircote/swagger-php/src/Serializer.php | 26 +-
.../zircote/swagger-php/src/SourceFinder.php | 2 +-
.../src/Spec/AbstractAttribute.php | 12 +-
.../zircote/swagger-php/src/Spec/Schema.php | 116 +--
.../swagger-php/src/Spec/ServerVariable.php | 13 +-
.../zircote/swagger-php/src/Specification.php | 6 +
.../src/Specification/PathItemHierarchy.php | 128 +++
.../swagger-php/src/Specification/Walker.php | 6 +-
.../src/Type/AbstractTypeResolver.php | 10 +-
.../src/Type/LegacyTypeResolver.php | 7 +-
.../swagger-php/src/Type/SchemaType.php | 3 +
.../src/Type/TypeInfoTypeResolver.php | 33 +-
.../swagger-php/src/Type/TypeMapper.php | 4 +-
.../swagger-php/src/Type/TypeResolver.php | 16 +-
.../swagger-php/src/TypeResolverInterface.php | 5 +-
.../src/Utils/AttributeFactory.php | 22 +-
.../swagger-php/src/Utils/ClassReflector.php | 40 +
.../zircote/swagger-php/src/Utils/Config.php | 2 +
.../swagger-php/src/Utils/DocBlockParser.php | 6 +-
.../swagger-php/src/Utils/Pipeline.php | 23 +-
.../src/Utils/ServerVariableEnum.php | 60 ++
.../swagger-php/src/Utils/SourceFinder.php | 7 +
.../swagger-php/src/Utils/SourceScanner.php | 5 +-
.../swagger-php/src/Utils/TokenScanner.php | 56 +-
.../swagger-php/src/Utils/TypeMapper.php | 2 +-
.../swagger-php/src/Utils/TypedList.php | 11 +-
114 files changed, 2885 insertions(+), 1081 deletions(-)
create mode 100644 upload/api/vendor/zircote/swagger-php/src/Specification/PathItemHierarchy.php
create mode 100644 upload/api/vendor/zircote/swagger-php/src/Utils/ClassReflector.php
create mode 100644 upload/api/vendor/zircote/swagger-php/src/Utils/ServerVariableEnum.php
diff --git a/upload/api/composer.lock b/upload/api/composer.lock
index 9bdd8941..a7fc5ed0 100644
--- a/upload/api/composer.lock
+++ b/upload/api/composer.lock
@@ -131,16 +131,16 @@
},
{
"name": "laravel/serializable-closure",
- "version": "v2.0.16",
+ "version": "v2.1.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
- "reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed"
+ "reference": "2d5869a838bbcf37e0d8b0568fc41914e81374b5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
- "reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/2d5869a838bbcf37e0d8b0568fc41914e81374b5",
+ "reference": "2d5869a838bbcf37e0d8b0568fc41914e81374b5",
"shasum": ""
},
"require": {
@@ -188,7 +188,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
- "time": "2026-08-18T20:28:54+00:00"
+ "time": "2026-09-22T14:32:34+00:00"
},
{
"name": "lcobucci/jwt",
@@ -603,16 +603,16 @@
},
{
"name": "monolog/monolog",
- "version": "3.12.0",
+ "version": "3.12.1",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "72c534fc0ab181ef52d92a68382318631e301608"
+ "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/72c534fc0ab181ef52d92a68382318631e301608",
- "reference": "72c534fc0ab181ef52d92a68382318631e301608",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7059e4c691d05ea1222b5124bed0f961be46e15",
+ "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15",
"shasum": ""
},
"require": {
@@ -692,7 +692,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
- "source": "https://github.com/Seldaek/monolog/tree/3.12.0"
+ "source": "https://github.com/Seldaek/monolog/tree/3.12.1"
},
"funding": [
{
@@ -704,7 +704,7 @@
"type": "tidelift"
}
],
- "time": "2026-09-09T08:34:20+00:00"
+ "time": "2026-09-29T19:21:12+00:00"
},
{
"name": "nikic/fast-route",
@@ -2083,16 +2083,16 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.41.0",
+ "version": "v1.43.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d"
+ "reference": "5cac8f4db97208823aa353f59a01faad2da52c75"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
- "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5cac8f4db97208823aa353f59a01faad2da52c75",
+ "reference": "5cac8f4db97208823aa353f59a01faad2da52c75",
"shasum": ""
},
"require": {
@@ -2141,7 +2141,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.43.0"
},
"funding": [
{
@@ -2161,20 +2161,20 @@
"type": "tidelift"
}
],
- "time": "2026-07-28T08:25:59+00:00"
+ "time": "2026-09-25T15:46:47+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.42.0",
+ "version": "v1.43.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502"
+ "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502",
- "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/ebd57ccc7edb93d632e184efbe7693d2517bab3b",
+ "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b",
"shasum": ""
},
"require": {
@@ -2226,7 +2226,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.42.0"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.43.0"
},
"funding": [
{
@@ -2246,20 +2246,20 @@
"type": "tidelift"
}
],
- "time": "2026-08-07T06:33:24+00:00"
+ "time": "2026-09-23T22:02:09+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.38.2",
+ "version": "v1.43.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
+ "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
- "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/4d5d331bf731e0aedbc6b2f5e61135b7b7996a72",
+ "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72",
"shasum": ""
},
"require": {
@@ -2311,7 +2311,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.43.0"
},
"funding": [
{
@@ -2331,7 +2331,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-27T06:59:30+00:00"
+ "time": "2026-09-24T15:19:04+00:00"
},
{
"name": "symfony/service-contracts",
@@ -3614,16 +3614,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "12.5.35",
+ "version": "12.5.37",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "345ac22e42cb14cdc7aa8428655d8bb8c82a4aab"
+ "reference": "684340c1590e5f3f3b859f5e80619725d88b63ba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/345ac22e42cb14cdc7aa8428655d8bb8c82a4aab",
- "reference": "345ac22e42cb14cdc7aa8428655d8bb8c82a4aab",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/684340c1590e5f3f3b859f5e80619725d88b63ba",
+ "reference": "684340c1590e5f3f3b859f5e80619725d88b63ba",
"shasum": ""
},
"require": {
@@ -3692,7 +3692,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.35"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.37"
},
"funding": [
{
@@ -3700,7 +3700,7 @@
"type": "other"
}
],
- "time": "2026-09-09T04:48:50+00:00"
+ "time": "2026-09-29T12:08:54+00:00"
},
{
"name": "psr/http-client",
@@ -4729,16 +4729,16 @@
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.37.0",
+ "version": "v1.43.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411"
+ "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
- "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74",
+ "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74",
"shasum": ""
},
"require": {
@@ -4789,7 +4789,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.43.0"
},
"funding": [
{
@@ -4809,20 +4809,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-10T16:19:22+00:00"
+ "time": "2026-09-25T15:50:05+00:00"
},
{
"name": "symfony/polyfill-php82",
- "version": "v1.38.1",
+ "version": "v1.43.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php82.git",
- "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b"
+ "reference": "843bbe8b2a7934307b749751eede0f111f5d1cfc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b",
- "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b",
+ "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/843bbe8b2a7934307b749751eede0f111f5d1cfc",
+ "reference": "843bbe8b2a7934307b749751eede0f111f5d1cfc",
"shasum": ""
},
"require": {
@@ -4869,7 +4869,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php82/tree/v1.38.1"
+ "source": "https://github.com/symfony/polyfill-php82/tree/v1.43.0"
},
"funding": [
{
@@ -4889,7 +4889,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-26T12:45:58+00:00"
+ "time": "2026-09-25T15:50:05+00:00"
},
{
"name": "theseer/tokenizer",
diff --git a/upload/api/vendor/composer/installed.json b/upload/api/vendor/composer/installed.json
index fc82d6f3..5c870950 100644
--- a/upload/api/vendor/composer/installed.json
+++ b/upload/api/vendor/composer/installed.json
@@ -131,17 +131,17 @@
},
{
"name": "laravel/serializable-closure",
- "version": "v2.0.16",
- "version_normalized": "2.0.16.0",
+ "version": "v2.1.0",
+ "version_normalized": "2.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
- "reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed"
+ "reference": "2d5869a838bbcf37e0d8b0568fc41914e81374b5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
- "reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/2d5869a838bbcf37e0d8b0568fc41914e81374b5",
+ "reference": "2d5869a838bbcf37e0d8b0568fc41914e81374b5",
"shasum": ""
},
"require": {
@@ -154,7 +154,7 @@
"phpstan/phpstan": "^2.0",
"symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
},
- "time": "2026-08-18T20:28:54+00:00",
+ "time": "2026-09-22T14:32:34+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -621,17 +621,17 @@
},
{
"name": "monolog/monolog",
- "version": "3.12.0",
- "version_normalized": "3.12.0.0",
+ "version": "3.12.1",
+ "version_normalized": "3.12.1.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "72c534fc0ab181ef52d92a68382318631e301608"
+ "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/72c534fc0ab181ef52d92a68382318631e301608",
- "reference": "72c534fc0ab181ef52d92a68382318631e301608",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7059e4c691d05ea1222b5124bed0f961be46e15",
+ "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15",
"shasum": ""
},
"require": {
@@ -680,7 +680,7 @@
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
},
- "time": "2026-09-09T08:34:20+00:00",
+ "time": "2026-09-29T19:21:12+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -713,7 +713,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
- "source": "https://github.com/Seldaek/monolog/tree/3.12.0"
+ "source": "https://github.com/Seldaek/monolog/tree/3.12.1"
},
"funding": [
{
@@ -1029,17 +1029,17 @@
},
{
"name": "phpstan/phpdoc-parser",
- "version": "2.3.5",
- "version_normalized": "2.3.5.0",
+ "version": "2.3.6",
+ "version_normalized": "2.3.6.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "148cefffaf0233e4c08cc13db8a195a56dd6dfe9"
+ "reference": "1427af4647235b4a73b13940b1f1258d3584f36e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/148cefffaf0233e4c08cc13db8a195a56dd6dfe9",
- "reference": "148cefffaf0233e4c08cc13db8a195a56dd6dfe9",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1427af4647235b4a73b13940b1f1258d3584f36e",
+ "reference": "1427af4647235b4a73b13940b1f1258d3584f36e",
"shasum": ""
},
"require": {
@@ -1056,7 +1056,7 @@
"phpunit/phpunit": "^9.6",
"symfony/process": "^5.2"
},
- "time": "2026-08-31T16:05:28+00:00",
+ "time": "2026-09-27T20:07:45+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1073,7 +1073,7 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
- "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.5"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.6"
},
"install-path": "../phpstan/phpdoc-parser"
},
@@ -1525,17 +1525,17 @@
},
{
"name": "radebatz/type-info-extras",
- "version": "1.0.8",
- "version_normalized": "1.0.8.0",
+ "version": "1.0.9",
+ "version_normalized": "1.0.9.0",
"source": {
"type": "git",
"url": "https://github.com/DerManoMann/type-info-extras.git",
- "reference": "519056728bcd4b4bd883c3ee5d78774d0384e757"
+ "reference": "2d9f01d60d642f890d9f170c0d8fde1f75ab17fb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/DerManoMann/type-info-extras/zipball/519056728bcd4b4bd883c3ee5d78774d0384e757",
- "reference": "519056728bcd4b4bd883c3ee5d78774d0384e757",
+ "url": "https://api.github.com/repos/DerManoMann/type-info-extras/zipball/2d9f01d60d642f890d9f170c0d8fde1f75ab17fb",
+ "reference": "2d9f01d60d642f890d9f170c0d8fde1f75ab17fb",
"shasum": ""
},
"require": {
@@ -1548,7 +1548,7 @@
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.0"
},
- "time": "2026-09-08T08:46:24+00:00",
+ "time": "2026-09-29T22:04:31+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -1584,7 +1584,7 @@
],
"support": {
"issues": "https://github.com/DerManoMann/type-info-extras/issues",
- "source": "https://github.com/DerManoMann/type-info-extras/tree/1.0.8"
+ "source": "https://github.com/DerManoMann/type-info-extras/tree/1.0.9"
},
"install-path": "../radebatz/type-info-extras"
},
@@ -1838,17 +1838,17 @@
},
{
"name": "symfony/console",
- "version": "v7.4.19",
- "version_normalized": "7.4.19.0",
+ "version": "v7.4.20",
+ "version_normalized": "7.4.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "3a1973458b153f566a8d7499bc06ece323d60063"
+ "reference": "d7bca2fe05241dc7732d0c8b6d551b879f5d0a7d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/3a1973458b153f566a8d7499bc06ece323d60063",
- "reference": "3a1973458b153f566a8d7499bc06ece323d60063",
+ "url": "https://api.github.com/repos/symfony/console/zipball/d7bca2fe05241dc7732d0c8b6d551b879f5d0a7d",
+ "reference": "d7bca2fe05241dc7732d0c8b6d551b879f5d0a7d",
"shasum": ""
},
"require": {
@@ -1881,7 +1881,7 @@
"symfony/stopwatch": "^6.4|^7.0|^8.0",
"symfony/var-dumper": "^6.4|^7.0|^8.0"
},
- "time": "2026-09-13T10:38:41+00:00",
+ "time": "2026-09-18T15:55:00+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1915,7 +1915,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v7.4.19"
+ "source": "https://github.com/symfony/console/tree/v7.4.20"
},
"funding": [
{
@@ -2013,17 +2013,17 @@
},
{
"name": "symfony/finder",
- "version": "v7.4.19",
- "version_normalized": "7.4.19.0",
+ "version": "v7.4.20",
+ "version_normalized": "7.4.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe"
+ "reference": "2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/1b900bc6ae5ba60c5eee69c11ba44566190576fe",
- "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e",
+ "reference": "2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e",
"shasum": ""
},
"require": {
@@ -2032,7 +2032,7 @@
"require-dev": {
"symfony/filesystem": "^6.4|^7.0|^8.0"
},
- "time": "2026-09-10T19:14:10+00:00",
+ "time": "2026-09-26T06:31:10+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2060,7 +2060,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v7.4.19"
+ "source": "https://github.com/symfony/finder/tree/v7.4.20"
},
"funding": [
{
@@ -2170,17 +2170,17 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.41.0",
- "version_normalized": "1.41.0.0",
+ "version": "v1.43.0",
+ "version_normalized": "1.43.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d"
+ "reference": "5cac8f4db97208823aa353f59a01faad2da52c75"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
- "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5cac8f4db97208823aa353f59a01faad2da52c75",
+ "reference": "5cac8f4db97208823aa353f59a01faad2da52c75",
"shasum": ""
},
"require": {
@@ -2189,7 +2189,7 @@
"suggest": {
"ext-intl": "For best performance"
},
- "time": "2026-07-28T08:25:59+00:00",
+ "time": "2026-09-25T15:46:47+00:00",
"type": "library",
"extra": {
"thanks": {
@@ -2231,7 +2231,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.43.0"
},
"funding": [
{
@@ -2255,17 +2255,17 @@
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.42.0",
- "version_normalized": "1.42.0.0",
+ "version": "v1.43.0",
+ "version_normalized": "1.43.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502"
+ "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502",
- "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/ebd57ccc7edb93d632e184efbe7693d2517bab3b",
+ "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b",
"shasum": ""
},
"require": {
@@ -2274,7 +2274,7 @@
"suggest": {
"ext-intl": "For best performance"
},
- "time": "2026-08-07T06:33:24+00:00",
+ "time": "2026-09-23T22:02:09+00:00",
"type": "library",
"extra": {
"thanks": {
@@ -2319,7 +2319,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.42.0"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.43.0"
},
"funding": [
{
@@ -2343,17 +2343,17 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.38.2",
- "version_normalized": "1.38.2.0",
+ "version": "v1.43.0",
+ "version_normalized": "1.43.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
+ "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
- "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/4d5d331bf731e0aedbc6b2f5e61135b7b7996a72",
+ "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72",
"shasum": ""
},
"require": {
@@ -2366,7 +2366,7 @@
"suggest": {
"ext-mbstring": "For best performance"
},
- "time": "2026-05-27T06:59:30+00:00",
+ "time": "2026-09-24T15:19:04+00:00",
"type": "library",
"extra": {
"thanks": {
@@ -2407,7 +2407,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.43.0"
},
"funding": [
{
@@ -2615,17 +2615,17 @@
},
{
"name": "symfony/type-info",
- "version": "v7.4.17",
- "version_normalized": "7.4.17.0",
+ "version": "v7.4.20",
+ "version_normalized": "7.4.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/type-info.git",
- "reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31"
+ "reference": "bbaaf3366400f5c85f66800b955c77b46e8abad2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/type-info/zipball/62d0ad6995630a4f7ea8fd270aadb3e2545c8e31",
- "reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31",
+ "url": "https://api.github.com/repos/symfony/type-info/zipball/bbaaf3366400f5c85f66800b955c77b46e8abad2",
+ "reference": "bbaaf3366400f5c85f66800b955c77b46e8abad2",
"shasum": ""
},
"require": {
@@ -2639,7 +2639,7 @@
"require-dev": {
"phpstan/phpdoc-parser": "^1.30|^2.0"
},
- "time": "2026-08-21T15:52:48+00:00",
+ "time": "2026-09-28T12:19:37+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2677,7 +2677,7 @@
"type"
],
"support": {
- "source": "https://github.com/symfony/type-info/tree/v7.4.17"
+ "source": "https://github.com/symfony/type-info/tree/v7.4.20"
},
"funding": [
{
@@ -2701,17 +2701,17 @@
},
{
"name": "symfony/yaml",
- "version": "v7.4.18",
- "version_normalized": "7.4.18.0",
+ "version": "v7.4.20",
+ "version_normalized": "7.4.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "4cef939e55b8a21c5780418de0d98ab00d0736e1"
+ "reference": "0222bf4dfaa1e3ceb8fff579635d655f2950d5a7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/4cef939e55b8a21c5780418de0d98ab00d0736e1",
- "reference": "4cef939e55b8a21c5780418de0d98ab00d0736e1",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/0222bf4dfaa1e3ceb8fff579635d655f2950d5a7",
+ "reference": "0222bf4dfaa1e3ceb8fff579635d655f2950d5a7",
"shasum": ""
},
"require": {
@@ -2725,7 +2725,7 @@
"require-dev": {
"symfony/console": "^6.4|^7.0|^8.0"
},
- "time": "2026-08-30T00:47:26+00:00",
+ "time": "2026-09-26T06:31:10+00:00",
"bin": [
"Resources/bin/yaml-lint"
],
@@ -2756,7 +2756,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/yaml/tree/v7.4.18"
+ "source": "https://github.com/symfony/yaml/tree/v7.4.20"
},
"funding": [
{
@@ -2780,17 +2780,17 @@
},
{
"name": "zircote/swagger-php",
- "version": "6.9.0",
- "version_normalized": "6.9.0.0",
+ "version": "6.11.0",
+ "version_normalized": "6.11.0.0",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
- "reference": "839d6b65c06737e4243635d6f81cbbe13022aa5a"
+ "reference": "f998f7e712658fba61f86fccbdcf8e301a982175"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zircote/swagger-php/zipball/839d6b65c06737e4243635d6f81cbbe13022aa5a",
- "reference": "839d6b65c06737e4243635d6f81cbbe13022aa5a",
+ "url": "https://api.github.com/repos/zircote/swagger-php/zipball/f998f7e712658fba61f86fccbdcf8e301a982175",
+ "reference": "f998f7e712658fba61f86fccbdcf8e301a982175",
"shasum": ""
},
"require": {
@@ -2799,7 +2799,7 @@
"phpstan/phpdoc-parser": "^2.0",
"psr/log": "^1.1 || ^2.0 || ^3.0",
"radebatz/type-info-extras": "^1.0.2",
- "symfony/console": "^7.4 || ^8.0",
+ "symfony/console": "^6.4 || ^7.0 || ^8.0",
"symfony/deprecation-contracts": "^2 || ^3",
"symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0"
@@ -2814,7 +2814,7 @@
"phpunit/phpunit": "^11.5 || >=12.5.22",
"rector/rector": "2.6.5"
},
- "time": "2026-09-13T22:58:11+00:00",
+ "time": "2026-09-26T07:11:30+00:00",
"bin": [
"bin/openapi"
],
@@ -2860,7 +2860,7 @@
],
"support": {
"issues": "https://github.com/zircote/swagger-php/issues",
- "source": "https://github.com/zircote/swagger-php/tree/6.9.0"
+ "source": "https://github.com/zircote/swagger-php/tree/6.11.0"
},
"funding": [
{
diff --git a/upload/api/vendor/composer/installed.php b/upload/api/vendor/composer/installed.php
index 0476877e..14392176 100644
--- a/upload/api/vendor/composer/installed.php
+++ b/upload/api/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'devcraftclub/dle-api',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '609968a85ca5329dc81cca33c741b120108ccd25',
+ 'reference' => '120f945758f45cdffb74a37fceb394ed1759eb3e',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -22,7 +22,7 @@
'devcraftclub/dle-api' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '609968a85ca5329dc81cca33c741b120108ccd25',
+ 'reference' => '120f945758f45cdffb74a37fceb394ed1759eb3e',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -38,9 +38,9 @@
'dev_requirement' => false,
),
'laravel/serializable-closure' => array(
- 'pretty_version' => 'v2.0.16',
- 'version' => '2.0.16.0',
- 'reference' => '7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed',
+ 'pretty_version' => 'v2.1.0',
+ 'version' => '2.1.0.0',
+ 'reference' => '2d5869a838bbcf37e0d8b0568fc41914e81374b5',
'type' => 'library',
'install_path' => __DIR__ . '/../laravel/serializable-closure',
'aliases' => array(),
@@ -104,9 +104,9 @@
),
),
'monolog/monolog' => array(
- 'pretty_version' => '3.12.0',
- 'version' => '3.12.0.0',
- 'reference' => '72c534fc0ab181ef52d92a68382318631e301608',
+ 'pretty_version' => '3.12.1',
+ 'version' => '3.12.1.0',
+ 'reference' => 'd7059e4c691d05ea1222b5124bed0f961be46e15',
'type' => 'library',
'install_path' => __DIR__ . '/../monolog/monolog',
'aliases' => array(),
@@ -158,9 +158,9 @@
'dev_requirement' => false,
),
'phpstan/phpdoc-parser' => array(
- 'pretty_version' => '2.3.5',
- 'version' => '2.3.5.0',
- 'reference' => '148cefffaf0233e4c08cc13db8a195a56dd6dfe9',
+ 'pretty_version' => '2.3.6',
+ 'version' => '2.3.6.0',
+ 'reference' => '1427af4647235b4a73b13940b1f1258d3584f36e',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpdoc-parser',
'aliases' => array(),
@@ -270,9 +270,9 @@
),
),
'radebatz/type-info-extras' => array(
- 'pretty_version' => '1.0.8',
- 'version' => '1.0.8.0',
- 'reference' => '519056728bcd4b4bd883c3ee5d78774d0384e757',
+ 'pretty_version' => '1.0.9',
+ 'version' => '1.0.9.0',
+ 'reference' => '2d9f01d60d642f890d9f170c0d8fde1f75ab17fb',
'type' => 'library',
'install_path' => __DIR__ . '/../radebatz/type-info-extras',
'aliases' => array(),
@@ -306,9 +306,9 @@
'dev_requirement' => false,
),
'symfony/console' => array(
- 'pretty_version' => 'v7.4.19',
- 'version' => '7.4.19.0',
- 'reference' => '3a1973458b153f566a8d7499bc06ece323d60063',
+ 'pretty_version' => 'v7.4.20',
+ 'version' => '7.4.20.0',
+ 'reference' => 'd7bca2fe05241dc7732d0c8b6d551b879f5d0a7d',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
@@ -324,9 +324,9 @@
'dev_requirement' => false,
),
'symfony/finder' => array(
- 'pretty_version' => 'v7.4.19',
- 'version' => '7.4.19.0',
- 'reference' => '1b900bc6ae5ba60c5eee69c11ba44566190576fe',
+ 'pretty_version' => 'v7.4.20',
+ 'version' => '7.4.20.0',
+ 'reference' => '2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/finder',
'aliases' => array(),
@@ -342,27 +342,27 @@
'dev_requirement' => false,
),
'symfony/polyfill-intl-grapheme' => array(
- 'pretty_version' => 'v1.41.0',
- 'version' => '1.41.0.0',
- 'reference' => 'bb899c1db0aa8127dc3afe8cda4a67eb24915f8d',
+ 'pretty_version' => 'v1.43.0',
+ 'version' => '1.43.0.0',
+ 'reference' => '5cac8f4db97208823aa353f59a01faad2da52c75',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
- 'pretty_version' => 'v1.42.0',
- 'version' => '1.42.0.0',
- 'reference' => 'aa20edea75bd9c48cfecc8360922e5a6e5c44502',
+ 'pretty_version' => 'v1.43.0',
+ 'version' => '1.43.0.0',
+ 'reference' => 'ebd57ccc7edb93d632e184efbe7693d2517bab3b',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
- 'pretty_version' => 'v1.38.2',
- 'version' => '1.38.2.0',
- 'reference' => 'd3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6',
+ 'pretty_version' => 'v1.43.0',
+ 'version' => '1.43.0.0',
+ 'reference' => '4d5d331bf731e0aedbc6b2f5e61135b7b7996a72',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
@@ -387,27 +387,27 @@
'dev_requirement' => false,
),
'symfony/type-info' => array(
- 'pretty_version' => 'v7.4.17',
- 'version' => '7.4.17.0',
- 'reference' => '62d0ad6995630a4f7ea8fd270aadb3e2545c8e31',
+ 'pretty_version' => 'v7.4.20',
+ 'version' => '7.4.20.0',
+ 'reference' => 'bbaaf3366400f5c85f66800b955c77b46e8abad2',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/type-info',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/yaml' => array(
- 'pretty_version' => 'v7.4.18',
- 'version' => '7.4.18.0',
- 'reference' => '4cef939e55b8a21c5780418de0d98ab00d0736e1',
+ 'pretty_version' => 'v7.4.20',
+ 'version' => '7.4.20.0',
+ 'reference' => '0222bf4dfaa1e3ceb8fff579635d655f2950d5a7',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/yaml',
'aliases' => array(),
'dev_requirement' => false,
),
'zircote/swagger-php' => array(
- 'pretty_version' => '6.9.0',
- 'version' => '6.9.0.0',
- 'reference' => '839d6b65c06737e4243635d6f81cbbe13022aa5a',
+ 'pretty_version' => '6.11.0',
+ 'version' => '6.11.0.0',
+ 'reference' => 'f998f7e712658fba61f86fccbdcf8e301a982175',
'type' => 'library',
'install_path' => __DIR__ . '/../zircote/swagger-php',
'aliases' => array(),
diff --git a/upload/api/vendor/laravel/serializable-closure/src/Serializers/Native.php b/upload/api/vendor/laravel/serializable-closure/src/Serializers/Native.php
index 8c608c7e..ac95d117 100644
--- a/upload/api/vendor/laravel/serializable-closure/src/Serializers/Native.php
+++ b/upload/api/vendor/laravel/serializable-closure/src/Serializers/Native.php
@@ -55,7 +55,7 @@ class Native implements Serializable
/**
* The closure's reference.
*
- * @var string
+ * @var string|int
*/
protected $reference;
@@ -133,7 +133,9 @@ public function __serialize()
}
}
- $this->reference = spl_object_hash($this->closure);
+ $this->reference = PHP_VERSION_ID >= 80600
+ ? spl_object_id($this->closure)
+ : spl_object_hash($this->closure);
$this->scope[$this->closure] = $this;
@@ -265,7 +267,7 @@ public static function wrapClosures(&$data, $storage)
$instance = $data;
$reflection = new ReflectionObject($instance);
- if (! $reflection->isUserDefined()) {
+ if (! $reflection->isUserDefined() || static::hasInternalAncestry($reflection)) {
$storage[$instance] = $data;
return;
@@ -497,7 +499,7 @@ protected function mapByReference(&$data)
$reflection = new ReflectionObject($data);
- if (! $reflection->isUserDefined()) {
+ if (! $reflection->isUserDefined() || static::hasInternalAncestry($reflection)) {
$this->scope[$instance] = $data;
return;
@@ -566,6 +568,32 @@ protected static function setPropertyValue(ReflectionProperty $property, object
: $property->setValue($object, $value);
}
+ /**
+ * Determine if the class inherits from a PHP internal class.
+ *
+ * Instances of such classes carry internal state that
+ * newInstanceWithoutConstructor() cannot rebuild (e.g. DateTime
+ * subclasses like Carbon), so they must be kept as-is instead of
+ * being reconstructed property by property.
+ *
+ * @param \ReflectionObject $reflection
+ * @return bool
+ */
+ protected static function hasInternalAncestry(ReflectionObject $reflection): bool
+ {
+ $class = $reflection->getParentClass();
+
+ while ($class !== false) {
+ if ($class->isInternal()) {
+ return true;
+ }
+
+ $class = $class->getParentClass();
+ }
+
+ return false;
+ }
+
/**
* Determine is virtual property.
*
diff --git a/upload/api/vendor/laravel/serializable-closure/src/Support/SelfReference.php b/upload/api/vendor/laravel/serializable-closure/src/Support/SelfReference.php
index 58319504..e97f06fb 100644
--- a/upload/api/vendor/laravel/serializable-closure/src/Support/SelfReference.php
+++ b/upload/api/vendor/laravel/serializable-closure/src/Support/SelfReference.php
@@ -7,14 +7,14 @@ class SelfReference
/**
* The unique hash representing the object.
*
- * @var string
+ * @var string|int
*/
public $hash;
/**
* Creates a new self reference instance.
*
- * @param string $hash
+ * @param string|int $hash
* @return void
*/
public function __construct($hash)
diff --git a/upload/api/vendor/monolog/monolog/CHANGELOG.md b/upload/api/vendor/monolog/monolog/CHANGELOG.md
index a94f2ff2..15a35665 100644
--- a/upload/api/vendor/monolog/monolog/CHANGELOG.md
+++ b/upload/api/vendor/monolog/monolog/CHANGELOG.md
@@ -1,3 +1,10 @@
+### 3.12.1 (2026-09-29)
+
+ * Fixed `Logger` silently dropping every record for the rest of the process once the infinite loop guard aborted a nested logging cycle, which could hit long-running workers (#2072)
+ * Fixed `RedactingFormatter` letting secrets containing `&`, `<` or `>` through when wrapping an `HtmlFormatter` (#2069)
+ * Fixed `SocketHandler` throwing the writing timeout immediately when the first write made no progress, instead of waiting for the configured timeout (#2071)
+ * Fixed `RotatingFileHandler` computing the first rotation in the default timezone instead of the `$timezone` passed to the constructor (#2070)
+
### 3.12.0 (2026-09-09)
* Added support for reading the timestamp of new records from a PSR-20 clock, via a new `$clock` constructor param and `Logger::setClock()` (#2065)
diff --git a/upload/api/vendor/monolog/monolog/README.md b/upload/api/vendor/monolog/monolog/README.md
index 0564f96f..52cf8c7c 100644
--- a/upload/api/vendor/monolog/monolog/README.md
+++ b/upload/api/vendor/monolog/monolog/README.md
@@ -1,4 +1,4 @@
-

+
# Monolog - Logging for PHP [](https://github.com/Seldaek/monolog/actions)
diff --git a/upload/api/vendor/monolog/monolog/src/Monolog/Formatter/RedactingFormatter.php b/upload/api/vendor/monolog/monolog/src/Monolog/Formatter/RedactingFormatter.php
index d97a66a1..65a311ae 100644
--- a/upload/api/vendor/monolog/monolog/src/Monolog/Formatter/RedactingFormatter.php
+++ b/upload/api/vendor/monolog/monolog/src/Monolog/Formatter/RedactingFormatter.php
@@ -173,13 +173,14 @@ private function collectSecrets(iterable $records): array
return [];
}
- // a secret containing quotes/backslashes/newlines shows up escaped in json output,
- // so both forms have to be looked for
+ // a secret shows up escaped in json output (quotes/backslashes/newlines) and in html output
+ // (& < >), so every spelling the wrapped formatter can produce has to be looked for
$escapedSecrets = [];
foreach ($secrets as $secret) {
- $escaped = substr(Utils::jsonEncode($secret, null, true), 1, -1);
- if ($escaped !== $secret && $escaped !== '') {
- $escapedSecrets[] = $escaped;
+ foreach ([substr(Utils::jsonEncode($secret, null, true), 1, -1), htmlspecialchars($secret, ENT_NOQUOTES, 'UTF-8')] as $escaped) {
+ if ($escaped !== $secret && $escaped !== '') {
+ $escapedSecrets[] = $escaped;
+ }
}
}
diff --git a/upload/api/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/upload/api/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php
index bb0d2bbc..ccdf2194 100644
--- a/upload/api/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php
+++ b/upload/api/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php
@@ -50,9 +50,9 @@ public function __construct(string $filename, int $maxFiles = 0, int|string|Leve
{
$this->filename = Utils::canonicalizePath($filename);
$this->maxFiles = $maxFiles;
+ $this->timezone = $timezone;
$this->setFilenameFormat($filenameFormat, $dateFormat);
$this->nextRotation = $this->getNextRotation();
- $this->timezone = $timezone;
parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking);
}
diff --git a/upload/api/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/upload/api/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php
index 36d46bf9..4560d763 100644
--- a/upload/api/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php
+++ b/upload/api/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php
@@ -385,6 +385,7 @@ private function writeToSocket(string $data): void
$length = \strlen($data);
$sent = 0;
$this->lastSentBytes = $sent;
+ $this->lastWritingAt = microtime(true);
while ($this->isConnected() && $sent < $length) {
if (0 === $sent) {
$chunk = $this->fwrite($data);
diff --git a/upload/api/vendor/monolog/monolog/src/Monolog/Logger.php b/upload/api/vendor/monolog/monolog/src/Monolog/Logger.php
index 75656235..177e1455 100644
--- a/upload/api/vendor/monolog/monolog/src/Monolog/Logger.php
+++ b/upload/api/vendor/monolog/monolog/src/Monolog/Logger.php
@@ -353,15 +353,15 @@ public function addRecord(int|Level $level, string $message, array $context = []
$logDepth = 0;
}
- if ($logDepth === 3) {
- $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.');
+ try {
+ if ($logDepth === 3) {
+ $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.');
- return false;
- } elseif ($logDepth >= 5) { // log depth 4 is let through, so we can log the warning above
- return false;
- }
+ return false;
+ } elseif ($logDepth >= 5) { // log depth 4 is let through, so we can log the warning above
+ return false;
+ }
- try {
$recordInitialized = \count($this->processors) === 0;
$record = new LogRecord(
diff --git a/upload/api/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php b/upload/api/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php
index f42cc2fb..043639f1 100644
--- a/upload/api/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php
+++ b/upload/api/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php
@@ -31,6 +31,9 @@ class TokenIterator
private ?string $newline = null;
+ /** @var list|null */
+ private ?array $offsets = null;
+
/**
* @param list $tokens
*/
@@ -81,12 +84,16 @@ public function currentTokenType(): int
public function currentTokenOffset(): int
{
- $offset = 0;
- for ($i = 0; $i < $this->index; $i++) {
- $offset += strlen($this->tokens[$i][Lexer::VALUE_OFFSET]);
+ if ($this->offsets === null) {
+ $offset = 0;
+ $this->offsets = [];
+ foreach ($this->tokens as $token) {
+ $this->offsets[] = $offset;
+ $offset += strlen($token[Lexer::VALUE_OFFSET]);
+ }
}
- return $offset;
+ return $this->offsets[$this->index];
}
public function currentTokenLine(): int
diff --git a/upload/api/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php b/upload/api/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php
index 37d4973f..efcd3be0 100644
--- a/upload/api/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php
+++ b/upload/api/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php
@@ -111,10 +111,18 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Bas
if ($node instanceof ArrayShapeNode) {
$shape = [];
foreach ($node->items as $item) {
- $shape[(string) $item->keyName] = [
+ $shapeItem = [
'type' => $this->getTypeFromNode($item->valueType, $typeContext),
'optional' => $item->optional,
];
+
+ if (null === $item->keyName) {
+ $shape[] = $shapeItem;
+
+ continue;
+ }
+
+ $shape[$item->keyName instanceof ConstExprStringNode ? $item->keyName->value : (string) $item->keyName] = $shapeItem;
}
return Type::arrayShape(
@@ -128,7 +136,7 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Bas
if ($node instanceof ObjectShapeNode) {
$shape = [];
foreach ($node->items as $item) {
- $shape[(string) $item->keyName] = [
+ $shape[$item->keyName instanceof ConstExprStringNode ? $item->keyName->value : (string) $item->keyName] = [
'type' => $this->getTypeFromNode($item->valueType, $typeContext),
'optional' => $item->optional,
];
@@ -149,7 +157,13 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Bas
if ($node instanceof ConstTypeNode) {
if ($node->constExpr instanceof ConstFetchNode) {
- $className = match (strtolower($node->constExpr->className)) {
+ $classKeyword = strtolower($node->constExpr->className);
+
+ if (null === $typeContext && \in_array($classKeyword, ['self', 'static', 'parent'], true)) {
+ throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "%s".', TypeContext::class, $classKeyword));
+ }
+
+ $className = match ($classKeyword) {
'self' => $typeContext->getDeclaringClass(),
'static' => $typeContext->getCalledClass(),
'parent' => $typeContext->getParentClass(),
@@ -177,6 +191,10 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Bas
}
}
+ if (!$types) {
+ throw new \DomainException(\sprintf('No "%s" constant found in "%s".', $node->constExpr->name, $className));
+ }
+
return CollectionType::mergeCollectionValueTypes($types);
}
diff --git a/upload/api/vendor/symfony/console/Formatter/OutputFormatter.php b/upload/api/vendor/symfony/console/Formatter/OutputFormatter.php
index 6e402ec4..1e757183 100644
--- a/upload/api/vendor/symfony/console/Formatter/OutputFormatter.php
+++ b/upload/api/vendor/symfony/console/Formatter/OutputFormatter.php
@@ -123,10 +123,6 @@ public function formatAndWrap(?string $message, int $width): string
return '';
}
- // For ASCII-only strings, byte positions equal character positions,
- // so we can use native strlen/substr which is much faster than Helper::length/substr.
- $isAscii = !preg_match('/[\x80-\xFF]/', $message);
-
$offset = 0;
$output = '';
$openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
@@ -141,17 +137,10 @@ public function formatAndWrap(?string $message, int $width): string
continue;
}
- if ($isAscii) {
- // For ASCII, byte position = character position, no conversion needed
- $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
- $offset = $pos + \strlen($text);
- } else {
- // convert byte position to character position.
- $pos = Helper::length(substr($message, 0, $pos));
- // add the text up to the next tag
- $output .= $this->applyCurrentStyle(Helper::substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
- $offset = $pos + Helper::length($text);
- }
+ // tags are delimited by "<" and ">", which are ASCII, so a tag offset can never
+ // fall inside a multibyte sequence: slicing by bytes is safe here
+ $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
+ $offset = $pos + \strlen($text);
// opening tag?
if ($open = '/' !== $text[1]) {
@@ -172,7 +161,7 @@ public function formatAndWrap(?string $message, int $width): string
}
}
- $output .= $this->applyCurrentStyle($isAscii ? substr($message, $offset) : Helper::substr($message, $offset), $output, $width, $currentLineLength);
+ $output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
}
diff --git a/upload/api/vendor/symfony/finder/Glob.php b/upload/api/vendor/symfony/finder/Glob.php
index d950e48f..ac11e8e0 100644
--- a/upload/api/vendor/symfony/finder/Glob.php
+++ b/upload/api/vendor/symfony/finder/Glob.php
@@ -77,7 +77,10 @@ public static function toRegex(string $glob, bool $strictLeadingDot = true, bool
if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
$regex .= "\\$car";
} elseif ('*' === $car) {
- $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
+ if ($escaping || $strictWildcardSlash || !str_ends_with($regex, '.*')) {
+ // ".*.*" matches the same as ".*" but backtracks quadratically
+ $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
+ }
} elseif ('?' === $car) {
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
} elseif ('{' === $car) {
diff --git a/upload/api/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php b/upload/api/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php
index 9e69d0e3..3715e269 100644
--- a/upload/api/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php
+++ b/upload/api/vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.php
@@ -39,7 +39,7 @@ public function __construct(\Iterator $iterator, string $baseDir)
$this->baseDir = $this->normalizePath(realpath($baseDir) ?: $baseDir);
foreach ([$this->baseDir, ...$this->parentDirectoriesUpwards($this->baseDir)] as $directory) {
- if (@is_dir("{$directory}/.git")) {
+ if (@file_exists("{$directory}/.git")) {
$this->baseDir = $directory;
break;
}
diff --git a/upload/api/vendor/symfony/polyfill-intl-grapheme/Grapheme.php b/upload/api/vendor/symfony/polyfill-intl-grapheme/Grapheme.php
index cad3b838..94d080ac 100644
--- a/upload/api/vendor/symfony/polyfill-intl-grapheme/Grapheme.php
+++ b/upload/api/vendor/symfony/polyfill-intl-grapheme/Grapheme.php
@@ -40,6 +40,10 @@ final class Grapheme
// This regular expression is a work around for http://bugs.exim.org/1279
public const GRAPHEME_CLUSTER_RX = '(?:\r\n|[\x{1F1E6}-\x{1F1FF}][\x{1F1E6}-\x{1F1FF}]?|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*(?:\x{200D}(?:[\x{1F1E6}-\x{1F1FF}]|[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가-힣]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*)*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';
+ // Table 3-7 of the Unicode standard for the well-formed byte sequences (group 1),
+ // then the maximal subparts an ill-formed sequence is made of (group 2).
+ private const UTF8_CHAR_OR_SUBPART_RX = '/([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|[\xEE-\xEF][\x80-\xBF]{2}|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|(\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|\xE0[\xA0-\xBF]|[\xE1-\xEC][\x80-\xBF]|\xED[\x80-\x9F]|[\xEE-\xEF][\x80-\xBF]|[\x80-\xFF])/s';
+
private const CASE_FOLD = [
['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'],
@@ -130,6 +134,10 @@ public static function grapheme_substr($s, $start, $len = null)
$len = 2147483647;
}
+ if (!self::isUtf8($s)) {
+ return false;
+ }
+
preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s, $s);
$slen = \count($s[0]);
@@ -189,33 +197,47 @@ public static function grapheme_strripos($s, $needle, $offset = 0)
public static function grapheme_stristr($s, $needle, $beforeNeedle = false)
{
+ if (!self::isUtf8($s) || !self::isUtf8($needle)) {
+ return false;
+ }
+
return mb_stristr($s, $needle, $beforeNeedle, 'UTF-8');
}
public static function grapheme_strstr($s, $needle, $beforeNeedle = false)
{
+ if (!self::isUtf8($s) || !self::isUtf8($needle)) {
+ return false;
+ }
+
return mb_strstr($s, $needle, $beforeNeedle, 'UTF-8');
}
public static function grapheme_str_split($s, $len = 1)
{
- if (0 > $len || 1073741823 < $len) {
- throw new \ValueError('grapheme_str_split(): Argument #2 ($length) must be greater than 0 and less than or equal to 1073741823.');
+ if (0 >= $len || 1073741823 < $len) {
+ throw new \ValueError('grapheme_str_split(): Argument #2 ($length) must be greater than 0 and less than or equal to 1073741823');
}
if ('' === $s) {
return [];
}
- if (!preg_match_all('/('.SYMFONY_GRAPHEME_CLUSTER_RX.')/u', $s, $matches)) {
+ if (preg_match_all('/('.SYMFONY_GRAPHEME_CLUSTER_RX.')/u', $s, $matches)) {
+ $graphemes = $matches[0];
+ } else {
+ $graphemes = self::splitIllFormed($s);
+ }
+
+ if (!$graphemes) {
return false;
}
if (1 === $len) {
- return $matches[0];
+ return $graphemes;
}
- $chunks = array_chunk($matches[0], $len);
+ $chunks = array_chunk($graphemes, $len);
foreach ($chunks as &$chunk) {
$chunk = implode('', $chunk);
@@ -224,14 +246,20 @@ public static function grapheme_str_split($s, $len = 1)
return $chunks;
}
- public static function grapheme_levenshtein($s1, $s2, $insertion_cost = 1, $replacement_cost = 1, $deletion_cost = 1)
+ public static function grapheme_levenshtein($s1, $s2, $insertion_cost = 1, $replacement_cost = 1, $deletion_cost = 1, $locale = '')
{
- if (!preg_match('//u', $s1) || !preg_match('//u', $s2)) {
- return false;
+ if ($insertion_cost <= 0 || $insertion_cost > 1073741823) {
+ throw new \ValueError('grapheme_levenshtein(): Argument #3 ($insertion_cost) must be greater than 0 and less than or equal to 1073741823');
+ }
+ if ($replacement_cost <= 0 || $replacement_cost > 1073741823) {
+ throw new \ValueError('grapheme_levenshtein(): Argument #4 ($replacement_cost) must be greater than 0 and less than or equal to 1073741823');
+ }
+ if ($deletion_cost <= 0 || $deletion_cost > 1073741823) {
+ throw new \ValueError('grapheme_levenshtein(): Argument #5 ($deletion_cost) must be greater than 0 and less than or equal to 1073741823');
}
- if (0 > $insertion_cost || 0 > $replacement_cost || 0 > $deletion_cost) {
- throw new \ValueError('grapheme_levenshtein(): Argument #3 ($insertion_cost), #4 ($replacement_cost), and #5 ($deletion_cost) must be greater than or equal to 0');
+ if (!preg_match('//u', $s1) || !preg_match('//u', $s2)) {
+ return false;
}
preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s1, $s1);
@@ -239,33 +267,58 @@ public static function grapheme_levenshtein($s1, $s2, $insertion_cost = 1, $repl
$s1 = $s1[0];
$s2 = $s2[0];
+
$l1 = \count($s1);
$l2 = \count($s2);
- if (0 === $l1) {
- return $l2 * $insertion_cost;
+ // Keep the rows as short as possible. Reversing the transformation
+ // swaps the meaning of insertion and deletion.
+ if ($l1 < $l2) {
+ [$s1, $s2] = [$s2, $s1];
+ [$l1, $l2] = [$l2, $l1];
+ [$insertion_cost, $deletion_cost] = [$deletion_cost, $insertion_cost];
}
+
if (0 === $l2) {
return $l1 * $deletion_cost;
}
- $dp = array_fill(0, $l1 + 1, array_fill(0, $l2 + 1, 0));
-
- for ($i = 1; $i <= $l1; ++$i) {
- $dp[$i][0] = $dp[$i - 1][0] + $deletion_cost;
+ if (\extension_loaded('intl')) {
+ // Graphemes are equal when the collator says so, as with ucol_strcoll() in intl
+ try {
+ $collator = new \Collator('' === $locale ? 'root' : $locale);
+ } catch (\IntlException $e) {
+ return false;
+ }
+ $s1 = array_map([$collator, 'getSortKey'], $s1);
+ $s2 = array_map([$collator, 'getSortKey'], $s2);
+ } elseif (class_exists(\Normalizer::class)) {
+ // Without a collator, at least treat canonically equivalent graphemes as equal
+ foreach ($s1 as $i => $g) {
+ $s1[$i] = \Normalizer::normalize($g, \Normalizer::FORM_D);
+ }
+ foreach ($s2 as $i => $g) {
+ $s2[$i] = \Normalizer::normalize($g, \Normalizer::FORM_D);
+ }
}
+
+ $previousRow = $currentRow = array_fill(0, $l2 + 1, 0);
for ($j = 1; $j <= $l2; ++$j) {
- $dp[0][$j] = $dp[0][$j - 1] + $insertion_cost;
+ $previousRow[$j] = $previousRow[$j - 1] + $insertion_cost;
}
for ($i = 1; $i <= $l1; ++$i) {
+ $currentRow[0] = $previousRow[0] + $deletion_cost;
+
for ($j = 1; $j <= $l2; ++$j) {
$cost = ($s1[$i - 1] === $s2[$j - 1]) ? 0 : $replacement_cost;
- $dp[$i][$j] = min($dp[$i - 1][$j] + $deletion_cost, $dp[$i][$j - 1] + $insertion_cost, $dp[$i - 1][$j - 1] + $cost);
+ $currentRow[$j] = min($previousRow[$j] + $deletion_cost, $currentRow[$j - 1] + $insertion_cost, $previousRow[$j - 1] + $cost);
}
+
+ [$previousRow, $currentRow] = [$currentRow, $previousRow];
}
- return $dp[$l1][$l2];
+ return $previousRow[$l2];
}
private static function grapheme_position($s, $needle, $offset, $mode)
@@ -335,16 +388,69 @@ private static function grapheme_position($s, $needle, $offset, $mode)
public static function grapheme_strrev(string $string)
{
- if (!preg_match('//u', $string)) {
- return false;
+ if (\extension_loaded('intl')) {
+ $iterator = \IntlBreakIterator::createCharacterInstance();
+ $iterator->setText($string);
+ $reversed = '';
+ $end = $iterator->last();
+
+ while (\IntlBreakIterator::DONE !== $start = $iterator->previous()) {
+ $reversed .= substr($string, $start, $end - $start);
+ $end = $start;
+ }
+
+ return $reversed;
}
- $units = grapheme_str_split($string);
+ return implode('', array_reverse(self::grapheme_str_split($string)));
+ }
- if (false === $units) {
- return false;
+ private static function isUtf8(?string $s)
+ {
+ $s = $s ?? '';
+
+ return '' === $s || preg_match('/./us', $s);
+ }
+
+ /**
+ * Splits a string that is not valid UTF-8 into grapheme clusters.
+ *
+ * The intl extension keeps the ill-formed bytes and breaks around them the
+ * way it would around the U+FFFD each maximal subpart stands for. Standing
+ * one in for real makes the cluster regexp applicable, then each cluster is
+ * mapped back to the bytes it was made of.
+ */
+ private static function splitIllFormed($s)
+ {
+ preg_match_all(self::UTF8_CHAR_OR_SUBPART_RX, $s, $matches, \PREG_SET_ORDER);
+
+ $segments = [];
+ $segmentAt = [];
+ $sanitized = '';
+
+ foreach ($matches as $m) {
+ $segmentAt[\strlen($sanitized)] = \count($segments);
+
+ if (isset($m[2]) && '' !== $m[2]) {
+ $segments[] = $m[2];
+ $sanitized .= "\xEF\xBF\xBD";
+ } else {
+ $segments[] = $m[1];
+ $sanitized .= $m[1];
+ }
+ }
+
+ $segmentAt[\strlen($sanitized)] = \count($segments);
+
+ preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $sanitized, $clusters, \PREG_OFFSET_CAPTURE);
+
+ $graphemes = [];
+
+ foreach ($clusters[0] as $cluster) {
+ $start = $segmentAt[$cluster[1]];
+ $graphemes[] = implode('', \array_slice($segments, $start, $segmentAt[$cluster[1] + \strlen($cluster[0])] - $start));
}
- return implode('', array_reverse($units));
+ return $graphemes;
}
}
diff --git a/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap.php b/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap.php
index 4fd87741..d6795031 100644
--- a/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap.php
+++ b/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap.php
@@ -62,7 +62,7 @@ function grapheme_substr($string, $offset, $length = null) { return p\Grapheme::
function grapheme_str_split(string $string, int $length = 1) { return p\Grapheme::grapheme_str_split($string, $length); }
}
if (!function_exists('grapheme_levenshtein')) {
- function grapheme_levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = '') { return p\Grapheme::grapheme_levenshtein($string1, $string2, $insertion_cost, $replacement_cost, $deletion_cost); }
+ function grapheme_levenshtein(string $string1, string $string2, ?int $insertion_cost = 1, ?int $replacement_cost = 1, ?int $deletion_cost = 1, string $locale = '') { return p\Grapheme::grapheme_levenshtein($string1, $string2, (int) $insertion_cost, (int) $replacement_cost, (int) $deletion_cost, $locale); }
}
if (!function_exists('grapheme_strrev')) {
function grapheme_strrev(string $string) { return p\Grapheme::grapheme_strrev($string); }
diff --git a/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php b/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php
index b05cfde5..c913b00b 100644
--- a/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php
+++ b/upload/api/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php
@@ -15,7 +15,7 @@
function grapheme_str_split(string $string, int $length = 1): array|false { return p\Grapheme::grapheme_str_split($string, $length); }
}
if (!function_exists('grapheme_levenshtein')) {
- function grapheme_levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = ''): int|false { return p\Grapheme::grapheme_levenshtein($string1, $string2, $insertion_cost, $replacement_cost, $deletion_cost); }
+ function grapheme_levenshtein(string $string1, string $string2, ?int $insertion_cost = 1, ?int $replacement_cost = 1, ?int $deletion_cost = 1, string $locale = ''): int|false { return p\Grapheme::grapheme_levenshtein($string1, $string2, (int) $insertion_cost, (int) $replacement_cost, (int) $deletion_cost, $locale); }
}
if (!function_exists('grapheme_strrev')) {
function grapheme_strrev(string $string): string|false { return p\Grapheme::grapheme_strrev($string); }
diff --git a/upload/api/vendor/symfony/polyfill-intl-normalizer/Normalizer.php b/upload/api/vendor/symfony/polyfill-intl-normalizer/Normalizer.php
index d8c71b87..3a3312d8 100644
--- a/upload/api/vendor/symfony/polyfill-intl-normalizer/Normalizer.php
+++ b/upload/api/vendor/symfony/polyfill-intl-normalizer/Normalizer.php
@@ -41,29 +41,33 @@ class Normalizer
private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
- public static function isNormalized(string $s, int $form = self::FORM_C)
+ public static function isNormalized(string $string, int $form = self::FORM_C)
{
if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) {
- return false;
+ if (80000 > \PHP_VERSION_ID) {
+ return false;
+ }
+
+ throw new \ValueError('normalizer_is_normalized(): Argument #2 ($form) must be a '.(80600 > \PHP_VERSION_ID ? 'a ' : '').'valid normalization form');
}
- if (!isset($s[strspn($s, self::$ASCII)])) {
+ if (!isset($string[strspn($string, self::$ASCII)])) {
return true;
}
- if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) {
+ if (self::NFC == $form && preg_match('//u', $string) && !preg_match('/[^\x00-\x{2FF}]/u', $string)) {
return true;
}
- return self::normalize($s, $form) === $s;
+ return self::normalize($string, $form) === $string;
}
- public static function getRawDecomposition(string $s, int $form = self::FORM_C)
+ public static function getRawDecomposition(string $string, int $form = self::FORM_C)
{
- if ('' === $s || !preg_match('//u', $s)) {
+ if ('' === $string || !preg_match('//u', $string)) {
return null;
}
- $ulen = $s[0] < "\x80" ? 1 : (self::$ulenMask[$s[0] & "\xF0"] ?? 0);
- if (!$ulen || \strlen($s) !== $ulen) {
+ $ulen = $string[0] < "\x80" ? 1 : (self::$ulenMask[$string[0] & "\xF0"] ?? 0);
+ if (!$ulen || \strlen($string) !== $ulen) {
return null;
}
@@ -71,8 +75,8 @@ public static function getRawDecomposition(string $s, int $form = self::FORM_C)
return '';
}
- if ($s >= "\xEA\xB0\x80" && $s <= "\xED\x9E\xA3") {
- $u = unpack('C*', $s);
+ if ($string >= "\xEA\xB0\x80" && $string <= "\xED\x9E\xA3") {
+ $u = unpack('C*', $string);
$j = (($u[1] - 224) << 12) + (($u[2] - 128) << 6) + $u[3] - 0xAC80;
if ($t = $j % 28) {
@@ -94,8 +98,8 @@ public static function getRawDecomposition(string $s, int $form = self::FORM_C)
self::$rawD = self::getData('rawCanonicalDecomposition');
}
- if (isset(self::$rawD[$s])) {
- return self::$rawD[$s];
+ if (isset(self::$rawD[$string])) {
+ return self::$rawD[$string];
}
if (self::NFKC === $form || self::NFKD === $form) {
@@ -103,18 +107,14 @@ public static function getRawDecomposition(string $s, int $form = self::FORM_C)
self::$rawKD = self::getData('rawCompatibilityDecomposition');
}
- return self::$rawKD[$s] ?? null;
+ return self::$rawKD[$string] ?? null;
}
return null;
}
- public static function normalize(string $s, int $form = self::FORM_C)
+ public static function normalize(string $string, int $form = self::FORM_C)
{
- if (!preg_match('//u', $s)) {
- return false;
- }
-
switch ($form) {
case self::NFC: $C = true; $K = false; break;
case self::NFD: $C = false; $K = false; break;
@@ -122,7 +122,7 @@ public static function normalize(string $s, int $form = self::FORM_C)
case self::NFKD: $C = false; $K = true; break;
default:
if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) {
- return $s;
+ return preg_match('//u', $string) ? $string : false;
}
if (80000 > \PHP_VERSION_ID) {
@@ -133,7 +133,11 @@ public static function normalize(string $s, int $form = self::FORM_C)
throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a '.(80600 > \PHP_VERSION_ID ? 'a ' : '').'valid normalization form');
}
- if ('' === $s) {
+ if (!preg_match('//u', $string)) {
+ return false;
+ }
+
+ if ('' === $string) {
return '';
}
@@ -150,7 +154,7 @@ public static function normalize(string $s, int $form = self::FORM_C)
mb_internal_encoding('8bit');
}
- $r = self::decompose($s, $K);
+ $r = self::decompose($string, $K);
if ($C) {
if (null === self::$C) {
diff --git a/upload/api/vendor/symfony/polyfill-mbstring/Mbstring.php b/upload/api/vendor/symfony/polyfill-mbstring/Mbstring.php
index e3d1f965..bc9e6acf 100644
--- a/upload/api/vendor/symfony/polyfill-mbstring/Mbstring.php
+++ b/upload/api/vendor/symfony/polyfill-mbstring/Mbstring.php
@@ -79,20 +79,58 @@ final class Mbstring
['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'],
];
+ // Encodings rejected by mb_ord() and mb_chr(), with their aliases
+ private const UNSUPPORTED_CODEPOINT_ENCODINGS = [
+ 'BASE64' => 'BASE64',
+ 'UUENCODE' => 'UUENCODE',
+ 'X-UUENCODE' => 'UUENCODE',
+ 'HTML-ENTITIES' => 'HTML-ENTITIES',
+ 'HTML' => 'HTML-ENTITIES',
+ 'QUOTED-PRINTABLE' => 'Quoted-Printable',
+ 'QPRINT' => 'Quoted-Printable',
+ 'UTF-7' => 'UTF-7',
+ 'UTF7' => 'UTF-7',
+ 'UTF7-IMAP' => 'UTF7-IMAP',
+ 'MUTF-7' => 'UTF7-IMAP',
+ 'JIS' => 'JIS',
+ 'ISO-2022-JP' => 'ISO-2022-JP',
+ 'ISO-2022-JP-2004' => 'ISO-2022-JP-2004',
+ 'ISO-2022-JP-MOBILE#KDDI' => 'ISO-2022-JP-MOBILE#KDDI',
+ 'ISO-2022-JP-KDDI' => 'ISO-2022-JP-MOBILE#KDDI',
+ 'ISO-2022-JP-MS' => 'ISO-2022-JP-MS',
+ 'ISO2022JPMS' => 'ISO-2022-JP-MS',
+ 'CP50220' => 'CP50220',
+ 'CP50220RAW' => 'CP50220',
+ 'CP50220-RAW' => 'CP50220',
+ 'JIS-MS' => 'CP50220',
+ 'CP50221' => 'CP50221',
+ 'CP50222' => 'CP50222',
+ ];
+
+ // Well-formed characters in group 1, then the maximal subparts of ill-formed sequences
+ private const UTF8_CHAR_REGEX = '/([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|\xE0[\xA0-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]|\xED[\x80-\x9F]|\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|[\x80-\xFF]/';
+
private static $encodingList = ['ASCII', 'UTF-8'];
private static $language = 'neutral';
private static $internalEncoding = 'UTF-8';
private static $iconvSupportsIgnore;
+ private static $convertibleEncodings = ['UTF-8' => true];
public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
{
- if (\is_array($s)) {
- $r = [];
- foreach ($s as $str) {
- $r[] = self::mb_convert_encoding($str, $toEncoding, $fromEncoding);
- }
+ if (\is_string($fromEncoding) && 2 < \strlen($fromEncoding) && '"' === $fromEncoding[0] && '"' === $fromEncoding[-1]) {
+ $fromEncoding = substr($fromEncoding, 1, -1);
+ }
- return $r;
+ if (80000 <= \PHP_VERSION_ID
+ && (!isset(self::$convertibleEncodings[$toEncoding]) || (null !== $fromEncoding && !(\is_string($fromEncoding) && isset(self::$convertibleEncodings[$fromEncoding]))))
+ && null !== $error = self::getConversionError('mb_convert_encoding', 2, $toEncoding, $fromEncoding)
+ ) {
+ throw new \ValueError($error);
+ }
+
+ if (\is_array($s)) {
+ return self::convertArray($s, $toEncoding, $fromEncoding);
}
if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) {
@@ -118,6 +156,8 @@ public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null
}
if ('UTF-8' !== $fromEncoding) {
$s = self::iconv($fromEncoding, 'UTF-8', $s);
+ } elseif (!preg_match('//u', $s)) {
+ $s = self::scrubUtf8($s);
}
return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s);
@@ -157,17 +197,42 @@ public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null
$fromEncoding = 'UTF-8';
}
+ if ('UTF-8' === $fromEncoding && !preg_match('//u', $s)) {
+ $s = self::scrubUtf8($s);
+ }
+
return self::iconv($fromEncoding, $toEncoding, $s);
}
public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
{
+ if (\is_string($fromEncoding) && 2 < \strlen($fromEncoding) && '"' === $fromEncoding[0] && '"' === $fromEncoding[-1]) {
+ $fromEncoding = substr($fromEncoding, 1, -1);
+ }
+
+ if (80000 <= \PHP_VERSION_ID && null !== $error = self::getConversionError('mb_convert_variables', 1, $toEncoding, $fromEncoding)) {
+ throw new \ValueError($error);
+ }
+
$ok = true;
- array_walk_recursive($vars, static function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
- if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
- $ok = false;
+ foreach ($vars as $i => &$var) {
+ $convert = static function (&$v) use (&$ok, $toEncoding, $fromEncoding, $i) {
+ if (\is_string($v)) {
+ if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
+ $ok = false;
+ }
+ } elseif (80600 <= \PHP_VERSION_ID && !\is_object($v)) {
+ trigger_error(\sprintf('mb_convert_variables(): Argument #%d must be of type string|array|object or only contain entries of type string|array|object, %s given', 3 + $i, strtok(get_debug_type($v), ' ')), \E_USER_WARNING);
+ }
+ };
+
+ if (\is_array($var)) {
+ array_walk_recursive($var, $convert);
+ } else {
+ $convert($var);
}
- });
+ }
+ unset($var);
return $ok ? $fromEncoding : false;
}
@@ -210,7 +275,7 @@ public static function mb_decode_numericentity($s, $convmap, $encoding = null)
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
- $s = @self::iconv('UTF-8', 'UTF-8', $s);
+ $s = self::scrubUtf8($s);
}
} else {
$s = self::iconv($encoding, 'UTF-8', $s);
@@ -276,7 +341,7 @@ public static function mb_encode_numericentity($s, $convmap, $encoding = null, $
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
- $s = @self::iconv('UTF-8', 'UTF-8', $s);
+ $s = self::scrubUtf8($s);
}
} else {
$s = self::iconv($encoding, 'UTF-8', $s);
@@ -290,10 +355,15 @@ public static function mb_encode_numericentity($s, $convmap, $encoding = null, $
$result = '';
while ($i < $len) {
- $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
+ $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"] ?? 1;
$uchr = substr($s, $i, $ulen);
$i += $ulen;
- $c = self::mb_ord($uchr);
+
+ // code points above U+10FFFF are outside any convmap, but mb_ord() rejects them
+ if (false === $c = self::mb_ord($uchr)) {
+ $result .= $uchr;
+ continue;
+ }
for ($j = 0; $j < $cnt; $j += 4) {
if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
@@ -324,7 +394,7 @@ public static function mb_convert_case($s, $mode, $encoding = null)
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
- $s = @self::iconv('UTF-8', 'UTF-8', $s);
+ $s = self::scrubUtf8($s);
}
} else {
$s = self::iconv($encoding, 'UTF-8', $s);
@@ -365,7 +435,7 @@ public static function mb_convert_case($s, $mode, $encoding = null)
$len = \strlen($s);
while ($i < $len) {
- $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
+ $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"] ?? 1;
$uchr = substr($s, $i, $ulen);
$i += $ulen;
@@ -462,6 +532,10 @@ public static function mb_check_encoding($var = null, $encoding = null)
}
if (!\is_array($var)) {
+ if ('UTF-8' === self::getEncoding($encoding)) {
+ return (bool) preg_match('//u', $var);
+ }
+
return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var);
}
@@ -562,50 +636,16 @@ public static function mb_strlen($s, $encoding = null)
public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
{
- $encoding = self::getEncoding($encoding);
- if ('CP850' === $encoding || 'ASCII' === $encoding) {
- return strpos($haystack, $needle, $offset);
- }
-
- $needle = (string) $needle;
- if ('' === $needle) {
- if (80000 > \PHP_VERSION_ID) {
- trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING);
-
- return false;
- }
-
- return 0;
- }
-
- return iconv_strpos($haystack, $needle, $offset, $encoding);
+ return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, false, false);
}
public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
{
- $encoding = self::getEncoding($encoding);
- if ('CP850' === $encoding || 'ASCII' === $encoding) {
- return strrpos($haystack, $needle, $offset);
- }
-
if ($offset != (int) $offset) {
$offset = 0;
- } elseif ($offset = (int) $offset) {
- if ($offset < 0) {
- if (0 > $offset += self::mb_strlen($needle)) {
- $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
- }
- $offset = 0;
- } else {
- $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
- }
}
- $pos = '' !== $needle || 80000 > \PHP_VERSION_ID
- ? iconv_strrpos($haystack, $needle, $encoding)
- : self::mb_strlen($haystack, $encoding);
-
- return false !== $pos ? $offset + $pos : false;
+ return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, true, false);
}
public static function mb_str_split($string, $split_length = 1, $encoding = null)
@@ -626,17 +666,27 @@ public static function mb_str_split($string, $split_length = 1, $encoding = null
throw new \ValueError('Argument #2 ($length) must be greater than 0');
}
+ if (80300 <= \PHP_VERSION_ID && 0x3FFFFFFF < $split_length) {
+ throw new \ValueError('Argument #2 ($length) is too large');
+ }
+
if (null === $encoding) {
$encoding = mb_internal_encoding();
}
if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
+ $string = (string) $string;
+
+ if (\strlen($string) <= $split_length) {
+ return '' === $string ? [] : [$string];
+ }
+
$rx = '/(';
while (65535 < $split_length) {
$rx .= '.{65535}';
$split_length -= 65535;
}
- $rx .= '.{'.$split_length.'})/us';
+ $rx .= '.{'.$split_length.'})/Aus';
return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY);
}
@@ -707,64 +757,32 @@ public static function mb_substr($s, $start, $length = null, $encoding = null)
public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
{
- [$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [
- self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding),
- self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding),
- ]);
-
- return self::mb_strpos($haystack, $needle, $offset, $encoding);
+ return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, false, true);
}
public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
{
- $pos = self::mb_stripos($haystack, $needle, 0, $encoding);
-
- return self::getSubpart($pos, $part, $haystack, $encoding);
+ return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, false, true);
}
public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
{
- $encoding = self::getEncoding($encoding);
- if ('CP850' === $encoding || 'ASCII' === $encoding) {
- $pos = strrpos($haystack, $needle);
- } else {
- $needle = self::mb_substr($needle, 0, 1, $encoding);
- $pos = iconv_strrpos($haystack, $needle, $encoding);
- }
-
- return self::getSubpart($pos, $part, $haystack, $encoding);
+ return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, true, false);
}
public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
{
- $needle = self::mb_substr($needle, 0, 1, $encoding);
- $pos = self::mb_strripos($haystack, $needle, $encoding);
-
- return self::getSubpart($pos, $part, $haystack, $encoding);
+ return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, true, true);
}
public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
{
- $haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding);
- $needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding);
-
- $haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack);
- $needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle);
-
- return self::mb_strrpos($haystack, $needle, $offset, $encoding);
+ return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, true, true);
}
public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
{
- $pos = strpos($haystack, $needle);
- if (false === $pos) {
- return false;
- }
- if ($part) {
- return substr($haystack, 0, $pos);
- }
-
- return substr($haystack, $pos);
+ return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, false, false);
}
public static function mb_get_info($type = 'all')
@@ -809,19 +827,37 @@ public static function mb_http_output($encoding = null)
public static function mb_strwidth($s, $encoding = null)
{
$encoding = self::getEncoding($encoding);
+ $wideChars = '/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u';
if ('UTF-8' !== $encoding) {
$s = self::iconv($encoding, 'UTF-8', $s);
+ } elseif (!preg_match('//u', $s)) {
+ // Each maximal subpart of an ill-formed sequence is one column wide
+ return preg_match_all(self::UTF8_CHAR_REGEX, $s) + preg_match_all($wideChars, self::scrubUtf8($s));
}
- $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
+ $s = preg_replace($wideChars, '', $s, -1, $wide);
return ($wide << 1) + iconv_strlen($s, 'UTF-8');
}
public static function mb_substr_count($haystack, $needle, $encoding = null)
{
- return substr_count($haystack, $needle);
+ if ('' === $needle = (string) $needle) {
+ if (80000 > \PHP_VERSION_ID) {
+ trigger_error('mb_substr_count(): Empty substring', \E_USER_WARNING);
+
+ return false;
+ }
+
+ throw new \ValueError('mb_substr_count(): Argument #2 ($needle) must not be empty');
+ }
+
+ if (!$search = self::prepareSearch((string) $haystack, $needle, self::getEncoding($encoding), false, true)) {
+ return 0;
+ }
+
+ return substr_count($search[0], $search[1]);
}
public static function mb_output_handler($contents, $status)
@@ -831,6 +867,10 @@ public static function mb_output_handler($contents, $status)
public static function mb_chr($code, $encoding = null)
{
+ if (null !== $encoding && !self::assertCodepointEncoding($encoding, 'mb_chr')) {
+ return false;
+ }
+
if (0x80 > $code %= 0x200000) {
$s = \chr($code);
} elseif (0x800 > $code) {
@@ -850,26 +890,64 @@ public static function mb_chr($code, $encoding = null)
public static function mb_ord($s, $encoding = null)
{
+ if ('' === (string) $s) {
+ if (80000 > \PHP_VERSION_ID) {
+ trigger_error('mb_ord(): Empty string', \E_USER_WARNING);
+
+ return false;
+ }
+
+ throw new \ValueError('mb_ord(): Argument #1 ($string) must not be empty');
+ }
+
+ if (null !== $encoding && !self::assertCodepointEncoding($encoding, 'mb_ord')) {
+ return false;
+ }
+
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
$s = mb_convert_encoding($s, 'UTF-8', $encoding);
}
- if (1 === \strlen($s)) {
- return \ord($s);
+ $s = unpack('C*', substr($s, 0, 4));
+ $code = $s[1] ?? 0;
+ if (0x80 > $code) {
+ return $code;
}
- $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
- if (0xF0 <= $code) {
- return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
+ if (0xC2 > $code || 0xF4 < $code) {
+ return false;
}
- if (0xE0 <= $code) {
- return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
+
+ $c2 = $s[2] ?? 0;
+ if (0x80 !== ($c2 & 0xC0)) {
+ return false;
}
- if (0xC0 <= $code) {
- return (($code - 0xC0) << 6) + $s[2] - 0x80;
+
+ if (0xE0 > $code) {
+ return (($code & 0x1F) << 6) | ($c2 & 0x3F);
}
- return $code;
+ $c3 = $s[3] ?? 0;
+ if (0x80 !== ($c3 & 0xC0)
+ || 0xE0 === $code && 0xA0 > $c2
+ || 0xED === $code && 0xA0 <= $c2
+ ) {
+ return false;
+ }
+
+ if (0xF0 > $code) {
+ return (($code & 0x0F) << 12) | (($c2 & 0x3F) << 6) | ($c3 & 0x3F);
+ }
+
+ $c4 = $s[4] ?? 0;
+ if (0x80 !== ($c4 & 0xC0)
+ || 0xF0 === $code && 0x90 > $c2
+ || 0xF4 === $code && 0x90 <= $c2
+ ) {
+ return false;
+ }
+
+ return (($code & 0x07) << 18) | (($c2 & 0x3F) << 12) | (($c3 & 0x3F) << 6) | ($c4 & 0x3F);
}
/** @return string|false */
@@ -893,7 +971,7 @@ public static function mb_str_pad(string $string, int $length, string $pad_strin
return false;
}
- if (self::mb_strlen($pad_string, $encoding) <= 0) {
+ if (0 >= $padStringLength = self::mb_strlen($pad_string, $encoding)) {
if (\PHP_VERSION_ID < 80000) {
trigger_error('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', \E_USER_WARNING);
@@ -921,15 +999,20 @@ public static function mb_str_pad(string $string, int $length, string $pad_strin
switch ($pad_type) {
case \STR_PAD_LEFT:
- return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string;
+ $leftPaddingLength = $paddingRequired;
+ break;
case \STR_PAD_RIGHT:
- return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding);
+ $leftPaddingLength = 0;
+ break;
default:
- $leftPaddingLength = floor($paddingRequired / 2);
- $rightPaddingLength = $paddingRequired - $leftPaddingLength;
-
- return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding);
+ $leftPaddingLength = intdiv($paddingRequired, 2);
}
+
+ $rightPaddingLength = $paddingRequired - $leftPaddingLength;
+
+ return str_repeat($pad_string, intdiv($leftPaddingLength, $padStringLength)).self::mb_substr($pad_string, 0, $leftPaddingLength % $padStringLength, $encoding)
+ .$string
+ .str_repeat($pad_string, intdiv($rightPaddingLength, $padStringLength)).self::mb_substr($pad_string, 0, $rightPaddingLength % $padStringLength, $encoding);
}
/** @return string|false */
@@ -980,16 +1063,288 @@ public static function mb_rtrim(string $string, ?string $characters = null, ?str
return self::mb_internal_trim('{[%s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
}
- private static function getSubpart($pos, $part, $haystack, $encoding)
+ /**
+ * Finds the position of $needle in $haystack like native mbstring.
+ *
+ * @return int|false
+ */
+ private static function find(string $function, $haystack, $needle, $offset, $encoding, bool $reverse, bool $fold)
+ {
+ $needle = (string) $needle;
+
+ if (80000 > \PHP_VERSION_ID && '' === $needle && 'mb_stripos' === $function) {
+ trigger_error($function.'(): Empty delimiter', \E_USER_WARNING);
+
+ return false;
+ }
+
+ if (!$search = self::prepareSearch((string) $haystack, $needle, self::searchEncoding($encoding, $fold), $fold, $fold)) {
+ return false;
+ }
+
+ [$haystack, $needle, $bytes, $dropped] = $search;
+
+ if (80000 > \PHP_VERSION_ID && $fold && ('' === $haystack || '' === $needle)) {
+ return false;
+ }
+
+ if (false === $start = self::utf8Offset($haystack, $offset = (int) $offset, $bytes)) {
+ // Native mbstring counts the ill-formed sequences that iconv() dropped: the offset could be in range
+ if ($dropped && abs($offset) <= self::utf8Length($haystack) + $dropped) {
+ return false;
+ }
+
+ if (80000 > \PHP_VERSION_ID) {
+ trigger_error($function.'(): '.($reverse ? 'Offset is greater than the length of haystack string' : 'Offset not contained in string'), \E_USER_WARNING);
+
+ return false;
+ }
+
+ throw new \ValueError($function.'(): Argument #3 ($offset) must be contained in argument #1 ($haystack)');
+ }
+
+ if (80000 > \PHP_VERSION_ID && '' === $needle) {
+ if ('mb_strpos' === $function) {
+ trigger_error($function.'(): Empty delimiter', \E_USER_WARNING);
+ }
+
+ return false;
+ }
+
+ if (!$reverse) {
+ $pos = strpos($haystack, $needle, $start);
+ } else {
+ $pos = strrpos($haystack, $needle, 0 > $offset ? $start - \strlen($haystack) : $start);
+ }
+
+ return false === $pos || $bytes ? $pos : self::utf8Length(substr($haystack, 0, $pos));
+ }
+
+ /**
+ * Returns the part of $haystack before or from $needle like native mbstring.
+ *
+ * @return string|false
+ */
+ private static function findPart(string $function, $haystack, $needle, $part, $encoding, bool $reverse, bool $fold)
+ {
+ $needle = (string) $needle;
+
+ if (80000 > \PHP_VERSION_ID && '' === $needle) {
+ if (!$reverse) {
+ trigger_error($function.'(): Empty delimiter', \E_USER_WARNING);
+ }
+
+ return false;
+ }
+
+ $haystack = (string) $haystack;
+
+ if (!$search = self::prepareSearch($haystack, $needle, $encoding = self::searchEncoding($encoding, $fold), $fold, $fold)) {
+ return false;
+ }
+
+ [$h, $n, $bytes] = $search;
+
+ if (false === $pos = $reverse ? strrpos($h, $n) : strpos($h, $n)) {
+ return false;
+ }
+
+ if ($bytes) {
+ return (string) ($part ? substr($haystack, 0, $pos) : substr($haystack, $pos));
+ }
+
+ if ($fold || ('UTF-8' === $encoding && (!preg_match('//u', $haystack) || !preg_match('//u', $needle)))) {
+ // Cut after as many characters as native mbstring counts before the match
+ $pos = self::utf8Length(substr($h, 0, $pos));
+ $h = 'UTF-8' === $encoding ? self::markIllFormedUtf8($haystack) : self::utf8($haystack, $encoding);
+ $pos = self::utf8Offset($h, $pos, false);
+ }
+
+ $h = (string) ($part ? substr($h, 0, $pos) : substr($h, $pos));
+
+ if (false !== strpos($h, "\xFF")) {
+ $h = str_replace("\xFF", 'none' === mb_substitute_character() ? '' : '?', $h);
+ }
+
+ return 'UTF-8' === $encoding ? $h : (string) @self::iconv('UTF-8', $encoding, $h);
+ }
+
+ /**
+ * @return array{string, string, bool, int}|null The haystack and the needle to search, whether offsets in them count bytes rather than UTF-8 characters, and how many bytes of ill-formed sequences iconv() dropped from the haystack
+ */
+ private static function prepareSearch(string $haystack, string $needle, string $encoding, bool $fold, bool $markIllFormed)
+ {
+ if ('ASCII' === $encoding || ('CP850' === $encoding && !$fold)) {
+ if ('ASCII' === $encoding) {
+ // Native mbstring turns every byte above 0x7F into the same error marker
+ $haystack = preg_replace('/[\x80-\xFF]/', "\xFF", $haystack);
+ $needle = preg_replace('/[\x80-\xFF]/', "\xFF", $needle);
+ }
+
+ if ($fold) {
+ $haystack = strtr($haystack, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
+ $needle = strtr($needle, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
+ }
+
+ return [$haystack, $needle, true, 0];
+ }
+
+ $dropped = 0;
+
+ if ('UTF-8' !== $encoding) {
+ if (false === ($haystack = self::utf8($haystack, $encoding, $dropped)) || false === $needle = self::utf8($needle, $encoding)) {
+ return null;
+ }
+ } elseif ($markIllFormed) {
+ $haystack = self::markIllFormedUtf8($haystack);
+ $needle = self::markIllFormedUtf8($needle);
+ }
+
+ return $fold ? [self::foldCase($haystack), self::foldCase($needle), false, $dropped] : [$haystack, $needle, false, $dropped];
+ }
+
+ private static function searchEncoding($encoding, bool $fold): string
+ {
+ $normalizedEncoding = self::getEncoding($encoding);
+
+ // Native mbstring folds the case of 8bit strings as Latin-1
+ return $fold && 'CP850' === $normalizedEncoding && null !== $encoding && 'CP850' !== strtoupper($encoding) ? 'ISO-8859-1' : $normalizedEncoding;
+ }
+
+ /**
+ * Converts $s to UTF-8 to search it, ending with "\xFF", a byte that is invalid in UTF-8, when its last sequence is truncated, like native mbstring.
+ *
+ * @param int $dropped Set to the number of bytes of the ill-formed sequences that iconv() dropped elsewhere
+ *
+ * @return string|false False when the encoding is unknown
+ */
+ private static function utf8(string $s, string $encoding, &$dropped = 0)
{
- if (false === $pos) {
+ if ('UTF-8' === $encoding || '' === $s) {
+ return $s;
+ }
+
+ if (false !== $u = @iconv($encoding, 'UTF-8', $s)) {
+ return $u;
+ }
+
+ // Let iconv() warn about an unknown encoding
+ if (false === iconv($encoding, 'UTF-8', '')) {
return false;
}
- if ($part) {
- return self::mb_substr($haystack, 0, $pos, $encoding);
+
+ // iconv() drops the ill-formed sequences, but fails on a truncated one at the end
+ for ($i = 0; $i < 4; ++$i) {
+ if (false !== $u = @self::iconv($encoding, 'UTF-8', $i ? substr($s, 0, -$i) : $s)) {
+ $dropped = \strlen($s) - $i - \strlen((string) @iconv('UTF-8', $encoding, $u));
+
+ return $i || '' === $u ? $u."\xFF" : $u;
+ }
+ }
+
+ $dropped = \strlen($s);
+
+ return "\xFF";
+ }
+
+ /**
+ * Replaces each maximal subpart of ill-formed UTF-8 sequences with "\xFF", like native mbstring before searching case-insensitively.
+ */
+ private static function markIllFormedUtf8(string $s): string
+ {
+ if (preg_match('//u', $s)) {
+ return $s;
+ }
+
+ return preg_replace('/\G(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*+\K(?:\xE0[\xA0-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]|\xED[\x80-\x9F]|\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|[\x80-\xFF])/', "\xFF", $s);
+ }
+
+ /**
+ * Folds the case of UTF-8 $s like the simple case folding of native mbstring.
+ */
+ private static function foldCase(string $s): string
+ {
+ static $map = null;
+
+ if (null === $map) {
+ $map = array_combine(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1]) + self::getData('lowerCase');
+ unset($map["\xC4\xB0"]); // U+0130 has no simple case folding
+ }
+
+ return strtr($s, $map);
+ }
+
+ /**
+ * Counts the characters of UTF-8 $s like native mbstring, even when ill-formed: one per byte that is not a continuation byte.
+ */
+ private static function utf8Length(string $s): int
+ {
+ return \strlen($s) - array_sum(\array_slice(count_chars($s), 0x80, 0x40));
+ }
+
+ /**
+ * @return int|false The offset in bytes of the character at $offset, false when it is out of range, like native mbstring
+ */
+ private static function utf8Offset(string $s, int $offset, bool $bytes)
+ {
+ if (0 === $offset) {
+ return 0;
+ }
+
+ if ($bytes || preg_match('//u', $u = str_replace("\xFF", '?', $s))) {
+ $length = $bytes ? \strlen($s) : self::utf8Length($s);
+
+ if (0 > $offset) {
+ $offset += $length;
+ }
+
+ if (0 > $offset || $offset > $length) {
+ return false;
+ }
+
+ if ($bytes) {
+ return $offset;
+ }
+
+ for ($rx = ''; 65535 < $offset; $offset -= 65535) {
+ $rx .= '.{65535}';
+ }
+ preg_match('/^'.$rx.'.{'.$offset.'}\K/su', $u, $m, \PREG_OFFSET_CAPTURE);
+
+ return $m[0][1];
}
- return self::mb_substr($haystack, $pos, null, $encoding);
+ // Walk ill-formed UTF-8 like native mbstring: backward over the bytes that are not continuation bytes, forward by the lengths that lead bytes announce
+ $length = \strlen($s);
+
+ if (0 > $offset) {
+ $i = $length;
+
+ while (0 > $offset) {
+ if (0 === $i) {
+ return false;
+ }
+
+ if (0x80 !== (\ord($s[--$i]) & 0xC0)) {
+ ++$offset;
+ }
+ }
+
+ return $i;
+ }
+
+ $i = 0;
+
+ while (0 < $offset--) {
+ if ($i >= $length) {
+ return false;
+ }
+
+ $c = \ord($s[$i]);
+ $i += $c < 0xC2 ? 1 : ($c < 0xE0 ? 2 : ($c < 0xF0 ? 3 : ($c < 0xF5 ? 4 : 1)));
+ }
+
+ return min($i, $length);
}
private static function html_encoding_callback(array $m)
@@ -1083,16 +1438,13 @@ private static function mb_internal_trim(string $regex, string $string, ?string
}
if ('' === $characters) {
- return null === $encoding ? $string : self::mb_convert_encoding($string, $encoding);
+ return $string;
}
- if ('UTF-8' === $encoding) {
+ if ('UTF-8' === self::getEncoding($encoding)) {
$encoding = null;
- if (!preg_match('//u', $string)) {
- $string = @self::iconv('UTF-8', 'UTF-8', $string);
- }
- if (null !== $characters && !preg_match('//u', $characters)) {
- $characters = @self::iconv('UTF-8', 'UTF-8', $characters);
+ if (!preg_match('//u', $string) || (null !== $characters && !preg_match('//u', $characters))) {
+ return self::mb_trim_invalid_utf8($string, $characters, $function);
}
} else {
$string = self::iconv($encoding, 'UTF-8', $string);
@@ -1117,6 +1469,139 @@ private static function mb_internal_trim(string $regex, string $string, ?string
return self::iconv('UTF-8', $encoding, $string);
}
+ /**
+ * Trims ill-formed UTF-8 like mbstring: each maximal subpart of an ill-formed
+ * sequence counts as one character, and the string is returned as is when
+ * nothing is trimmed, else re-encoded with the substitute character.
+ */
+ private static function mb_trim_invalid_utf8(string $string, ?string $characters, string $function): string
+ {
+ $regex = self::UTF8_CHAR_REGEX;
+
+ // Ill-formed sequences leave group 1 empty: they are trimmed when $characters has one too
+ preg_match_all($regex, $characters ?? "\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", $m);
+ $trimmed = array_flip($m[1]);
+
+ $start = 0;
+ $end = \strlen($string);
+
+ while ('mb_rtrim' !== $function && $start < $end && preg_match($regex, $string, $m, 0, $start) && isset($trimmed[$m[1] ?? ''])) {
+ $start += \strlen($m[0]);
+ }
+
+ while ('mb_ltrim' !== $function && $start < $end) {
+ // Characters are at most 4 bytes long, and only continuation bytes don't start one
+ for ($i = $end - 1; $i > $start && $i > $end - 4 && 0x80 === (\ord($string[$i]) & 0xC0); --$i) {
+ }
+
+ preg_match_all($regex, substr($string, $i, $end - $i), $m);
+
+ if (!isset($trimmed[end($m[1])])) {
+ break;
+ }
+
+ $end -= \strlen(end($m[0]));
+ }
+
+ if (0 === $start && \strlen($string) === $end) {
+ return $string;
+ }
+
+ $string = substr($string, $start, $end - $start);
+
+ return 'none' === mb_substitute_character() ? self::scrubUtf8($string) : mb_convert_encoding($string, 'UTF-8', 'UTF-8');
+ }
+
+ private static function scrubUtf8(string $s): string
+ {
+ return preg_replace(self::UTF8_CHAR_REGEX, '$1', $s);
+ }
+
+ /**
+ * @param true[] $referenceIds The ids of the references followed to reach $array
+ */
+ private static function convertArray(array $array, $toEncoding, $fromEncoding, array $referenceIds = []): array
+ {
+ $r = [];
+
+ foreach ($array as $key => $v) {
+ if (null !== $v && !\is_scalar($v) && !\is_array($v)) {
+ trigger_error('mb_convert_encoding(): Object is not supported', \E_USER_WARNING);
+ continue;
+ }
+
+ $k = \is_string($key) ? self::mb_convert_encoding($key, $toEncoding, $fromEncoding) : $key;
+
+ if (\is_string($v)) {
+ $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding);
+ } elseif (\is_array($v)) {
+ // Only a reference can close a cycle: following the same one twice means one was found
+ $id = 70400 <= \PHP_VERSION_ID && ($reference = \ReflectionReference::fromArrayElement($array, $key)) ? $reference->getId() : null;
+
+ if (null !== $id && isset($referenceIds[$id])) {
+ trigger_error('mb_convert_encoding(): Cannot convert recursively referenced values', \E_USER_WARNING);
+ $v = [];
+ } else {
+ $v = self::convertArray($v, $toEncoding, $fromEncoding, null === $id ? $referenceIds : $referenceIds + [$id => true]);
+ }
+ }
+
+ // Keys that collide once converted keep their first value
+ $r += [$k => $v];
+ }
+
+ return $r;
+ }
+
+ /**
+ * Returns the error native mbstring throws for these encoding arguments, if any.
+ */
+ private static function getConversionError(string $function, int $argument, $toEncoding, $fromEncoding): ?string
+ {
+ if (!self::isConvertibleEncoding((string) $toEncoding)) {
+ return \sprintf('%s(): Argument #%d ($to_encoding) must be a valid encoding, "%s" given', $function, $argument, $toEncoding);
+ }
+
+ if (null === $fromEncoding) {
+ return null;
+ }
+
+ if (!$list = \is_array($fromEncoding) ? $fromEncoding : ('' === $fromEncoding ? [] : explode(',', $fromEncoding))) {
+ return \sprintf('%s(): Argument #%d ($from_encoding) must specify at least one encoding', $function, 1 + $argument);
+ }
+
+ foreach ($list as $encoding) {
+ $encoding = \is_array($fromEncoding) ? (string) $encoding : trim($encoding, " \t");
+
+ // PHP 8.4 takes an empty entry of a comma-separated list for "auto"
+ if ('' === $encoding && !\is_array($fromEncoding) && 80400 <= \PHP_VERSION_ID) {
+ continue;
+ }
+
+ if (0 !== strcasecmp($encoding, 'auto') && !self::isConvertibleEncoding($encoding)) {
+ return \sprintf('%s(): Argument #%d ($from_encoding) contains invalid encoding "%s"', $function, 1 + $argument, $encoding);
+ }
+ }
+
+ return null;
+ }
+
+ private static function isConvertibleEncoding(string $encoding): bool
+ {
+ if (isset(self::$convertibleEncodings[$encoding])) {
+ return true;
+ }
+
+ $normalizedEncoding = self::getEncoding($encoding);
+
+ // Native encoding names use no other characters, while iconv() also accepts suffixes like //TRANSLIT
+ if ('' === $normalizedEncoding || preg_match('/[^\w#\-.:]/', $encoding) || (null === (self::UNSUPPORTED_CODEPOINT_ENCODINGS[$normalizedEncoding] ?? null) && false === @iconv($normalizedEncoding, $normalizedEncoding, ''))) {
+ return false;
+ }
+
+ return self::$convertibleEncodings[$encoding] = true;
+ }
+
private static function assertEncoding(string $encoding, string $errorFormat): bool
{
try {
@@ -1135,4 +1620,31 @@ private static function assertEncoding(string $encoding, string $errorFormat): b
return $validEncoding;
}
+
+ private static function assertCodepointEncoding(string $encoding, string $function): bool
+ {
+ if (null !== $name = self::UNSUPPORTED_CODEPOINT_ENCODINGS[strtoupper($encoding)] ?? null) {
+ if (80000 > \PHP_VERSION_ID) {
+ trigger_error(\sprintf('%s(): Unsupported encoding "%s"', $function, $name), \E_USER_WARNING);
+
+ return false;
+ }
+
+ throw new \ValueError(\sprintf('%s() does not support the "%s" encoding', $function, $name));
+ }
+
+ $normalizedEncoding = self::getEncoding($encoding);
+
+ if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, '')) {
+ return true;
+ }
+
+ if (80000 > \PHP_VERSION_ID) {
+ trigger_error(\sprintf('%s(): Unknown encoding "%s"', $function, $encoding), \E_USER_WARNING);
+
+ return false;
+ }
+
+ throw new \ValueError(\sprintf('%s(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $function, $encoding));
+ }
}
diff --git a/upload/api/vendor/symfony/type-info/Type.php b/upload/api/vendor/symfony/type-info/Type.php
index 43f1771c..175cd66a 100644
--- a/upload/api/vendor/symfony/type-info/Type.php
+++ b/upload/api/vendor/symfony/type-info/Type.php
@@ -84,7 +84,7 @@ public function accepts(mixed $value): bool
}
/**
- * Traverses the whole type tree.
+ * Traverses the type and its direct composed or wrapped parts.
*
* @return iterable
*/
diff --git a/upload/api/vendor/symfony/type-info/Type/ArrayShapeType.php b/upload/api/vendor/symfony/type-info/Type/ArrayShapeType.php
index a5277e2f..2a3d5eea 100644
--- a/upload/api/vendor/symfony/type-info/Type/ArrayShapeType.php
+++ b/upload/api/vendor/symfony/type-info/Type/ArrayShapeType.php
@@ -125,7 +125,7 @@ public function __toString(): string
$items = [];
foreach ($this->shape as $key => $value) {
- $itemKey = \is_int($key) ? (string) $key : \sprintf("'%s'", $key);
+ $itemKey = \is_int($key) ? (string) $key : \sprintf("'%s'", addcslashes($key, "'\\"));
if ($value['optional'] ?? false) {
$itemKey = \sprintf('%s?', $itemKey);
}
diff --git a/upload/api/vendor/symfony/type-info/Type/IntersectionType.php b/upload/api/vendor/symfony/type-info/Type/IntersectionType.php
index c8f011da..bb23c52f 100644
--- a/upload/api/vendor/symfony/type-info/Type/IntersectionType.php
+++ b/upload/api/vendor/symfony/type-info/Type/IntersectionType.php
@@ -75,6 +75,17 @@ public function composedTypesAreSatisfiedBy(callable $specification): bool
return true;
}
+ public function accepts(mixed $value): bool
+ {
+ foreach ($this->types as $type) {
+ if (!$type->accepts($value)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
public function __toString(): string
{
$string = '';
diff --git a/upload/api/vendor/symfony/type-info/Type/UnionType.php b/upload/api/vendor/symfony/type-info/Type/UnionType.php
index bf74c7e6..3a167f59 100644
--- a/upload/api/vendor/symfony/type-info/Type/UnionType.php
+++ b/upload/api/vendor/symfony/type-info/Type/UnionType.php
@@ -95,6 +95,17 @@ public function composedTypesAreSatisfiedBy(callable $specification): bool
return false;
}
+ public function accepts(mixed $value): bool
+ {
+ foreach ($this->types as $type) {
+ if ($type->accepts($value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public function __toString(): string
{
$string = '';
diff --git a/upload/api/vendor/symfony/type-info/TypeContext/TypeContext.php b/upload/api/vendor/symfony/type-info/TypeContext/TypeContext.php
index 9a044df0..e7018394 100644
--- a/upload/api/vendor/symfony/type-info/TypeContext/TypeContext.php
+++ b/upload/api/vendor/symfony/type-info/TypeContext/TypeContext.php
@@ -65,7 +65,7 @@ public function normalize(string $name): string
return \sprintf('%s\\%s', $this->uses[$firstNamePart], implode('\\', $nameParts));
}
- if (null !== $this->namespace) {
+ if ($this->namespace) {
return \sprintf('%s\\%s', $this->namespace, $name);
}
diff --git a/upload/api/vendor/symfony/type-info/TypeContext/TypeContextFactory.php b/upload/api/vendor/symfony/type-info/TypeContext/TypeContextFactory.php
index 070a1cf4..b66021a7 100644
--- a/upload/api/vendor/symfony/type-info/TypeContext/TypeContextFactory.php
+++ b/upload/api/vendor/symfony/type-info/TypeContext/TypeContextFactory.php
@@ -52,6 +52,11 @@ final class TypeContextFactory
*/
private array $typeContextCache = [];
+ /**
+ * @var array
+ */
+ private array $collectingTypeAliases = [];
+
private ?Lexer $phpstanLexer = null;
private ?PhpDocParser $phpstanParser = null;
@@ -290,7 +295,17 @@ private function collectTypeAliases(\ReflectionClass $reflection, TypeContext $t
throw new LogicException(\sprintf('Type alias "%s" is not imported from a valid class name.', $tag->value->importedAlias));
}
- $importedFromContext = $this->createFromClassName($importedFromType->getClassName());
+ if (isset($this->collectingTypeAliases[$importedFromType->getClassName()])) {
+ throw new LogicException(\sprintf('Cannot import "%s" type alias from "%s" as it is recursive.', $tag->value->importedAlias, $importedFromType->getClassName()));
+ }
+
+ $this->collectingTypeAliases[$typeContext->declaringClassName] = true;
+
+ try {
+ $importedFromContext = $this->createFromClassName($importedFromType->getClassName());
+ } finally {
+ unset($this->collectingTypeAliases[$typeContext->declaringClassName]);
+ }
$typeAlias = $importedFromContext->typeAliases[$tag->value->importedAlias] ?? null;
if (!$typeAlias) {
@@ -366,7 +381,7 @@ private function getPhpDocNode(string $rawDocNode): PhpDocNode
$this->phpstanParser ??= new PhpDocParser($config, new TypeParser($config, new ConstExprParser($config)), new ConstExprParser($config));
} else {
$this->phpstanLexer ??= new Lexer();
- $this->phpstanParser ??= new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
+ $this->phpstanParser ??= new PhpDocParser(new TypeParser($constExprParser = new ConstExprParser(true, true), true), $constExprParser);
}
return $this->phpstanParser->parse(new TokenIterator($this->phpstanLexer->tokenize($rawDocNode)));
diff --git a/upload/api/vendor/symfony/type-info/TypeFactoryTrait.php b/upload/api/vendor/symfony/type-info/TypeFactoryTrait.php
index d814497d..b3736ab8 100644
--- a/upload/api/vendor/symfony/type-info/TypeFactoryTrait.php
+++ b/upload/api/vendor/symfony/type-info/TypeFactoryTrait.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\TypeInfo;
+use Symfony\Component\TypeInfo\Exception\InvalidArgumentException;
use Symfony\Component\TypeInfo\Type\ArrayShapeType;
use Symfony\Component\TypeInfo\Type\BackedEnumType;
use Symfony\Component\TypeInfo\Type\BuiltinType;
@@ -324,6 +325,10 @@ public static function union(Type ...$types): UnionType
}
if (1 === \count($unionTypes)) {
+ if ($unionTypes[0] instanceof BuiltinType && $unionTypes[0]->getTypeIdentifier()->isStandalone()) {
+ throw new InvalidArgumentException(\sprintf('Cannot create union with "%s" standalone type.', $unionTypes[0]));
+ }
+
return self::nullable($unionTypes[0]);
}
diff --git a/upload/api/vendor/symfony/type-info/TypeResolver/PhpDocAwareReflectionTypeResolver.php b/upload/api/vendor/symfony/type-info/TypeResolver/PhpDocAwareReflectionTypeResolver.php
index 7d6562ea..47f4a507 100644
--- a/upload/api/vendor/symfony/type-info/TypeResolver/PhpDocAwareReflectionTypeResolver.php
+++ b/upload/api/vendor/symfony/type-info/TypeResolver/PhpDocAwareReflectionTypeResolver.php
@@ -52,7 +52,7 @@ public function __construct(
} else {
$this->lexer = $lexer ?? new Lexer();
$this->phpDocParser = $phpDocParser ?? new PhpDocParser(
- new TypeParser($constExprParser = new ConstExprParser()),
+ new TypeParser($constExprParser = new ConstExprParser(true, true), true),
$constExprParser,
);
}
diff --git a/upload/api/vendor/symfony/type-info/TypeResolver/StringTypeResolver.php b/upload/api/vendor/symfony/type-info/TypeResolver/StringTypeResolver.php
index 72dff850..105311d9 100644
--- a/upload/api/vendor/symfony/type-info/TypeResolver/StringTypeResolver.php
+++ b/upload/api/vendor/symfony/type-info/TypeResolver/StringTypeResolver.php
@@ -37,6 +37,7 @@
use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPStan\PhpDocParser\ParserConfig;
use Symfony\Component\TypeInfo\Exception\InvalidArgumentException;
+use Symfony\Component\TypeInfo\Exception\LogicException;
use Symfony\Component\TypeInfo\Exception\UnsupportedException;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\BackedEnumType;
@@ -60,6 +61,11 @@ final class StringTypeResolver implements TypeResolverInterface
*/
private static array $classExistCache = [];
+ /**
+ * @var array
+ */
+ private array $resolvingExtraTypeAliases = [];
+
private readonly Lexer $lexer;
private readonly TypeParser $parser;
@@ -76,7 +82,7 @@ public function __construct(
$this->parser = $parser ?? new TypeParser($config = new ParserConfig([]), new ConstExprParser($config));
} else {
$this->lexer = $lexer ?? new Lexer();
- $this->parser = $parser ?? new TypeParser(new ConstExprParser());
+ $this->parser = $parser ?? new TypeParser(new ConstExprParser(true, true), true);
}
}
@@ -113,10 +119,18 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Typ
if ($node instanceof ArrayShapeNode) {
$shape = [];
foreach ($node->items as $item) {
- $shape[(string) $item->keyName] = [
+ $shapeItem = [
'type' => $this->getTypeFromNode($item->valueType, $typeContext),
'optional' => $item->optional,
];
+
+ if (null === $item->keyName) {
+ $shape[] = $shapeItem;
+
+ continue;
+ }
+
+ $shape[$item->keyName instanceof ConstExprStringNode ? $item->keyName->value : (string) $item->keyName] = $shapeItem;
}
return Type::arrayShape(
@@ -141,7 +155,13 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Typ
if ($node instanceof ConstTypeNode) {
if ($node->constExpr instanceof ConstFetchNode) {
- $className = match (strtolower($node->constExpr->className)) {
+ $classKeyword = strtolower($node->constExpr->className);
+
+ if (null === $typeContext && \in_array($classKeyword, ['self', 'static', 'parent'], true)) {
+ throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "%s".', TypeContext::class, $classKeyword));
+ }
+
+ $className = match ($classKeyword) {
'self' => $typeContext->getDeclaringClass(),
'static' => $typeContext->getCalledClass(),
'parent' => $typeContext->getParentClass(),
@@ -169,17 +189,21 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Typ
}
}
+ if (!$types) {
+ throw new \DomainException(\sprintf('No "%s" constant found in "%s".', $node->constExpr->name, $className));
+ }
+
return CollectionType::mergeCollectionValueTypes($types);
}
- return match ($node->constExpr::class) {
- ConstExprArrayNode::class => Type::array(),
- ConstExprFalseNode::class => Type::false(),
- ConstExprFloatNode::class => Type::float(),
- ConstExprIntegerNode::class => Type::int(),
- ConstExprNullNode::class => Type::null(),
- ConstExprStringNode::class => Type::string(),
- ConstExprTrueNode::class => Type::true(),
+ return match (true) {
+ $node->constExpr instanceof ConstExprArrayNode => Type::array(),
+ $node->constExpr instanceof ConstExprFalseNode => Type::false(),
+ $node->constExpr instanceof ConstExprFloatNode => Type::float(),
+ $node->constExpr instanceof ConstExprIntegerNode => Type::int(),
+ $node->constExpr instanceof ConstExprNullNode => Type::null(),
+ $node->constExpr instanceof ConstExprStringNode => Type::string(),
+ $node->constExpr instanceof ConstExprTrueNode => Type::true(),
default => throw new \DomainException(\sprintf('Unhandled "%s" constant expression.', $node->constExpr::class)),
};
}
@@ -360,7 +384,17 @@ private function resolveCustomIdentifier(string $identifier, ?TypeContext $typeC
}
if (isset($this->extraTypeAliases[$identifier])) {
- return $this->resolve($this->extraTypeAliases[$identifier]);
+ if (isset($this->resolvingExtraTypeAliases[$identifier])) {
+ throw new LogicException(\sprintf('Cannot resolve "%s" type alias as it is recursive.', $identifier));
+ }
+
+ $this->resolvingExtraTypeAliases[$identifier] = true;
+
+ try {
+ return $this->resolve($this->extraTypeAliases[$identifier]);
+ } finally {
+ unset($this->resolvingExtraTypeAliases[$identifier]);
+ }
}
throw new \DomainException(\sprintf('Unhandled "%s" identifier.', $identifier));
diff --git a/upload/api/vendor/symfony/yaml/Inline.php b/upload/api/vendor/symfony/yaml/Inline.php
index 0c1d454e..3c24c20f 100644
--- a/upload/api/vendor/symfony/yaml/Inline.php
+++ b/upload/api/vendor/symfony/yaml/Inline.php
@@ -69,6 +69,16 @@ public static function parse(string $value, int $flags = 0, array &$references =
$i = 0;
$isQuoted = null;
$tag = self::parseTag($value, $i, $flags);
+
+ // a tag without value, possibly followed by a comment
+ if (null !== $tag && '' !== $tag && (!isset($value[$i]) || '#' === $value[$i])) {
+ if (str_starts_with($tag, 'php/')) {
+ throw new ParseException(\sprintf('Missing value for tag "%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
+ }
+
+ return new TaggedValue($tag, '');
+ }
+
switch ($value[$i]) {
case '[':
$result = self::parseSequence($state, $value, $flags, $i, $references);
@@ -402,6 +412,10 @@ private static function parseSequence(ParserState $state, string $sequence, int
continue;
}
+ if (!isset($sequence[$i])) {
+ break;
+ }
+
switch ($sequence[$i]) {
case '[':
// nested sequence
@@ -522,7 +536,7 @@ private static function parseMapping(ParserState $state, string $mapping, int $f
if (!$isKeyQuoted) {
$evaluatedKey = self::evaluateScalar($state, $key, $flags, $references);
- if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) {
+ if ('' !== $key && $evaluatedKey !== $key && (!\is_string($evaluatedKey) || '!' === $key[0]) && !\is_int($evaluatedKey)) {
throw new ParseException('Implicit casting of incompatible mapping keys to strings is not supported. Quote your evaluable mapping keys instead.', self::$parsedLineNumber + 1, $mapping);
}
}
@@ -561,6 +575,10 @@ private static function parseMapping(ParserState $state, string $mapping, int $f
continue 2;
}
+ if (!isset($mapping[$i])) {
+ break;
+ }
+
switch ($mapping[$i]) {
case '[':
// nested sequence
@@ -687,6 +705,9 @@ private static function evaluateScalar(ParserState $state, string $scalar, int $
return false;
case '!' === $scalar[0]:
switch (true) {
+ case '!!str' === $scalar:
+ case '!!binary' === $scalar:
+ return '';
case str_starts_with($scalar, '!!str '):
$s = substr($scalar, 6);
@@ -894,10 +915,6 @@ private static function parseTag(string $value, int &$i, int $flags): ?string
throw new ParseException(\sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}
- if ('' !== $tag && !isset($value[$i])) {
- throw new ParseException(\sprintf('Missing value for tag "%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
- }
-
if ('' === $tag || Yaml::PARSE_CUSTOM_TAGS & $flags) {
return $tag;
}
@@ -907,7 +924,11 @@ private static function parseTag(string $value, int &$i, int $flags): ?string
public static function evaluateBinaryScalar(string $scalar): string
{
- $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
+ $parsedBinaryData = preg_replace('/\s/', '', $scalar);
+
+ if ('' === $parsedBinaryData || '' === $parsedBinaryData = self::parseScalar($parsedBinaryData)) {
+ return '';
+ }
if (!\is_scalar($parsedBinaryData ?? '') && !$parsedBinaryData instanceof \Stringable) {
throw new ParseException(\sprintf('The "!!binary" tag only supports a base64 encoded string, got "%s".', get_debug_type($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
diff --git a/upload/api/vendor/symfony/yaml/Parser.php b/upload/api/vendor/symfony/yaml/Parser.php
index 948fd743..c35b1818 100644
--- a/upload/api/vendor/symfony/yaml/Parser.php
+++ b/upload/api/vendor/symfony/yaml/Parser.php
@@ -24,7 +24,7 @@
class Parser
{
public const TAG_PATTERN = '(?P![\w!.\/:-]+)';
- public const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?';
+ public const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P[ \t]+#.*)?';
public const REFERENCE_PATTERN = '#^&(?P[[^ ]++) *+(?P.*)#u';
public const DEFAULT_MAX_NESTING_LEVEL = 128;
public const DEFAULT_MAX_ALIASES_FOR_COLLECTIONS = 128;
@@ -129,7 +129,7 @@ private function doParse(string $value, int $flags): mixed
{
$this->currentLineNb = -1;
$this->currentLine = '';
- $value = $this->cleanup($value);
+ $value = $this->cleanup($value, $hasContentOnStartMarkerLine);
$this->lines = explode("\n", $value);
$this->numberOfParsedLines = \count($this->lines);
$this->locallySkippedLineNumbers = [];
@@ -150,8 +150,12 @@ private function doParse(string $value, int $flags): mixed
}
// Resolves the tag and returns if end of the document
- if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) {
- return new TaggedValue($tag, '');
+ if (null !== $tag = $this->getLineTag($this->currentLine, $flags, false)) {
+ do {
+ if (!$this->moveToNextLine()) {
+ return new TaggedValue($tag, '');
+ }
+ } while ($this->isCurrentLineEmpty());
}
do {
@@ -173,6 +177,14 @@ private function doParse(string $value, int $flags): mixed
}
$context = 'sequence';
+ if ($hasContentOnStartMarkerLine && 0 === $this->currentLineNb) {
+ throw new ParseException('A block collection cannot start on the line of the document start marker (---).', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ if (isset($values['value']) && '!' === $values['value'][0]) {
+ $values['value'] = $this->moveAnchorBeforeTag($values['value']);
+ }
+
if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) {
$isRef = $matches['ref'];
$this->refsBeingParsed[] = $isRef;
@@ -190,9 +202,14 @@ private function doParse(string $value, int $flags): mixed
$sequenceIndentation = \strlen($values['leadspaces']) + 1;
$sequenceYaml = substr($this->currentLine, $sequenceIndentation);
- $sequenceYaml .= "\n".$this->getNextEmbedBlock($sequenceIndentation, true);
+ $embedBlock = $this->getNextEmbedBlock($sequenceIndentation, true);
+
+ // keep trailing empty lines, they can be part of a block scalar
+ if ($currentLineNumber !== $this->getRealCurrentLineNb()) {
+ $sequenceYaml .= "\n".$embedBlock;
+ }
- $data[] = $this->parseBlock($currentLineNumber, rtrim($sequenceYaml), $flags);
+ $data[] = $this->parseBlock($currentLineNumber, $sequenceYaml, $flags);
} elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || str_starts_with(ltrim($values['value'], ' '), '#')) {
$data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true) ?? '', $flags);
} elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) {
@@ -200,7 +217,7 @@ private function doParse(string $value, int $flags): mixed
$subTag,
$this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags)
);
- } elseif (self::preg_match('/^'.self::TAG_PATTERN.' +'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $values['value'])) {
+ } elseif (self::preg_match('/^'.self::TAG_PATTERN.'[ \t]+'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $values['value'])) {
$data[] = $this->parseValue($values['value'], $flags, $context);
} else {
if (
@@ -210,12 +227,14 @@ private function doParse(string $value, int $flags): mixed
|| self::preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $this->trimTag($values['value']), $matches)
)
) {
+ $currentLineNumber = $this->getRealCurrentLineNb();
+
$block = $values['value'];
- if ($this->isNextLineIndented() || isset($matches['value']) && '>-' === $matches['value']) {
+ if ($this->isNextLineIndented() || $this->isNextLineInBlockScalar($block)) {
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1);
}
- $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
+ $data[] = $this->parseBlock($currentLineNumber, $block, $flags);
} else {
$data[] = $this->parseValue($values['value'], $flags, $context);
}
@@ -225,7 +244,7 @@ private function doParse(string $value, int $flags): mixed
array_pop($this->refsBeingParsed);
}
} elseif (
- self::preg_match('#^(?P(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{!].*?)) *\:(( |\t)++(?P.+))?$#u', rtrim($this->currentLine), $values)
+ self::preg_match('#^(?P(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{!].*?)|![^\s]++(?= +\:)) *\:(( |\t)++(?P.+))?$#u', rtrim($this->currentLine), $values)
&& (!str_contains($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"], true))
) {
if ($context && 'sequence' == $context) {
@@ -233,6 +252,14 @@ private function doParse(string $value, int $flags): mixed
}
$context = 'mapping';
+ if ($hasContentOnStartMarkerLine && 0 === $this->currentLineNb) {
+ throw new ParseException('A block collection cannot start on the line of the document start marker (---).', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+ }
+
+ if (isset($values['value'][0]) && '!' === $values['value'][0]) {
+ $values['value'] = $this->moveAnchorBeforeTag($values['value']);
+ }
+
try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
@@ -284,12 +311,14 @@ private function doParse(string $value, int $flags): mixed
$data += $refValue; // array union
} else {
+ $currentLineNumber = $this->getRealCurrentLineNb();
+
if (isset($values['value']) && '' !== $values['value']) {
$value = $values['value'];
} else {
$value = $this->getNextEmbedBlock();
}
- $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
+ $parsed = $this->parseBlock($currentLineNumber + 1, $value, $flags);
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) {
$parsed = (array) $parsed;
@@ -450,11 +479,6 @@ private function doParse(string $value, int $flags): mixed
throw $e;
}
} else {
- // multiple documents are not supported
- if ('---' === $this->currentLine) {
- throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename);
- }
-
if (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1]) {
throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
@@ -773,17 +797,18 @@ private function parseValue(string $value, int $flags, string $context): mixed
return $this->refs[$value];
}
- if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
+ if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.'[ \t]+)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
$modifiers = $matches['modifiers'] ?? '';
$data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers));
if ('' !== $matches['tag'] && '!' !== $matches['tag']) {
- if ('!!binary' === $matches['tag']) {
- return Inline::evaluateBinaryScalar($data);
- }
-
- return new TaggedValue(substr($matches['tag'], 1), $data);
+ return match ($matches['tag']) {
+ '!!binary' => Inline::evaluateBinaryScalar($data),
+ '!!str' => $data,
+ '!!float' => (float) $data,
+ default => new TaggedValue(substr($matches['tag'], 1), $data),
+ };
}
return $data;
@@ -880,9 +905,12 @@ private function parseBlockScalar(string $style, string $chomping = '', int $ind
$isCurrentLineBlank = $this->isCurrentLineBlank();
$blockLines = [];
+ $longestBlankLine = 0;
// leading blank lines are consumed before determining indentation
- while ($notEOF && $isCurrentLineBlank) {
+ while ($notEOF && $isCurrentLineBlank && (!$indentation || \strlen($this->currentLine) <= $indentation)) {
+ $longestBlankLine = max($longestBlankLine, \strlen($this->currentLine));
+
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$blockLines[] = '';
@@ -897,32 +925,44 @@ private function parseBlockScalar(string $style, string $chomping = '', int $ind
for ($i = 0; $i < $currentLineLength && ' ' === $this->currentLine[$i]; ++$i) {
++$indentation;
}
+
+ // a comment less indented than the leading empty lines is not content, the indentation is then the one of the longest empty line
+ if ($indentation < $longestBlankLine && $this->isCurrentLineComment()) {
+ $indentation = $longestBlankLine;
+ }
}
- if ($indentation > 0) {
- $pattern = \sprintf('/^ {%d}(.*)$/', $indentation);
+ if (!$notEOF || !$indentation || strspn($this->currentLine, ' ') < $indentation) {
+ if ($notEOF) {
+ $this->moveToPreviousLine();
+ } elseif (!$this->isCurrentLineLastLineInDocument()) {
+ $blockLines[] = '';
+ }
- while (
- $notEOF && (
- $isCurrentLineBlank
- || self::preg_match($pattern, $this->currentLine, $matches)
- )
- ) {
- if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) {
- $blockLines[] = substr($this->currentLine, $indentation);
- } elseif ($isCurrentLineBlank) {
- $blockLines[] = '';
- } else {
- $blockLines[] = $matches[1];
- }
+ // without content, the line breaks of the empty lines are only kept with the "+" chomping indicator
+ return '+' === $chomping ? str_repeat("\n", \count($blockLines)) : '';
+ }
- // newline only if not EOF
- if ($notEOF = $this->moveToNextLine()) {
- $isCurrentLineBlank = $this->isCurrentLineBlank();
- }
+ $pattern = \sprintf('/^ {%d}(.*)$/', $indentation);
+
+ while (
+ $notEOF && (
+ $isCurrentLineBlank
+ || self::preg_match($pattern, $this->currentLine, $matches)
+ )
+ ) {
+ if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) {
+ $blockLines[] = substr($this->currentLine, $indentation);
+ } elseif ($isCurrentLineBlank) {
+ $blockLines[] = '';
+ } else {
+ $blockLines[] = $matches[1];
+ }
+
+ // newline only if not EOF
+ if ($notEOF = $this->moveToNextLine()) {
+ $isCurrentLineBlank = $this->isCurrentLineBlank();
}
- } elseif ($notEOF) {
- $blockLines[] = '';
}
if ($notEOF) {
@@ -935,16 +975,15 @@ private function parseBlockScalar(string $style, string $chomping = '', int $ind
// folded style
if ('>' === $style) {
$text = '';
- $previousLineIndented = false;
+ $previousLineIndented = null;
$previousLineBlank = false;
for ($i = 0, $blockLinesCount = \count($blockLines); $i < $blockLinesCount; ++$i) {
if ('' === $blockLines[$i]) {
$text .= "\n";
- $previousLineIndented = false;
$previousLineBlank = true;
- } elseif (' ' === $blockLines[$i][0]) {
- $text .= "\n".$blockLines[$i];
+ } elseif (' ' === $blockLines[$i][0] || "\t" === $blockLines[$i][0]) {
+ $text .= (null === $previousLineIndented ? '' : "\n").$blockLines[$i];
$previousLineIndented = true;
$previousLineBlank = false;
} elseif ($previousLineIndented) {
@@ -1031,37 +1070,81 @@ private function isCurrentLineLastLineInDocument(): bool
return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1);
}
- private function cleanup(string $value): string
+ private function cleanup(string $value, ?bool &$hasContentOnStartMarkerLine = null): string
{
+ $hasContentOnStartMarkerLine = false;
+
$value = str_replace(["\r\n", "\r"], "\n", $value);
- // strip YAML header
- $count = 0;
- $value = preg_replace('#^%YAML[: ][\d.]++[^\n]*+\n#u', '', $value, -1, $count);
- $this->offset += $count;
-
- // remove leading comments
- $trimmedValue = preg_replace('#^(?:\#[^\n]*+\n)++#', '', $value, -1, $count);
- if (1 === $count) {
- // items have been removed, update the offset
- $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
- $value = $trimmedValue;
+ // remove the YAML header, and the comments and empty lines before the content
+ $value = $this->removeLeadingLines('#^(?:[ \t]*+(?:\#[^\n]*+)?+\n)*+(?:%YAML[: ][\d.]++[^\n]*+\n(?:[ \t]*+(?:\#[^\n]*+)?+\n)*+)?+#', $value);
+
+ // document markers and the properties of the root node are only found at the top level, not in nested blocks
+ if (null !== $this->totalNumberOfLines) {
+ return $value;
}
- // remove start of the document marker (---)
- $trimmedValue = preg_replace('#^---[^\n]*+\n#', '', $value, -1, $count);
- if (1 === $count) {
- // items have been removed, update the offset
- $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
- $value = $trimmedValue;
+ // remove start of the document marker (---), with the YAML 1.0 directives, or the node properties and block scalar header that can follow it alone, as they were always ignored
+ $value = preg_replace('#^---(?:[ \t]++(?:%[^\n]*+|(?:(?:[!&][^ \t\n]*+|[|>][-+0-9]*+)[ \t]*+)++(?=(?:\#[^\n]*+)?+(?:\n|\z)))?+|(?=\n)|\z)#', '', $value, -1, $count);
+
+ // remove the anchor of the root node, as nothing can refer to it
+ $value = preg_replace('#^((?:![^! \t\n][^ \t\n]*+[ \t]++)?+)&[^ \t\n]++(?:[ \t]++|(?=\n)|\z)#', '$1', $value);
- // remove end of the document marker (...)
- $value = preg_replace('#\.\.\.\s*+$#', '', $value);
+ // remove the lines left without content
+ $offset = $this->offset;
+ $value = $this->removeLeadingLines('#^(?:[ \t]*+(?:\#[^\n]*+)?+\n)++#', $value);
+ $hasContentOnStartMarkerLine = 1 === $count && $offset === $this->offset;
+
+ // remove end of the document marker (...), which can only be followed by comments
+ $value = preg_replace('#^\.\.\.(?:[ \t]++(?:\#[^\n]*+)?+)?+(?:\n[ \t]*+(?:\#[^\n]*+)?+)*+\z#m', '', $value);
+
+ if (self::preg_match('#^(?:---|\.\.\.)(?:[ \t][^\n]*+)?+$#m', $value, $matches, \PREG_OFFSET_CAPTURE)) {
+ throw new ParseException('Multiple documents are not supported.', $this->offset + substr_count($value, "\n", 0, $matches[0][1]) + 1, $matches[0][0], $this->filename);
}
return $value;
}
+ /**
+ * Tells whether the next line belongs to a block scalar whose header ends the given value.
+ *
+ * Unlike isNextLineIndented(), empty and comment-like lines are not skipped, as they can be part of the block scalar.
+ */
+ private function isNextLineInBlockScalar(string $value): bool
+ {
+ if (!isset($this->lines[$this->currentLineNb + 1]) || !self::preg_match('/:[ \t]+(?:'.self::TAG_PATTERN.'[ \t]+)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value)) {
+ return false;
+ }
+
+ $nextLine = $this->lines[$this->currentLineNb + 1];
+
+ return '' === trim($nextLine, ' ') || strspn($nextLine, ' ') > $this->getCurrentLineIndentation();
+ }
+
+ /**
+ * Turns "!tag &anchor value" into "&anchor !tag value", as both orders are allowed.
+ *
+ * Built-in tags are left alone: "!!str &foo" is the "&foo" string, as in flow collections.
+ */
+ private function moveAnchorBeforeTag(string $value): string
+ {
+ if (str_starts_with($value, '!!')) {
+ return $value;
+ }
+
+ return preg_replace('#^'.self::TAG_PATTERN.' ++(&[^ ]++)#u', '$2 $1', $value);
+ }
+
+ private function removeLeadingLines(string $pattern, string $value): string
+ {
+ $trimmedValue = preg_replace($pattern, '', $value);
+
+ // items have been removed, update the offset
+ $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
+
+ return $trimmedValue;
+ }
+
private function isNextLineUnIndentedCollection(): bool
{
$currentIndentation = $this->getCurrentLineIndentation();
@@ -1075,11 +1158,7 @@ private function isNextLineUnIndentedCollection(): bool
}
} while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));
- if ($EOF) {
- return false;
- }
-
- $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();
+ $ret = !$EOF && $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();
for ($i = 0; $i < $movements; ++$i) {
$this->moveToPreviousLine();
@@ -1134,7 +1213,8 @@ private function getLineTag(string $value, int $flags, bool $nextLineCheck = tru
return null;
}
- if ($nextLineCheck && !$this->isNextLineIndented()) {
+ // unlike a sequence item, the value of a mapping key can also be a sequence that is not indented
+ if ($nextLineCheck && !$this->isNextLineIndented() && ($this->isStringUnIndentedCollectionItem() || !$this->isNextLineUnIndentedCollection())) {
return null;
}
@@ -1142,6 +1222,11 @@ private function getLineTag(string $value, int $flags, bool $nextLineCheck = tru
// Built-in tags
if ($tag && '!' === $tag[0]) {
+ // "!!str" and "!!binary" can be resolved as inline scalars when they don't apply to a nested block
+ if (!$nextLineCheck && \in_array($tag, ['!str', '!binary'], true) && !$this->isNextLineIndented()) {
+ return null;
+ }
+
throw new ParseException(\sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}
diff --git a/upload/api/vendor/zircote/swagger-php/CONTRIBUTING.md b/upload/api/vendor/zircote/swagger-php/CONTRIBUTING.md
index e5c79c69..8ce38a40 100644
--- a/upload/api/vendor/zircote/swagger-php/CONTRIBUTING.md
+++ b/upload/api/vendor/zircote/swagger-php/CONTRIBUTING.md
@@ -61,6 +61,43 @@ Commit subjects follow the same `type(Scope): subject` shape as the title, with
of `feat`, `fix`, `docs`, `test`, `chore` or `refactor`. A commit message body documents
what the diff does; the reasoning belongs in the pull request description.
+## Versioning
+
+swagger-php follows [semantic versioning](https://semver.org). What a change earns:
+
+**Patch** — a fix that changes generated output only where the old output was wrong, an
+internal refactor, a documentation or CI change. Anything a consumer can take without
+reading the release notes.
+
+**Minor** — new public API, a new annotation or attribute, a new field on an existing one,
+and **any widening of a `require` constraint**. A dependency change is a minor even when it
+only widens: a patch is expected to be a drop-in with no effect on resolution, and widening
+lets consumers install combinations that were impossible before.
+
+**Major** — removing or re-signing public API, narrowing a `require` constraint, raising the
+PHP floor, or changing generated output that a correct document could have depended on.
+
+**The commit type does not decide this.** #2196 carried no `feat` — its subject is *"Drop
+symfony/console 7.4 requirement"*, which reads like a fix — but it is a minor, because
+applications on Symfony 6.4 that could not install the library at all now can. Read the
+effect on a consumer, not the prefix. The reverse holds too: a `feat` that only adds a test
+helper changes nothing for anyone and is not a minor on its own.
+
+**Generated output is the case that needs care**, because most fixes here change it. Correcting
+output that was invalid, or that no valid document could have relied on, is a patch — that is
+the bug being fixed. Changing output that was already valid is at least a minor, even when the
+new output is better, because a consumer's committed spec file or downstream generator will
+see a diff it did not ask for.
+
+**Public API is every class under `src/` that a consumer can reach**, which today means all of
+them — nothing is marked `@internal`. In practice the classes people build on are `Generator`,
+the analysers and processors, the annotations and attributes, and the `Spec` classes; the
+`Console` classes exist for `bin/openapi` and are reached through the command line rather than
+extended. That is a description of how things are used, not a promise, so when a change touches
+a class outside that list, say so in the pull request rather than assuming nobody is on it.
+
+If a release mixes levels, the highest one wins.
+
## Documentation
The documentation website is build from the [docs](docs/) folder with [vitepress](https://vitepress.vuejs.org).
diff --git a/upload/api/vendor/zircote/swagger-php/README.md b/upload/api/vendor/zircote/swagger-php/README.md
index 7e94e062..a9d8cb36 100644
--- a/upload/api/vendor/zircote/swagger-php/README.md
+++ b/upload/api/vendor/zircote/swagger-php/README.md
@@ -20,7 +20,7 @@ attributes and annotations.
- Extracts information from code and existing phpdoc comments.
- Can be used programmatically or via command-line tool.
- Error reporting (with hints, context).
-- 🧪 **Spec attributes pipeline (beta)** — a new processing mode with typed DTOs, grouped augmenters, and version-aware compilers.
+- 🧪 **Spec attributes (beta)** — attributes you can also build by hand and compile without scanning, and a pipeline that tells you when an attribute is wrong; see [Why spec attributes?](https://zircote.github.io/swagger-php/guide/why-spec).
## OpenAPI version support
diff --git a/upload/api/vendor/zircote/swagger-php/ROADMAP.md b/upload/api/vendor/zircote/swagger-php/ROADMAP.md
index 84f5de06..0b2f0f65 100644
--- a/upload/api/vendor/zircote/swagger-php/ROADMAP.md
+++ b/upload/api/vendor/zircote/swagger-php/ROADMAP.md
@@ -1,23 +1,85 @@
# Roadmap
## Overview
-This is a high level roadmap predominantly for **v6**, **v7** and **v8**.
+This is a high level roadmap for **v6**, **v7** and **v8** - what happens to the classic pipeline
+and the new _Spec_ attributes and processing pipeline, and when.
-In particular this is in relation to the introduction of the new _Spec_ attributes and processing pipeline and what may/will happen when.
+The discussion and the tick list of what has shipped are in
+[#1953](https://github.com/zircote/swagger-php/issues/1953). This file is the plan; the issue
+tracks it.
+
+## Deprecation rule
+A `@deprecated` marker names the version that removes the thing:
+
+```php
+/** @deprecated since 6.11, removed in 8.0 - use ... instead */
+```
+
+Nothing is removed before the version its marker names, and nothing is removed without a marker.
+Something marked for `8.0` keeps working through **v7** unchanged. That is what "no cliff" means
+here: the marker is the promise.
## Timeline
### v6
-`6.5.0` saw the introduction of the new `Spec` system. Right now this is considered not yet production ready.
-The remainder of **v6** will be defined by improving and completing the new system.
+`6.5.0` introduced the `Spec` system as opt-in beta. The remainder of **v6** is about completing
+it and getting people onto it:
+
+* `Spec` attributes declared frozen, and the **beta** label dropped in that minor
+* a rector rule set for `OpenApi\Attributes` -> `OpenApi\Spec`
+* a seam to contribute pre-built attributes to a `Specification` before resolution, for
+ framework integrations that do not scan
+* docblock annotation support marked `@deprecated`, removed in `8.0` - the README has said so
+ since 4.8, the code now agrees, and parsing a docblock annotation triggers a runtime
+ deprecation once per run
+* the relocation shims already marked (`Pipeline`, `SourceFinder`, `Analysers\TokenScanner`,
+ `Utils\TypeMapper`, `LegacyTypeResolver`) are removed in `7.0`
+* `Generator::UNDEFINED` and `Generator::isDefault()` stay until `8.0` - every classic custom
+ processor uses them, so they go with classic and not before
+
+Classic attributes, the `Generator` and the processors are **not** marked in v6. The replacement
+is still beta for part of the major, and a stable API is not deprecated in favour of a beta one.
### v7
-This is where things will start to turn:
-* Default mode will switch to `hybrid` - this means existing `classic` projects should keep working via the bridge (probably with the exception of `NelmioApiDocBundle`, as that relies on a lot of actual `classic` features), although they will be routed through the new `spec` pipeline via a custom bridge.
-* All classic code - annotations + attributes and related pipeline code will be marked `deprecated`
-* All code marked `depecated` in **v6** will be removed
-* Bridge is marked `deprecated`
-* `Builder::setMode()` is marked `deprecated`
-* `nikic/php-parser` is raised to `^5.0` — the `^4.19` branch parses no further than
+This is where things turn:
+* default mode switches to `hybrid` - existing `classic` projects keep working, routed through
+ the new pipeline via the bridge, with the exception of integrations that build annotation
+ objects directly instead of scanning for them, which the bridge has nothing to scan for
+* all classic code - annotations, attributes and the related pipeline code - is marked
+ `@deprecated`, removed in `8.0`
+* the bridge and mode selection are marked `@deprecated`, removed in `8.0` - that is
+ `Builder::setMode()`, the `Builder\Mode` enum, `Console\GenerateInput::$mode` and the CLI's
+ `-m`/`--mode` option, which go together because there is one pipeline in `8.0` and nothing
+ left to select. These markers name no replacement, unlike every other marker here
+* everything whose marker says `7.0` is removed
+* `nikic/php-parser` is raised to `^5.0` - the `^4.19` branch parses no further than
PHP 8.3 syntax
-## v8
-`classic` is removed from the codebase, leaving only the spec pipeline. Annotations are no longer supported at all.
+* the v7 migration guide states the v8 namespace move below, so nobody meets it as a surprise
+
+Open: whether the default **output** version follows the default mode. `classic` defaults to
+OpenAPI 3.0.0 and `hybrid` to 3.1.0 today, so switching the default mode also switches the
+default output version for everyone unless v7 pins 3.0.0. Decided in #1953 before v7.
+
+### v8
+`classic` is removed from the codebase, leaving only the spec pipeline. Annotations are no longer
+supported at all.
+
+The spec attributes move from `OpenApi\Spec` to `OpenApi\Attributes`. With one pipeline left,
+"Spec" is a mode name that means nothing, and `OpenApi\Attributes` is the better home. For code
+written against `OpenApi\Spec` that is one `use` line per file, and the rector set does it.
+
+Open: whether `OpenApi\Spec` stays as deprecated aliases for one major, so the move is optional
+until v9.
+
+## Finding what to remove
+Code carries `@deprecated` with the version that removes it, which is the primary marker and
+the one tooling understands. Documentation, fixture layout and test-matrix structure cannot
+carry it - nothing consumes a docblock in a markdown file or a yaml expectation - so those are
+marked inline with the mode they exist to serve: `[classic]`, `[hybrid]`, or `[classic/hybrid]`
+for what serves both. `grep -rnE '\[(classic|hybrid)'` lists them, and all of it goes when
+`classic` does.
+
+The marker names the mode rather than the release on purpose. A thing is `[hybrid]` for as
+long as hybrid exists, which stays true whatever happens to this timeline; `[v8: drop]` would
+be a claim about a schedule, and would need revisiting if the schedule moved. The two markers
+do not overlap with `@deprecated`: if a thing can carry a docblock, it gets `@deprecated` and
+not this.
diff --git a/upload/api/vendor/zircote/swagger-php/bin/openapi b/upload/api/vendor/zircote/swagger-php/bin/openapi
index 26a5a43f..3d0f5797 100755
--- a/upload/api/vendor/zircote/swagger-php/bin/openapi
+++ b/upload/api/vendor/zircote/swagger-php/bin/openapi
@@ -41,6 +41,6 @@ $options = $definition->getOptions();
unset($options['version'], $options['no-interaction']);
$definition->setOptions($options);
-$app->addCommand(new GenerateCommand($logger));
+$app->addCommands([new GenerateCommand($logger)]);
$app->setDefaultCommand('openapi', true);
$app->run($input, $output);
diff --git a/upload/api/vendor/zircote/swagger-php/composer.json b/upload/api/vendor/zircote/swagger-php/composer.json
index 68e6332e..aa6d5c6f 100644
--- a/upload/api/vendor/zircote/swagger-php/composer.json
+++ b/upload/api/vendor/zircote/swagger-php/composer.json
@@ -49,7 +49,7 @@
"phpstan/phpdoc-parser": "^2.0",
"psr/log": "^1.1 || ^2.0 || ^3.0",
"radebatz/type-info-extras": "^1.0.2",
- "symfony/console": "^7.4 || ^8.0",
+ "symfony/console": "^6.4 || ^7.0 || ^8.0",
"symfony/deprecation-contracts": "^2 || ^3",
"symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0"
diff --git a/upload/api/vendor/zircote/swagger-php/package-lock.json b/upload/api/vendor/zircote/swagger-php/package-lock.json
index 4ed46815..98470f89 100644
--- a/upload/api/vendor/zircote/swagger-php/package-lock.json
+++ b/upload/api/vendor/zircote/swagger-php/package-lock.json
@@ -9,13 +9,13 @@
"version": "1.0.0",
"license": "Apache-2.0",
"devDependencies": {
- "@redocly/cli": "^2.37.0"
+ "@redocly/cli": "^2.53.3"
}
},
"node_modules/@redocly/cli": {
- "version": "2.37.0",
- "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.37.0.tgz",
- "integrity": "sha512-2xKvVy2re6xiWfrWHJj633oEoVAYHisPsxEwoxtSxMqB+RhW2uqM98+JPjrLQPpj2m1bXrj08KXwpSFfojV0/g==",
+ "version": "2.53.3",
+ "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.53.3.tgz",
+ "integrity": "sha512-hzNAWzHCOZ05vwRx0ehTxNeJaxxizjGV505eKtfs9MR/8ieD/8lYhAK4GF5FaqRRwOEpLO0PvzQwBSKYXmxhRA==",
"dev": true,
"license": "MIT",
"bin": {
diff --git a/upload/api/vendor/zircote/swagger-php/package.json b/upload/api/vendor/zircote/swagger-php/package.json
index b2796972..74adbc39 100644
--- a/upload/api/vendor/zircote/swagger-php/package.json
+++ b/upload/api/vendor/zircote/swagger-php/package.json
@@ -6,6 +6,6 @@
"redocly": "redocly"
},
"devDependencies": {
- "@redocly/cli": "^2.37.0"
+ "@redocly/cli": "^2.53.3"
}
}
diff --git a/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockAnnotationFactory.php b/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockAnnotationFactory.php
index a5b17765..347413f5 100644
--- a/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockAnnotationFactory.php
+++ b/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockAnnotationFactory.php
@@ -11,12 +11,20 @@
use OpenApi\Generator;
use OpenApi\GeneratorAwareTrait;
+/**
+ * Builds annotations from docblock comments.
+ *
+ * @deprecated since 6.11, removed in 8.0 - use attributes instead
+ */
class DocBlockAnnotationFactory implements AnnotationFactoryInterface
{
use GeneratorAwareTrait;
protected ?DocBlockParser $docBlockParser = null;
+ /** Reset per generator run, so a scan of a thousand annotated files reports once. */
+ protected bool $deprecationReported = false;
+
public function __construct(?DocBlockParser $docBlockParser = null)
{
$this->docBlockParser = $docBlockParser ?: new DocBlockParser();
@@ -30,6 +38,7 @@ public function isSupported(): bool
public function setGenerator(Generator $generator): static
{
$this->generator = $generator;
+ $this->deprecationReported = false;
$this->docBlockParser->setAliases($generator->getAliases());
@@ -60,6 +69,7 @@ public function build(\Reflector $reflector, Context $context): array
$annotations = [];
foreach ($this->docBlockParser->fromComment($comment, $context) as $instance) {
if ($instance instanceof OA\AbstractAnnotation) {
+ $this->reportDeprecation();
$annotations[] = $instance;
} else {
if ($context->is('other') === false) {
@@ -74,4 +84,18 @@ public function build(\Reflector $reflector, Context $context): array
return [];
}
+
+ /**
+ * Reported on the first docblock annotation actually parsed, not on the first docblock
+ * seen: a project that has migrated to attributes still has docblocks everywhere.
+ */
+ protected function reportDeprecation(): void
+ {
+ if ($this->deprecationReported) {
+ return;
+ }
+
+ $this->deprecationReported = true;
+ trigger_deprecation('zircote/swagger-php', '6.11', 'Docblock annotations are deprecated and will be removed in 8.0; use attributes instead');
+ }
}
diff --git a/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockParser.php b/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockParser.php
index ae34859c..5b84cacb 100644
--- a/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockParser.php
+++ b/upload/api/vendor/zircote/swagger-php/src/Analysers/DocBlockParser.php
@@ -14,19 +14,24 @@
/**
* Extract swagger-php annotations from a [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) using Doctrine's DocParser.
*/
+/**
+ * @deprecated since 6.11, removed in 8.0 - use attributes instead
+ */
class DocBlockParser
{
protected DocParser $docParser;
/**
- * @param array $aliases
+ * @param array $aliases
*/
public function __construct(array $aliases = [])
{
if (DocBlockParser::isEnabled()) {
$docParser = new DocParser();
$docParser->setIgnoreNotImportedAnnotations(true);
- $docParser->setImports($aliases);
+ /** @var array $importedAliases doctrine wants class-string; ours may be bare namespaces too */
+ $importedAliases = $aliases;
+ $docParser->setImports($importedAliases);
$this->docParser = $docParser;
}
}
@@ -40,11 +45,13 @@ public static function isEnabled(): bool
}
/**
- * @param array $aliases
+ * @param array $aliases
*/
public function setAliases(array $aliases): void
{
- $this->docParser->setImports($aliases);
+ /** @var array $importedAliases doctrine wants class-string; ours may be bare namespaces too */
+ $importedAliases = $aliases;
+ $this->docParser->setImports($importedAliases);
}
/**
@@ -69,7 +76,7 @@ public function fromComment(string $comment, Context $context): array
if (preg_match('/^(.+) at position ([0-9]+) in ' . preg_quote((string) $context, '/') . '\.$/', $exception->getMessage(), $matches)) {
$errorMessage = $matches[1];
$errorPos = (int) $matches[2];
- $atPos = strpos($comment, '@');
+ $atPos = strpos($comment, '@') ?: 0;
$context->line -= substr_count($comment, "\n", $atPos + $errorPos) + 1;
$lines = explode("\n", substr($comment, $atPos, $errorPos));
$context->character = strlen(array_pop($lines)) + 1; // position starts at 0 character starts at 1
diff --git a/upload/api/vendor/zircote/swagger-php/src/Analysers/ReflectionAnalyser.php b/upload/api/vendor/zircote/swagger-php/src/Analysers/ReflectionAnalyser.php
index 6a0654e9..f8aa8f2c 100644
--- a/upload/api/vendor/zircote/swagger-php/src/Analysers/ReflectionAnalyser.php
+++ b/upload/api/vendor/zircote/swagger-php/src/Analysers/ReflectionAnalyser.php
@@ -12,6 +12,7 @@
use OpenApi\Generator;
use OpenApi\GeneratorAwareTrait;
use OpenApi\OpenApiException;
+use OpenApi\Utils\ClassReflector;
use OpenApi\Utils\TokenScanner;
/**
@@ -20,6 +21,11 @@
* Can read either PHP ]DocBlocks or Attributes.
*
* Due to the nature of reflection, this requires all related classes to be auto-loadable.
+ *
+ * @phpstan-import-type ClassDefinition from Analysis
+ * @phpstan-import-type InterfaceDefinition from Analysis
+ * @phpstan-import-type TraitDefinition from Analysis
+ * @phpstan-import-type EnumDefinition from Analysis
*/
class ReflectionAnalyser implements AnalyserInterface
{
@@ -68,8 +74,12 @@ public function fromFile(string $filename, Context $context): Analysis
return $analysis;
}
+ /**
+ * @param class-string $fqdn
+ */
public function fromFqdn(string $fqdn, Analysis $analysis): Analysis
{
+ /** @var class-string $fqdn ltrim() does not change that */
$fqdn = ltrim($fqdn, '\\');
$rc = new \ReflectionClass($fqdn);
@@ -85,15 +95,35 @@ public function fromFqdn(string $fqdn, Analysis $analysis): Analysis
return $analysis;
}
+ /**
+ * @param array $details
+ */
protected function analyzeFqdn(string $fqdn, Analysis $analysis, array $details): Analysis
{
- if (!class_exists($fqdn) && !interface_exists($fqdn) && !trait_exists($fqdn) && (!function_exists('enum_exists') || !enum_exists($fqdn))) {
- $analysis->context->logger->warning('Skipping unknown ' . $fqdn);
+ [$rc, $reason] = ClassReflector::tryReflect($fqdn);
+ if (!$rc instanceof \ReflectionClass) {
+ $analysis->context->logger->warning($reason === null
+ ? 'Skipping unknown ' . $fqdn
+ : "Skipping unloadable {$fqdn}: {$reason}");
return $analysis;
}
- $rc = new \ReflectionClass($fqdn);
+ try {
+ return $this->buildDefinition($rc, $analysis, $details);
+ } catch (\Throwable $throwable) {
+ $analysis->context->logger->warning("Skipping unloadable {$fqdn}: {$throwable->getMessage()}");
+
+ return $analysis;
+ }
+ }
+
+ /**
+ * @param \ReflectionClass