diff --git a/upload/api/vendor/autoload.php b/upload/api/vendor/autoload.php index 126f1f81..43d6a48c 100644 --- a/upload/api/vendor/autoload.php +++ b/upload/api/vendor/autoload.php @@ -19,4 +19,4 @@ require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit0c5beb53538340c3eedf0becc5d8c9c8::getLoader(); +return ComposerAutoloaderInite77d48595e9f9bb29fb530f5885e1fc3::getLoader(); diff --git a/upload/api/vendor/composer/autoload_classmap.php b/upload/api/vendor/composer/autoload_classmap.php index 44f54407..984713c9 100644 --- a/upload/api/vendor/composer/autoload_classmap.php +++ b/upload/api/vendor/composer/autoload_classmap.php @@ -8,4 +8,5 @@ return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'DcApi' => $baseDir . '/sdk/DcApi.php', + 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', ); diff --git a/upload/api/vendor/composer/autoload_files.php b/upload/api/vendor/composer/autoload_files.php index e56bd016..adfc73d7 100644 --- a/upload/api/vendor/composer/autoload_files.php +++ b/upload/api/vendor/composer/autoload_files.php @@ -8,6 +8,10 @@ return array( '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', '253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', 'b33e3d135e5d9e47d845c576147bda89' => $vendorDir . '/php-di/php-di/src/functions.php', diff --git a/upload/api/vendor/composer/autoload_psr4.php b/upload/api/vendor/composer/autoload_psr4.php index afcc81da..1753964b 100644 --- a/upload/api/vendor/composer/autoload_psr4.php +++ b/upload/api/vendor/composer/autoload_psr4.php @@ -6,11 +6,19 @@ $baseDir = dirname($vendorDir); return array( + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'), + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), + 'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'), 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), + 'Symfony\\Component\\TypeInfo\\' => array($vendorDir . '/symfony/type-info'), + 'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'), 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Slim\\Psr7\\' => array($vendorDir . '/slim/psr7/src'), 'Slim\\' => array($vendorDir . '/slim/slim/Slim'), + 'Radebatz\\TypeInfoExtras\\' => array($vendorDir . '/radebatz/type-info-extras/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), 'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-middleware/src', $vendorDir . '/psr/http-server-handler/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), diff --git a/upload/api/vendor/composer/autoload_real.php b/upload/api/vendor/composer/autoload_real.php index 0f90e1e7..1abfddf8 100644 --- a/upload/api/vendor/composer/autoload_real.php +++ b/upload/api/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit0c5beb53538340c3eedf0becc5d8c9c8 +class ComposerAutoloaderInite77d48595e9f9bb29fb530f5885e1fc3 { private static $loader; @@ -24,16 +24,16 @@ public static function getLoader() require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInit0c5beb53538340c3eedf0becc5d8c9c8', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInite77d48595e9f9bb29fb530f5885e1fc3', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInit0c5beb53538340c3eedf0becc5d8c9c8', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInite77d48595e9f9bb29fb530f5885e1fc3', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInite77d48595e9f9bb29fb530f5885e1fc3::getInitializer($loader)); $loader->register(true); - $filesToLoad = \Composer\Autoload\ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8::$files; + $filesToLoad = \Composer\Autoload\ComposerStaticInite77d48595e9f9bb29fb530f5885e1fc3::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; diff --git a/upload/api/vendor/composer/autoload_static.php b/upload/api/vendor/composer/autoload_static.php index 2ac8346c..ba823d03 100644 --- a/upload/api/vendor/composer/autoload_static.php +++ b/upload/api/vendor/composer/autoload_static.php @@ -4,11 +4,15 @@ namespace Composer\Autoload; -class ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8 +class ComposerStaticInite77d48595e9f9bb29fb530f5885e1fc3 { public static $files = array ( '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', '253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', 'b33e3d135e5d9e47d845c576147bda89' => __DIR__ . '/..' . '/php-di/php-di/src/functions.php', @@ -19,12 +23,23 @@ class ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8 public static $prefixLengthsPsr4 = array ( 'S' => array ( + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33, + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31, 'Symfony\\Polyfill\\Ctype\\' => 23, + 'Symfony\\Contracts\\Service\\' => 26, 'Symfony\\Component\\Yaml\\' => 23, + 'Symfony\\Component\\TypeInfo\\' => 27, + 'Symfony\\Component\\String\\' => 25, 'Symfony\\Component\\Finder\\' => 25, + 'Symfony\\Component\\Console\\' => 26, 'Slim\\Psr7\\' => 10, 'Slim\\' => 5, ), + 'R' => + array ( + 'Radebatz\\TypeInfoExtras\\' => 24, + ), 'P' => array ( 'Psr\\Log\\' => 8, @@ -74,18 +89,46 @@ class ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8 ); public static $prefixDirsPsr4 = array ( + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer', + ), + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme', + ), 'Symfony\\Polyfill\\Ctype\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', ), + 'Symfony\\Contracts\\Service\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/service-contracts', + ), 'Symfony\\Component\\Yaml\\' => array ( 0 => __DIR__ . '/..' . '/symfony/yaml', ), + 'Symfony\\Component\\TypeInfo\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/type-info', + ), + 'Symfony\\Component\\String\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/string', + ), 'Symfony\\Component\\Finder\\' => array ( 0 => __DIR__ . '/..' . '/symfony/finder', ), + 'Symfony\\Component\\Console\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/console', + ), 'Slim\\Psr7\\' => array ( 0 => __DIR__ . '/..' . '/slim/psr7/src', @@ -94,6 +137,10 @@ class ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8 array ( 0 => __DIR__ . '/..' . '/slim/slim/Slim', ), + 'Radebatz\\TypeInfoExtras\\' => + array ( + 0 => __DIR__ . '/..' . '/radebatz/type-info-extras/src', + ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/src', @@ -190,14 +237,15 @@ class ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8 public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'DcApi' => __DIR__ . '/../..' . '/sdk/DcApi.php', + 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit0c5beb53538340c3eedf0becc5d8c9c8::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInite77d48595e9f9bb29fb530f5885e1fc3::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInite77d48595e9f9bb29fb530f5885e1fc3::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInite77d48595e9f9bb29fb530f5885e1fc3::$classMap; }, null, ClassLoader::class); } diff --git a/upload/api/vendor/composer/installed.json b/upload/api/vendor/composer/installed.json index 5d9e7d84..76c5031a 100644 --- a/upload/api/vendor/composer/installed.json +++ b/upload/api/vendor/composer/installed.json @@ -1521,6 +1521,71 @@ }, "install-path": "../psr/log" }, + { + "name": "radebatz/type-info-extras", + "version": "1.0.7", + "version_normalized": "1.0.7.0", + "source": { + "type": "git", + "url": "https://github.com/DerManoMann/type-info-extras.git", + "reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DerManoMann/type-info-extras/zipball/95a524a74a61648b44e355cb33d38db4b17ef5ce", + "reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "phpstan/phpdoc-parser": "^2.0", + "symfony/type-info": "^7.3.8 || ^7.4.1 || ^8.0 || ^8.1-@dev" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.70", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^11.0" + }, + "time": "2026-03-06T22:40:29+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Radebatz\\TypeInfoExtras\\": "src" + }, + "exclude-from-classmap": [ + "/tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Martin Rademacher", + "email": "mano@radebatz.org" + } + ], + "description": "Extras for symfony/type-info", + "homepage": "http://radebatz.net/mano/", + "keywords": [ + "component", + "symfony", + "type-info", + "types" + ], + "support": { + "issues": "https://github.com/DerManoMann/type-info-extras/issues", + "source": "https://github.com/DerManoMann/type-info-extras/tree/1.0.7" + }, + "install-path": "../radebatz/type-info-extras" + }, { "name": "ralouphie/getallheaders", "version": "3.0.3", @@ -1769,6 +1834,107 @@ ], "install-path": "../slim/slim" }, + { + "name": "symfony/console", + "version": "v7.4.17", + "version_normalized": "7.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "962e18f09ebe68a49039b4c82fc0ea4871824fca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/962e18f09ebe68a49039b4c82fc0ea4871824fca", + "reference": "962e18f09ebe68a49039b4c82fc0ea4871824fca", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "time": "2026-08-21T12:09:28+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.17" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/console" + }, { "name": "symfony/deprecation-contracts", "version": "v3.7.1", @@ -1845,17 +2011,17 @@ }, { "name": "symfony/finder", - "version": "v7.4.14", - "version_normalized": "7.4.14.0", + "version": "v7.4.17", + "version_normalized": "7.4.17.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "13b38720174286f55d1761152b575a8d1436fc25" + "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/13b38720174286f55d1761152b575a8d1436fc25", - "reference": "13b38720174286f55d1761152b575a8d1436fc25", + "url": "https://api.github.com/repos/symfony/finder/zipball/5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", + "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", "shasum": "" }, "require": { @@ -1864,7 +2030,7 @@ "require-dev": { "symfony/filesystem": "^6.4|^7.0|^8.0" }, - "time": "2026-06-27T08:31:18+00:00", + "time": "2026-08-21T12:09:28+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1892,7 +2058,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.14" + "source": "https://github.com/symfony/finder/tree/v7.4.17" }, "funding": [ { @@ -2001,43 +2167,129 @@ "install-path": "../symfony/polyfill-ctype" }, { - "name": "symfony/yaml", - "version": "v7.4.15", - "version_normalized": "7.4.15.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.41.0", + "version_normalized": "1.41.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e101850ded5d2c0d44bf32abb8996404afec2dec", - "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "^1.8" + "php": ">=7.2" }, - "conflict": { - "symfony/console": "<6.4" + "suggest": { + "ext-intl": "For best performance" }, - "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" + "time": "2026-07-28T08:25:59+00:00", + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } }, - "time": "2026-07-21T15:13:06+00:00", - "bin": [ - "Resources/bin/yaml-lint" + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } ], + "install-path": "../symfony/polyfill-intl-grapheme" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.42.0", + "version_normalized": "1.42.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502", + "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2026-08-07T06:33:24+00:00", "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "installation-source": "dist", "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2046,18 +2298,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Loads and dumps YAML files", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.15" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.42.0" }, "funding": [ { @@ -2077,57 +2337,489 @@ "type": "tidelift" } ], - "install-path": "../symfony/yaml" + "install-path": "../symfony/polyfill-intl-normalizer" }, { - "name": "zircote/swagger-php", - "version": "5.8.3", - "version_normalized": "5.8.3.0", + "name": "symfony/polyfill-mbstring", + "version": "v1.38.2", + "version_normalized": "1.38.2.0", "source": { "type": "git", - "url": "https://github.com/zircote/swagger-php.git", - "reference": "098223019f764a16715f64089a58606096719c98" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/098223019f764a16715f64089a58606096719c98", - "reference": "098223019f764a16715f64089a58606096719c98", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { - "ext-json": "*", - "nikic/php-parser": "^4.19 || ^5.0", - "php": ">=7.4", - "phpstan/phpdoc-parser": "^2.0", - "psr/log": "^1.1 || ^2.0 || ^3.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" - }, - "conflict": { - "symfony/process": ">=6, <6.4.14" + "ext-iconv": "*", + "php": ">=7.2" }, - "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/annotations": "^2.0", - "friendsofphp/php-cs-fixer": "^3.62.0", - "phpstan/phpstan": "^1.6 || ^2.0", - "phpunit/phpunit": "^9.0", - "rector/rector": "^1.0 || ^2.3.1", - "vimeo/psalm": "^4.30 || ^5.0" + "provide": { + "ext-mbstring": "*" }, "suggest": { - "doctrine/annotations": "^2.0", - "radebatz/type-info-extras": "^1.0.2" + "ext-mbstring": "For best performance" }, - "time": "2026-03-02T00:47:18+00:00", - "bin": [ - "bin/openapi" - ], + "time": "2026-05-27T06:59:30+00:00", "type": "library", "extra": { - "branch-alias": { - "dev-master": "5.x-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-mbstring" + }, + { + "name": "symfony/service-contracts", + "version": "v3.7.1", + "version_normalized": "3.7.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "time": "2026-06-16T09:55:08+00:00", + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/service-contracts" + }, + { + "name": "symfony/string", + "version": "v7.4.15", + "version_normalized": "7.4.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.33", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" + }, + "time": "2026-07-28T07:33:02+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/string" + }, + { + "name": "symfony/type-info", + "version": "v7.4.17", + "version_normalized": "7.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/type-info.git", + "reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/type-info/zipball/62d0ad6995630a4f7ea8fd270aadb3e2545c8e31", + "reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "phpstan/phpdoc-parser": "<1.30" + }, + "require-dev": { + "phpstan/phpdoc-parser": "^1.30|^2.0" + }, + "time": "2026-08-21T15:52:48+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\TypeInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Arlaud", + "email": "mathias.arlaud@gmail.com" + }, + { + "name": "Baptiste LEDUC", + "email": "baptiste.leduc@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Extracts PHP types information.", + "homepage": "https://symfony.com", + "keywords": [ + "PHPStan", + "phpdoc", + "symfony", + "type" + ], + "support": { + "source": "https://github.com/symfony/type-info/tree/v7.4.17" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/type-info" + }, + { + "name": "symfony/yaml", + "version": "v7.4.17", + "version_normalized": "7.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "0b040d7b66ceb10b7bb24c8e6656257932693a12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0b040d7b66ceb10b7bb24c8e6656257932693a12", + "reference": "0b040d7b66ceb10b7bb24c8e6656257932693a12", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "time": "2026-08-21T12:09:28+00:00", + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.4.17" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/yaml" + }, + { + "name": "zircote/swagger-php", + "version": "6.6.0", + "version_normalized": "6.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "cc81abcfffec8b2f993db64013ea3b49679341c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/cc81abcfffec8b2f993db64013ea3b49679341c9", + "reference": "cc81abcfffec8b2f993db64013ea3b49679341c9", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.19 || ^5.0", + "php": ">=8.2", + "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/deprecation-contracts": "^2 || ^3", + "symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "conflict": { + "symfony/process": ">=6, <6.4.14" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "friendsofphp/php-cs-fixer": "^3.62", + "phpstan/phpstan": "^2.2", + "phpunit/phpunit": "^11.5 || >=12.5.22", + "rector/rector": "^2.3" + }, + "time": "2026-08-18T03:19:28+00:00", + "bin": [ + "bin/openapi" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" } }, "installation-source": "dist", @@ -2166,7 +2858,7 @@ ], "support": { "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/5.8.3" + "source": "https://github.com/zircote/swagger-php/tree/6.6.0" }, "funding": [ { diff --git a/upload/api/vendor/composer/installed.php b/upload/api/vendor/composer/installed.php index 42dfe606..8b1701a9 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' => 'bd9dd38cabded2ed0897f8869deae4cf18f2c39d', + 'reference' => 'cc9a2299dc5b02aa1251034830e4aafc7dcc6cd7', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -22,7 +22,7 @@ 'devcraftclub/dle-api' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'bd9dd38cabded2ed0897f8869deae4cf18f2c39d', + 'reference' => 'cc9a2299dc5b02aa1251034830e4aafc7dcc6cd7', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -265,9 +265,19 @@ 'psr/log-implementation' => array( 'dev_requirement' => false, 'provided' => array( - 0 => '3.0.0', + 0 => '1.0|2.0|3.0', + 1 => '3.0.0', ), ), + 'radebatz/type-info-extras' => array( + 'pretty_version' => '1.0.7', + 'version' => '1.0.7.0', + 'reference' => '95a524a74a61648b44e355cb33d38db4b17ef5ce', + 'type' => 'library', + 'install_path' => __DIR__ . '/../radebatz/type-info-extras', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'ralouphie/getallheaders' => array( 'pretty_version' => '3.0.3', 'version' => '3.0.3.0', @@ -295,6 +305,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'symfony/console' => array( + 'pretty_version' => 'v7.4.17', + 'version' => '7.4.17.0', + 'reference' => '962e18f09ebe68a49039b4c82fc0ea4871824fca', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/console', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'symfony/deprecation-contracts' => array( 'pretty_version' => 'v3.7.1', 'version' => '3.7.1.0', @@ -305,9 +324,9 @@ 'dev_requirement' => false, ), 'symfony/finder' => array( - 'pretty_version' => 'v7.4.14', - 'version' => '7.4.14.0', - 'reference' => '13b38720174286f55d1761152b575a8d1436fc25', + 'pretty_version' => 'v7.4.17', + 'version' => '7.4.17.0', + 'reference' => '5ce28827081f6d1f0c32eaf3882750f19cb5bbe6', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/finder', 'aliases' => array(), @@ -322,19 +341,73 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'symfony/yaml' => array( + 'symfony/polyfill-intl-grapheme' => array( + 'pretty_version' => 'v1.41.0', + 'version' => '1.41.0.0', + 'reference' => 'bb899c1db0aa8127dc3afe8cda4a67eb24915f8d', + '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', + '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', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/service-contracts' => array( + 'pretty_version' => 'v3.7.1', + 'version' => '3.7.1.0', + 'reference' => 'c0a284bab1ed8aa0417e3d69250ab437739563a0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/service-contracts', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/string' => array( 'pretty_version' => 'v7.4.15', 'version' => '7.4.15.0', - 'reference' => 'e101850ded5d2c0d44bf32abb8996404afec2dec', + 'reference' => 'e394af32256bf9e7bf80849d95e589167c10097b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/string', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/type-info' => array( + 'pretty_version' => 'v7.4.17', + 'version' => '7.4.17.0', + 'reference' => '62d0ad6995630a4f7ea8fd270aadb3e2545c8e31', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/type-info', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/yaml' => array( + 'pretty_version' => 'v7.4.17', + 'version' => '7.4.17.0', + 'reference' => '0b040d7b66ceb10b7bb24c8e6656257932693a12', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/yaml', 'aliases' => array(), 'dev_requirement' => false, ), 'zircote/swagger-php' => array( - 'pretty_version' => '5.8.3', - 'version' => '5.8.3.0', - 'reference' => '098223019f764a16715f64089a58606096719c98', + 'pretty_version' => '6.6.0', + 'version' => '6.6.0.0', + 'reference' => 'cc81abcfffec8b2f993db64013ea3b49679341c9', 'type' => 'library', 'install_path' => __DIR__ . '/../zircote/swagger-php', 'aliases' => array(), diff --git a/upload/api/vendor/radebatz/type-info-extras/LICENSE b/upload/api/vendor/radebatz/type-info-extras/LICENSE new file mode 100644 index 00000000..16dc73e7 --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024-present Martin Rademacher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/api/vendor/radebatz/type-info-extras/Readme.md b/upload/api/vendor/radebatz/type-info-extras/Readme.md new file mode 100644 index 00000000..c6971b6c --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/Readme.md @@ -0,0 +1,41 @@ +[![build](https://github.com/DerManoMann/type-info-extras/actions/workflows/build.yml/badge.svg)](https://github.com/DerManoMann/type-info-extras/actions/workflows/build.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + +TypeInfoExtras +============== + +Library adding some extra features to the Symfony [Type Info](https://github.com/symfony/type-info) component. + +Compatible with Symfony +[type-info:7.3.8+](https://github.com/symfony/type-info/tree/7.3), +[type-info:7.4.1+](https://github.com/symfony/type-info/tree/7.4), +[type-info:8.0+](https://github.com/symfony/type-info/tree/8.0) +and [type-info:8.1+](https://github.com/symfony/type-info/tree/8.1). + +Basic Usage +----------- + +```php +resolve('html-escaped-string'); +echo get_class($type); // Radebatz\TypeInfoExtras\Type\ExplicitType +echo $type->getExplicitType() // "html-escaped-string" + +$type = $resolver->resolve('class-string'); +echo get_class($type); // Radebatz\TypeInfoExtras\Type\ClassLikeType +echo $type->getExplicitType(); // "class-string" +echo get_class($type->getObjectType()); // Symfony\Component\TypeInfo\Type\ObjectType +echo $type->getObjectType(); // Foo + +$type = $resolver->resolve('int<5,20>'); +echo get_class($type); // Radebatz\TypeInfoExtras\Type\IntRangeType + +``` + +If your code is doing `instanceof` checks on the returned `Type`, then you will need to add another case and treat +`Radebatz\TypeInfoExtras\Tests\Type\ExplicitType` same as `BuiltinType`. diff --git a/upload/api/vendor/radebatz/type-info-extras/composer.json b/upload/api/vendor/radebatz/type-info-extras/composer.json new file mode 100644 index 00000000..e6722218 --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/composer.json @@ -0,0 +1,65 @@ +{ + "name": "radebatz/type-info-extras", + "description": "Extras for symfony/type-info", + "keywords": ["component", "types", "symfony", "type-info"], + "license": [ + "MIT" + ], + "homepage": "http://radebatz.net/mano/", + "authors": [ + { + "name": "Martin Rademacher", + "email": "mano@radebatz.org" + } + ], + "config": { + "sort-packages": true, + "preferred-install": { + "symfony/type-info": "source", + "*": "dist" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "require": { + "php": ">=8.2", + "phpstan/phpdoc-parser": "^2.0", + "symfony/type-info": "^7.3.8 || ^7.4.1 || ^8.0 || ^8.1-@dev" + }, + "autoload": { + "psr-4": { "Radebatz\\TypeInfoExtras\\": "src" }, + "exclude-from-classmap": [ + "/tests/" + ] + }, + "autoload-dev": { + "psr-4": { + "Radebatz\\TypeInfoExtras\\Tests\\": "tests/" + } + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.70", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^11.0" + }, + "scripts-descriptions": { + "cs": "Fix all codestyle issues", + "lint": "Test codestyle", + "test": "Run all PHP, codestyle tests", + "analyse": "Run static analysis (phpstan)" + }, + "scripts": { + "cs": "export XDEBUG_MODE=off && php-cs-fixer fix --allow-risky=yes", + "lint": "@cs --dry-run", + "test": [ + "export XDEBUG_MODE=off && phpunit", + "@lint" + ], + "analyse": [ + "export XDEBUG_MODE=off && phpstan analyse --memory-limit=2G" + ] + } +} diff --git a/upload/api/vendor/radebatz/type-info-extras/src/Type/ClassLikeType.php b/upload/api/vendor/radebatz/type-info-extras/src/Type/ClassLikeType.php new file mode 100644 index 00000000..e40aa850 --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/src/Type/ClassLikeType.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Radebatz\TypeInfoExtras\Type; + +use Symfony\Component\TypeInfo\Type\ObjectType; +use Symfony\Component\TypeInfo\TypeIdentifier; + +/** + * Class-like string type. + * + * @author Martin Rademacher + * + * @extends ExplicitType + */ +final class ClassLikeType extends ExplicitType +{ + public function __construct(string $explicitType, private ObjectType $objectType) + { + parent::__construct(TypeIdentifier::STRING, $explicitType); + } + + public function getObjectType(): ObjectType + { + return $this->objectType; + } + + public function __toString(): string + { + return \sprintf('%s<%s>', $this->getExplicitType(), $this->objectType); + } +} diff --git a/upload/api/vendor/radebatz/type-info-extras/src/Type/ExplicitType.php b/upload/api/vendor/radebatz/type-info-extras/src/Type/ExplicitType.php new file mode 100644 index 00000000..397282f5 --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/src/Type/ExplicitType.php @@ -0,0 +1,85 @@ +typeIdentifier; + } + + /** + * Get the underlying builtin type. + */ + public function getBuiltinType(): BuiltinType + { + return Type::builtin($this->typeIdentifier); + } + + public function getExplicitType(): string + { + return $this->explicitType; + } + + public function isIdentifiedBy(TypeIdentifier|string ...$identifiers): bool + { + foreach ($identifiers as $identifier) { + if (\is_string($identifier)) { + try { + $identifier = TypeIdentifier::from($identifier); + } catch (\ValueError) { + continue; + } + } + + if ($identifier === $this->typeIdentifier) { + return true; + } + } + + return false; + } + + public function isNullable(): bool + { + return false; + } + + public function accepts(mixed $value): bool + { + return match ($this->typeIdentifier) { + TypeIdentifier::ARRAY => \is_array($value), + TypeIdentifier::INT => \is_int($value), + default => false, + }; + } + + public function __toString(): string + { + return $this->explicitType; + } +} diff --git a/upload/api/vendor/radebatz/type-info-extras/src/Type/IntRangeType.php b/upload/api/vendor/radebatz/type-info-extras/src/Type/IntRangeType.php new file mode 100644 index 00000000..41cd11ea --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/src/Type/IntRangeType.php @@ -0,0 +1,51 @@ + + * + * @extends ExplicitType + */ +final class IntRangeType extends ExplicitType +{ + public function __construct( + private int $from = \PHP_INT_MIN, + private int $to = \PHP_INT_MAX, + ?string $explicitType = null, + ) { + parent::__construct(TypeIdentifier::INT, $explicitType ?? $this->canonical($this->from, $this->to)); + } + + public function getFrom(): int + { + return $this->from; + } + + public function getTo(): int + { + return $this->to; + } + + public function accepts(mixed $value): bool + { + return parent::accepts($value) && $this->from <= $value && $value <= $this->to; + } + + public function __toString(): string + { + return $this->canonical($this->from, $this->to); + } + + protected function canonical(int $from, int $to): string + { + $min = \PHP_INT_MIN === $from ? 'min' : $from; + $max = \PHP_INT_MAX === $to ? 'max' : $to; + + return \sprintf('int<%s, %s>', $min, $max); + } +} diff --git a/upload/api/vendor/radebatz/type-info-extras/src/Type/Type.php b/upload/api/vendor/radebatz/type-info-extras/src/Type/Type.php new file mode 100644 index 00000000..51a8a08b --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/src/Type/Type.php @@ -0,0 +1,11 @@ +name) { + 'min' => \PHP_INT_MIN, + 'max' => \PHP_INT_MAX, + default => throw new \DomainException(\sprintf('Invalid int range value "%s".', $node->name)), + }; + } + + if ($node instanceof ConstTypeNode && $node->constExpr instanceof ConstExprIntegerNode) { + return (int) $node->constExpr->value; + } + + throw new \DomainException(\sprintf('Invalid int range expression "%s".', \get_class($node))); + }; + + $boundaries = array_map(fn (TypeNode $t): int => $getBoundaryFromNode($t), $node->genericTypes); + + return Type::intRange($boundaries[0], $boundaries[1]); + } + + /** + * @param array $variableTypes + */ + protected function tryAsClassLike(BaseType $type, array $variableTypes): ?ClassLikeType + { + if ($type instanceof ExplicitType + && \in_array($type->getExplicitType(), ['class-string', 'interface-string', 'trait-string'], true) + && 1 === \count($variableTypes) && $variableTypes[0] instanceof ObjectType) { + return Type::classLike($type->getExplicitType(), $variableTypes[0]); + } + + return null; + } + + public function reduceToBuiltinType(?BaseType $type) + { + return $type instanceof ExplicitType + ? $type->getBuiltinType() + : $type; + } +} 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 new file mode 100644 index 00000000..2473daf7 --- /dev/null +++ b/upload/api/vendor/radebatz/type-info-extras/src/TypeResolver/StringTypeResolver.php @@ -0,0 +1,385 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Radebatz\TypeInfoExtras\TypeResolver; + +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNullNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; +use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; +use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; +use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; +use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; +use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; +use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; +use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; +use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode; +use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; +use PHPStan\PhpDocParser\Ast\Type\TypeNode; +use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; +use PHPStan\PhpDocParser\Lexer\Lexer; +use PHPStan\PhpDocParser\Parser\ConstExprParser; +use PHPStan\PhpDocParser\Parser\TokenIterator; +use PHPStan\PhpDocParser\Parser\TypeParser; +use PHPStan\PhpDocParser\ParserConfig; +use Radebatz\TypeInfoExtras\Type\Type; +use Symfony\Component\TypeInfo\Exception\InvalidArgumentException; +use Symfony\Component\TypeInfo\Exception\UnsupportedException; +use Symfony\Component\TypeInfo\Type as BaseType; +use Symfony\Component\TypeInfo\Type\BackedEnumType; +use Symfony\Component\TypeInfo\Type\BuiltinType; +use Symfony\Component\TypeInfo\Type\CollectionType; +use Symfony\Component\TypeInfo\Type\GenericType; +use Symfony\Component\TypeInfo\Type\ObjectType; +use Symfony\Component\TypeInfo\TypeContext\TypeContext; +use Symfony\Component\TypeInfo\TypeIdentifier; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; + +/** + * Resolves type for a given string. + * + * @author Mathias Arlaud + * @author Baptiste Leduc + * @author Martin Rademacher + */ +final class StringTypeResolver implements TypeResolverInterface +{ + use ResolverExtrasTrait; + + /** + * @var array + */ + private static array $classExistCache = []; + + private readonly Lexer $lexer; + private readonly TypeParser $parser; + + /** + * @param array $extraTypeAliases + */ + public function __construct( + ?Lexer $lexer = null, + ?TypeParser $parser = null, + private readonly array $extraTypeAliases = [], + ) { + $this->lexer = $lexer ?? new Lexer(new ParserConfig([])); + $this->parser = $parser ?? new TypeParser($config = new ParserConfig([]), new ConstExprParser($config)); + } + + public function resolve(mixed $subject, ?TypeContext $typeContext = null): BaseType + { + if ($subject instanceof \Stringable) { + $subject = (string) $subject; + } elseif (!\is_string($subject)) { + throw new UnsupportedException(\sprintf('Expected subject to be a "string", "%s" given.', get_debug_type($subject)), $subject); + } + + try { + $tokens = new TokenIterator($this->lexer->tokenize($subject)); + $node = $this->parser->parse($tokens); + + return $this->getTypeFromNode($node, $typeContext); + } catch (\DomainException $e) { + throw new UnsupportedException(\sprintf('Cannot resolve "%s".', $subject), $subject, previous: $e); + } + } + + private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): BaseType + { + $typeIsCollectionObject = static fn (BaseType $type): bool => $type->isIdentifiedBy(\Traversable::class) || $type->isIdentifiedBy(\ArrayAccess::class); + + if ($node instanceof CallableTypeNode) { + return Type::callable(); + } + + if ($node instanceof ArrayTypeNode) { + return Type::array($this->getTypeFromNode($node->type, $typeContext)); + } + + if ($node instanceof ArrayShapeNode) { + $shape = []; + foreach ($node->items as $item) { + $shape[(string) $item->keyName] = [ + 'type' => $this->getTypeFromNode($item->valueType, $typeContext), + 'optional' => $item->optional, + ]; + } + + return Type::arrayShape( + $shape, + $node->sealed, + $node->unsealedType?->keyType ? $this->getTypeFromNode($node->unsealedType->keyType, $typeContext) : null, + $node->unsealedType?->valueType ? $this->getTypeFromNode($node->unsealedType->valueType, $typeContext) : null, + ); + } + + if ($node instanceof ObjectShapeNode) { + $shape = []; + foreach ($node->items as $item) { + $shape[(string) $item->keyName] = [ + 'type' => $this->getTypeFromNode($item->valueType, $typeContext), + 'optional' => $item->optional, + ]; + } + + return ($shape && method_exists(Type::class, 'objectShape')) + ? Type::objectShape($shape) + : Type::object(); + } + + if ($node instanceof ThisTypeNode) { + if (null === $typeContext) { + throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "$this".', TypeContext::class)); + } + + return Type::object($typeContext->getCalledClass()); + } + + if ($node instanceof ConstTypeNode) { + if ($node->constExpr instanceof ConstFetchNode) { + $className = match (strtolower($node->constExpr->className)) { + 'self' => $typeContext->getDeclaringClass(), + 'static' => $typeContext->getCalledClass(), + 'parent' => $typeContext->getParentClass(), + default => null, + }; + + if (null === $className) { + $classType = $this->resolveCustomIdentifier($node->constExpr->className, $typeContext); + if (!$classType instanceof ObjectType) { + return Type::mixed(); + } + + $className = $classType->getClassName(); + } + + if (!class_exists($className)) { + return Type::mixed(); + } + + $types = []; + + foreach ((new \ReflectionClass($className))->getReflectionConstants() as $const) { + if (preg_match('/^' . str_replace('\*', '.*', preg_quote($node->constExpr->name, '/')) . '$/', $const->getName())) { + $types[] = Type::fromValue($const->getValue()); + } + } + + 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(), + default => throw new \DomainException(\sprintf('Unhandled "%s" constant expression.', $node->constExpr::class)), + }; + } + + if ($node instanceof IdentifierTypeNode) { + $type = match ($node->name) { + 'bool', 'boolean' => Type::bool(), + 'true' => Type::true(), + 'false' => Type::false(), + 'int', 'integer' => Type::int(), + 'positive-int' => Type::intRange(from: 1, explicitType: $node->name), + 'negative-int' => Type::intRange(to: -1, explicitType: $node->name), + 'non-positive-int' => Type::intRange(to: 0, explicitType: $node->name), + 'non-negative-int' => Type::intRange(from: 0, explicitType: $node->name), + 'non-zero-int' => BaseType::union(Type::intRange(to: -1), Type::intRange(from: 1)), + 'float', 'double' => Type::float(), + 'string' => BaseType::string(), + 'class-string', + 'trait-string', + 'interface-string', + 'callable-string', + 'numeric-string', + 'lowercase-string', + 'non-empty-lowercase-string', + 'non-empty-string', + 'non-falsy-string', + 'truthy-string', + 'literal-string', + 'html-escaped-string' => Type::explicit(TypeIdentifier::STRING, $node->name), + 'resource' => Type::resource(), + 'object' => Type::object(), + 'callable' => Type::callable(), + 'array', 'non-empty-array' => Type::array(), + 'list', 'non-empty-list' => Type::list(), + 'iterable' => Type::iterable(), + 'mixed' => Type::mixed(), + 'null' => Type::null(), + 'array-key' => Type::arrayKey(), + 'scalar' => Type::union(Type::int(), Type::float(), Type::string(), Type::bool()), + 'number' => Type::union(Type::int(), Type::float()), + 'numeric' => Type::union(Type::int(), Type::float(), Type::string()), + 'self' => $typeContext ? Type::object($typeContext->getDeclaringClass()) : throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "self".', TypeContext::class)), + 'static' => $typeContext ? Type::object($typeContext->getCalledClass()) : throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "static".', TypeContext::class)), + 'parent' => $typeContext ? Type::object($typeContext->getParentClass()) : throw new InvalidArgumentException(\sprintf('A "%s" must be provided to resolve "parent".', TypeContext::class)), + 'void' => Type::void(), + 'never', 'never-return', 'never-returns', 'no-return' => Type::never(), + default => $this->resolveCustomIdentifier($node->name, $typeContext), + }; + + if ($typeIsCollectionObject($type)) { + return Type::collection($type); + } + + return $type; + } + + if ($node instanceof NullableTypeNode) { + return Type::nullable($this->getTypeFromNode($node->type, $typeContext)); + } + + if ($node instanceof GenericTypeNode) { + if ($node->type instanceof IdentifierTypeNode && 'value-of' === $node->type->name) { + $type = $this->getTypeFromNode($node->genericTypes[0], $typeContext); + if ($type instanceof BackedEnumType) { + return $type->getBackingType(); + } + + if ($type instanceof CollectionType) { + return $type->getCollectionValueType(); + } + + throw new \DomainException(\sprintf('"%s" is not a valid type for "value-of".', $node->genericTypes[0])); + } + + if ($node->type instanceof IdentifierTypeNode && 'key-of' === $node->type->name) { + $type = $this->getTypeFromNode($node->genericTypes[0], $typeContext); + if ($type instanceof CollectionType) { + return $type->getCollectionKeyType(); + } + + throw new \DomainException(\sprintf('"%s" is not a valid type for "key-of".', $node->genericTypes[0])); + } + + $type = $this->getTypeFromNode($node->type, $typeContext); + + // handle integer ranges + if ($type->isIdentifiedBy(TypeIdentifier::INT)) { + return $this->resolveIntRange($node); + } + + $variableTypes = array_map(fn (TypeNode $t): BaseType => $this->getTypeFromNode($t, $typeContext), $node->genericTypes); + + if ($type instanceof CollectionType) { + $asList = $type->isList(); + $keyType = $type->getCollectionKeyType(); + $type = $type->getWrappedType(); + + if ($type instanceof GenericType) { + $type = $type->getWrappedType(); + } + + if (1 === \count($variableTypes)) { + return new CollectionType(Type::generic($type, $keyType, $variableTypes[0]), $asList); + } elseif (2 === \count($variableTypes)) { + if ($asList) { + throw new \DomainException(\sprintf('"%s" type cannot have a key type defined.', $node->type)); + } + + return Type::collection($type, $variableTypes[1], $this->reduceToBuiltinType($variableTypes[0]), $asList); + } + } + + if ($typeIsCollectionObject($type)) { + return match (\count($variableTypes)) { + 1 => Type::collection($type, $variableTypes[0]), + 2 => Type::collection($type, $variableTypes[1], $variableTypes[0]), + default => Type::collection($type), + }; + } + + if ($classLike = $this->tryAsClassLike($type, $variableTypes)) { + return $classLike; + } + + if ($type instanceof BuiltinType && TypeIdentifier::ARRAY !== $type->getTypeIdentifier() && TypeIdentifier::ITERABLE !== $type->getTypeIdentifier()) { + return $type; + } + + return Type::generic($type, ...$variableTypes); + } + + if ($node instanceof UnionTypeNode) { + $types = []; + + foreach ($node->types as $nodeType) { + $type = $this->getTypeFromNode($nodeType, $typeContext); + + if ($type instanceof BuiltinType && TypeIdentifier::MIXED === $type->getTypeIdentifier()) { + return Type::mixed(); + } + + $types[] = $type; + } + + return Type::union(...$types); + } + + if ($node instanceof IntersectionTypeNode) { + return Type::intersection(...array_map(fn (TypeNode $t): BaseType => $this->getTypeFromNode($t, $typeContext), $node->types)); + } + + throw new \DomainException(\sprintf('Unhandled "%s" node.', $node::class)); + } + + private function resolveCustomIdentifier(string $identifier, ?TypeContext $typeContext): BaseType + { + $className = $typeContext ? $typeContext->normalize($identifier) : $identifier; + + if (!isset(self::$classExistCache[$className])) { + self::$classExistCache[$className] = false; + + if (class_exists($className) || interface_exists($className)) { + self::$classExistCache[$className] = true; + } else { + try { + new \ReflectionClass($className); + self::$classExistCache[$className] = true; + } catch (\Throwable) { + } + } + } + + if (self::$classExistCache[$className]) { + if (is_subclass_of($className, \UnitEnum::class) && !interface_exists($className)) { + return Type::enum($className); + } + + return Type::object($className); + } + + if (isset($typeContext?->templates[$identifier])) { + return Type::template($identifier, $typeContext->templates[$identifier]); + } + + if (isset($typeContext?->typeAliases[$identifier])) { + return $typeContext->typeAliases[$identifier]; + } + + if (isset($this->extraTypeAliases[$identifier])) { + return $this->resolve($this->extraTypeAliases[$identifier]); + } + + throw new \DomainException(\sprintf('Unhandled "%s" identifier.', $identifier)); + } +} diff --git a/upload/api/vendor/symfony/console/Application.php b/upload/api/vendor/symfony/console/Application.php new file mode 100644 index 00000000..886608e4 --- /dev/null +++ b/upload/api/vendor/symfony/console/Application.php @@ -0,0 +1,1361 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\CompleteCommand; +use Symfony\Component\Console\Command\DumpCompletionCommand; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\LazyCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Completion\Suggestion; +use Symfony\Component\Console\Event\ConsoleAlarmEvent; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleSignalEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Exception\NamespaceNotFoundException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\SignalRegistry\SignalRegistry; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\ErrorHandler\ErrorHandler; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Service\ResetInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->addCommand(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + */ +class Application implements ResetInterface +{ + private array $commands = []; + private bool $wantHelps = false; + private ?Command $runningCommand = null; + private ?CommandLoaderInterface $commandLoader = null; + private bool $catchExceptions = true; + private bool $catchErrors = false; + private bool $autoExit = true; + private InputDefinition $definition; + private HelperSet $helperSet; + private ?EventDispatcherInterface $dispatcher = null; + private Terminal $terminal; + private string $defaultCommand; + private bool $singleCommand = false; + private bool $initialized = false; + private ?SignalRegistry $signalRegistry = null; + private array $signalsToDispatchEvent = []; + private ?int $alarmInterval = null; + + public function __construct( + private string $name = 'UNKNOWN', + private string $version = 'UNKNOWN', + ) { + $this->terminal = new Terminal(); + $this->defaultCommand = 'list'; + if (\defined('SIGINT') && SignalRegistry::isSupported()) { + $this->signalRegistry = new SignalRegistry(); + $this->signalsToDispatchEvent = [\SIGINT, \SIGQUIT, \SIGTERM, \SIGUSR1, \SIGUSR2, \SIGALRM]; + } + } + + /** + * @final + */ + public function setDispatcher(EventDispatcherInterface $dispatcher): void + { + $this->dispatcher = $dispatcher; + } + + public function setCommandLoader(CommandLoaderInterface $commandLoader): void + { + $this->commandLoader = $commandLoader; + } + + public function getSignalRegistry(): SignalRegistry + { + if (!$this->signalRegistry) { + throw new RuntimeException('Signals are not supported. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); + } + + return $this->signalRegistry; + } + + public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent): void + { + $this->signalsToDispatchEvent = $signalsToDispatchEvent; + } + + /** + * Sets the interval to schedule a SIGALRM signal in seconds. + */ + public function setAlarmInterval(?int $seconds): void + { + $this->alarmInterval = $seconds; + $this->scheduleAlarm(); + } + + /** + * Gets the interval in seconds on which a SIGALRM signal is dispatched. + */ + public function getAlarmInterval(): ?int + { + return $this->alarmInterval; + } + + private function scheduleAlarm(): void + { + if (null !== $this->alarmInterval) { + $this->getSignalRegistry()->scheduleAlarm($this->alarmInterval); + } + } + + /** + * Runs the current application. + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. + */ + public function run(?InputInterface $input = null, ?OutputInterface $output = null): int + { + if (\function_exists('putenv')) { + @putenv('LINES='.$this->terminal->getHeight()); + @putenv('COLUMNS='.$this->terminal->getWidth()); + } + + $input ??= new ArgvInput(); + $output ??= new ConsoleOutput(); + + $renderException = function (\Throwable $e) use ($output) { + if ($output instanceof ConsoleOutputInterface) { + $this->renderThrowable($e, $output->getErrorOutput()); + } else { + $this->renderThrowable($e, $output); + } + }; + if ($phpHandler = set_exception_handler($renderException)) { + restore_exception_handler(); + if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { + $errorHandler = true; + } elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) { + $phpHandler[0]->setExceptionHandler($errorHandler); + } + } + + $empty = new \stdClass(); + $prevShellVerbosity = [$_ENV['SHELL_VERBOSITY'] ?? $empty, $_SERVER['SHELL_VERBOSITY'] ?? $empty, getenv('SHELL_VERBOSITY')]; + + try { + $this->configureIO($input, $output); + + $exitCode = $this->doRun($input, $output); + } catch (\Throwable $e) { + if ($e instanceof \Exception && !$this->catchExceptions) { + throw $e; + } + if (!$e instanceof \Exception && !$this->catchErrors) { + throw $e; + } + + $renderException($e); + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if ($exitCode <= 0) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } finally { + // if the exception handler changed, keep it + // otherwise, unregister $renderException + if (!$phpHandler) { + if (set_exception_handler($renderException) === $renderException) { + restore_exception_handler(); + } + restore_exception_handler(); + } elseif (!$errorHandler) { + $finalHandler = $phpHandler[0]->setExceptionHandler(null); + if ($finalHandler !== $renderException) { + $phpHandler[0]->setExceptionHandler($finalHandler); + } + } + + // SHELL_VERBOSITY is set by Application::configureIO so we need to unset/reset it + // to its previous value to avoid one command verbosity to spread to other commands + if ($empty === $_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity[0]) { + unset($_ENV['SHELL_VERBOSITY']); + } + if ($empty === $_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity[1]) { + unset($_SERVER['SHELL_VERBOSITY']); + } + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY'.(false === ($prevShellVerbosity[2] ?? false) ? '' : '='.$prevShellVerbosity[2])); + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output): int + { + if ($input->hasParameterOption(['--version', '-V'], true)) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + try { + // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument. + $input->bind($this->getDefinition()); + } catch (ExceptionInterface) { + // Errors must be ignored, full binding/validation happens later when the command is known. + } + + $name = $this->getCommandName($input); + if ($input->hasParameterOption(['--help', '-h'], true)) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(['command_name' => $this->defaultCommand]); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $definition = $this->getDefinition(); + $definition->setArguments(array_merge( + $definition->getArguments(), + [ + 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), + ] + )); + } + + try { + $this->runningCommand = null; + // the command name MUST be the first element of the input + $command = $this->find($name); + } catch (\Throwable $e) { + if (($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) && 1 === \count($alternatives = $e->getAlternatives()) && $input->isInteractive()) { + $alternative = $alternatives[0]; + + $style = new SymfonyStyle($input, $output); + $output->writeln(''); + $formattedBlock = (new FormatterHelper())->formatBlock(\sprintf('Command "%s" is not defined.', $name), 'error', true); + $output->writeln($formattedBlock); + if (!$style->confirm(\sprintf('Do you want to run "%s" instead? ', $alternative), false)) { + if (null !== $this->dispatcher) { + $event = new ConsoleErrorEvent($input, $output, $e); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + + return $event->getExitCode(); + } + + return 1; + } + + $command = $this->find($alternative); + } else { + if (null !== $this->dispatcher) { + $event = new ConsoleErrorEvent($input, $output, $e); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + + if (0 === $event->getExitCode()) { + return 0; + } + + $e = $event->getError(); + } + + try { + if ($e instanceof CommandNotFoundException && $namespace = $this->findNamespace($name)) { + $helper = new DescriptorHelper(); + $helper->describe($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output, $this, [ + 'format' => 'txt', + 'raw_text' => false, + 'namespace' => $namespace, + 'short' => false, + ]); + + return isset($event) ? $event->getExitCode() : 1; + } + + throw $e; + } catch (NamespaceNotFoundException) { + throw $e; + } + } + } + + if ($command instanceof LazyCommand) { + $command = $command->getCommand(); + } + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + public function reset(): void + { + } + + public function setHelperSet(HelperSet $helperSet): void + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + */ + public function getHelperSet(): HelperSet + { + return $this->helperSet ??= $this->getDefaultHelperSet(); + } + + public function setDefinition(InputDefinition $definition): void + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + */ + public function getDefinition(): InputDefinition + { + $this->definition ??= $this->getDefaultInputDefinition(); + + if ($this->singleCommand) { + $this->definition->setArguments(); + } + + return $this->definition; + } + + /** + * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). + */ + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ( + CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() + && 'command' === $input->getCompletionName() + ) { + foreach ($this->all() as $name => $command) { + // skip hidden commands and aliased commands as they already get added below + if ($command->isHidden() || $command->getName() !== $name) { + continue; + } + $suggestions->suggestValue(new Suggestion($command->getName(), $command->getDescription())); + foreach ($command->getAliases() as $name) { + $suggestions->suggestValue(new Suggestion($name, $command->getDescription())); + } + } + + return; + } + + if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) { + $suggestions->suggestOptions($this->getDefinition()->getOptions()); + } + + if ( + CompletionInput::TYPE_OPTION_VALUE === $input->getCompletionType() + && ($definition = $this->getDefinition())->hasOption($input->getCompletionName()) + ) { + $definition->getOption($input->getCompletionName())->complete($input, $suggestions); + + return; + } + } + + /** + * Gets the help message. + */ + public function getHelp(): string + { + return $this->getLongVersion(); + } + + /** + * Gets whether to catch exceptions or not during commands execution. + */ + public function areExceptionsCaught(): bool + { + return $this->catchExceptions; + } + + /** + * Sets whether to catch exceptions or not during commands execution. + */ + public function setCatchExceptions(bool $boolean): void + { + $this->catchExceptions = $boolean; + } + + /** + * Sets whether to catch errors or not during commands execution. + */ + public function setCatchErrors(bool $catchErrors = true): void + { + $this->catchErrors = $catchErrors; + } + + /** + * Gets whether to automatically exit after a command execution or not. + */ + public function isAutoExitEnabled(): bool + { + return $this->autoExit; + } + + /** + * Sets whether to automatically exit after a command execution or not. + */ + public function setAutoExit(bool $boolean): void + { + $this->autoExit = $boolean; + } + + /** + * Gets the name of the application. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Sets the application name. + */ + public function setName(string $name): void + { + $this->name = $name; + } + + /** + * Gets the application version. + */ + public function getVersion(): string + { + return $this->version; + } + + /** + * Sets the application version. + */ + public function setVersion(string $version): void + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + */ + public function getLongVersion(): string + { + if ('UNKNOWN' !== $this->getName()) { + if ('UNKNOWN' !== $this->getVersion()) { + return \sprintf('%s %s', $this->getName(), $this->getVersion()); + } + + return $this->getName(); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + */ + public function register(string $name): Command + { + return $this->addCommand(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * If a Command is not enabled it will not be added. + * + * @param callable[]|Command[] $commands An array of commands + */ + public function addCommands(array $commands): void + { + foreach ($commands as $command) { + $this->addCommand($command); + } + } + + /** + * @deprecated since Symfony 7.4, use Application::addCommand() instead + */ + public function add(Command $command): ?Command + { + trigger_deprecation('symfony/console', '7.4', 'The "%s()" method is deprecated and will be removed in Symfony 8.0, use "%s::addCommand()" instead.', __METHOD__, self::class); + + return $this->addCommand($command); + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * If the command is not enabled it will not be added. + */ + public function addCommand(callable|Command $command): ?Command + { + $this->init(); + + if (!$command instanceof Command) { + $command = new Command(null, $command); + } + + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return null; + } + + if (!$command instanceof LazyCommand) { + // Will throw if the command is not correctly initialized. + $command->getDefinition(); + } + + if (!$command->getName()) { + throw new LogicException(\sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @throws CommandNotFoundException When given command name does not exist + */ + public function get(string $name): Command + { + $this->init(); + + if (!$this->has($name)) { + throw new CommandNotFoundException(\sprintf('The command "%s" does not exist.', $name)); + } + + // When the command has a different name than the one used at the command loader level + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(\sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + */ + public function has(string $name): bool + { + $this->init(); + + return isset($this->commands[$name]) || ($this->commandLoader?->has($name) && $this->addCommand($this->commandLoader->get($name))); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not return the global namespace which always exists. + * + * @return string[] + */ + public function getNamespaces(): array + { + $namespaces = []; + foreach ($this->all() as $command) { + if ($command->isHidden()) { + continue; + } + + $namespaces[] = $this->extractAllNamespaces($command->getName()); + + foreach ($command->getAliases() as $alias) { + $namespaces[] = $this->extractAllNamespaces($alias); + } + } + + return array_values(array_unique(array_filter(array_merge([], ...$namespaces)))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @throws NamespaceNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace(string $namespace): string + { + $allNamespaces = $this->getNamespaces(); + $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $namespace))).'[^:]*'; + $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); + + if (!$namespaces) { + $message = \sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == \count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new NamespaceNotFoundException($message, $alternatives); + } + + $exact = \in_array($namespace, $namespaces, true); + if (\count($namespaces) > 1 && !$exact) { + throw new NamespaceNotFoundException(\sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find(string $name): Command + { + $this->init(); + + $aliases = []; + + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + if (!$this->has($alias)) { + $this->commands[$alias] = $command; + } + } + } + + if ($this->has($name)) { + return $this->get($name); + } + + $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); + $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*'; + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (!$commands) { + $commands = preg_grep('{^'.$expr.'}i', $allCommands); + } + + // if no commands matched or we just matched namespaces + if (!$commands || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = \sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + $wantHelps = $this->wantHelps; + $this->wantHelps = false; + + // remove hidden commands + if ($alternatives = array_filter($alternatives, fn ($name) => !$this->get($name)->isHidden())) { + $message .= \sprintf("\n\nDid you mean %s?\n %s", 1 === \count($alternatives) ? 'this' : 'one of these', implode("\n ", $alternatives)); + } + $this->wantHelps = $wantHelps; + } + + throw new CommandNotFoundException($message, array_values($alternatives)); + } + + // filter out aliases for commands which are already on the list + if (\count($commands) > 1) { + $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; + $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) { + if (!$commandList[$nameOrAlias] instanceof Command) { + $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias); + } + + $commandName = $commandList[$nameOrAlias]->getName(); + + $aliases[$nameOrAlias] = $commandName; + + return $commandName === $nameOrAlias || !\in_array($commandName, $commands, true); + })); + } + + // check whether all commands left are aliases to the same one + if (\count($commands) > 1) { + $uniqueCommands = array_unique(array_map(function ($nameOrAlias) use (&$commandList) { + if (!$commandList[$nameOrAlias] instanceof Command) { + $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias); + } + + return $commandList[$nameOrAlias]->getName(); + }, $commands)); + + if (1 === \count($uniqueCommands)) { + $commands = [reset($uniqueCommands)]; + } + } + + if (\count($commands) > 1) { + $usableWidth = $this->terminal->getWidth() - 10; + $abbrevs = array_values($commands); + $maxLen = 0; + foreach ($abbrevs as $abbrev) { + $maxLen = max(Helper::width($abbrev), $maxLen); + } + $abbrevs = array_map(static function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) { + if ($commandList[$cmd]->isHidden()) { + unset($commands[array_search($cmd, $commands)]); + + return false; + } + + $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription(); + + return Helper::width($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev; + }, array_values($commands)); + + if (\count($commands) > 1) { + $suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs)); + + throw new CommandNotFoundException(\sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands)); + } + } + + $command = $commands ? $this->get(reset($commands)) : null; + + if (!$command || $command->isHidden()) { + throw new CommandNotFoundException(\sprintf('The command "%s" does not exist.', $name)); + } + + return $command; + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @return Command[] + */ + public function all(?string $namespace = null): array + { + $this->init(); + + if (null === $namespace) { + if (!$this->commandLoader) { + return $this->commands; + } + + $commands = $this->commands; + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + + return $commands; + } + + $commands = []; + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + if ($this->commandLoader) { + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @return string[][] + */ + public static function getAbbreviations(array $names): array + { + $abbrevs = []; + foreach ($names as $name) { + for ($len = \strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + public function renderThrowable(\Throwable $e, OutputInterface $output): void + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + $this->doRenderThrowable($e, $output); + + if (null !== $this->runningCommand) { + $output->writeln(\sprintf('%s', OutputFormatter::escape(\sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void + { + do { + $message = trim($e->getMessage()); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $class = get_debug_type($e); + $title = \sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : ''); + $len = Helper::width($title); + } else { + $len = 0; + } + + if (str_contains($message, "@anonymous\0")) { + $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)?[0-9a-fA-F]++/', static fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $message); + } + + $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX; + $lines = []; + foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = Helper::width($line) + 4; + $lines[] = [$line, $lineLength]; + + $len = max($lineLength, $len); + } + } + + $messages = []; + if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = \sprintf('%s', OutputFormatter::escape(\sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); + } + $messages[] = $emptyLine = \sprintf('%s', str_repeat(' ', $len)); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = \sprintf('%s%s', $title, str_repeat(' ', max(0, $len - Helper::width($title)))); + } + foreach ($lines as $line) { + $messages[] = \sprintf(' %s %s', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + + array_unshift($trace, [ + 'function' => '', + 'file' => $e->getFile() ?: 'n/a', + 'line' => $e->getLine() ?: 'n/a', + 'args' => [], + ]); + + for ($i = 0, $count = \count($trace); $i < $count; ++$i) { + $class = $trace[$i]['class'] ?? ''; + $type = $trace[$i]['type'] ?? ''; + $function = $trace[$i]['function'] ?? ''; + $file = $trace[$i]['file'] ?? 'n/a'; + $line = $trace[$i]['line'] ?? 'n/a'; + + $output->writeln(\sprintf(' %s%s at %s:%s', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + } + + /** + * Configures the input and output instances based on the user arguments and options. + */ + protected function configureIO(InputInterface $input, OutputInterface $output): void + { + if ($input->hasParameterOption(['--ansi'], true)) { + $output->setDecorated(true); + } elseif ($input->hasParameterOption(['--no-ansi'], true)) { + $output->setDecorated(false); + } + + $shellVerbosity = match (true) { + $input->hasParameterOption(['--silent'], true) => -2, + $input->hasParameterOption(['--quiet', '-q'], true) => -1, + $input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true) => 3, + $input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true) => 2, + $input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true) => 1, + default => (int) ($_ENV['SHELL_VERBOSITY'] ?? $_SERVER['SHELL_VERBOSITY'] ?? getenv('SHELL_VERBOSITY')), + }; + + $output->setVerbosity(match ($shellVerbosity) { + -2 => OutputInterface::VERBOSITY_SILENT, + -1 => OutputInterface::VERBOSITY_QUIET, + 1 => OutputInterface::VERBOSITY_VERBOSE, + 2 => OutputInterface::VERBOSITY_VERY_VERBOSE, + 3 => OutputInterface::VERBOSITY_DEBUG, + default => ($shellVerbosity = 0) ?: $output->getVerbosity(), + }); + + if (0 > $shellVerbosity || $input->hasParameterOption(['--no-interaction', '-n'], true)) { + $input->setInteractive(false); + } + + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY='.$shellVerbosity); + } + $_ENV['SHELL_VERBOSITY'] = $shellVerbosity; + $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity; + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @return int 0 if everything went fine, or an error code + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + $registeredSignals = false; + if (($commandSignals = $command->getSubscribedSignals()) || $this->dispatcher && $this->signalsToDispatchEvent) { + $signalRegistry = $this->getSignalRegistry(); + + $registeredSignals = true; + $this->getSignalRegistry()->pushCurrentHandlers(); + + if ($this->dispatcher) { + // We register application signals, so that we can dispatch the event + foreach ($this->signalsToDispatchEvent as $signal) { + $signalEvent = new ConsoleSignalEvent($command, $input, $output, $signal); + $alarmEvent = \SIGALRM === $signal ? new ConsoleAlarmEvent($command, $input, $output) : null; + + $signalRegistry->register($signal, function ($signal) use ($signalEvent, $alarmEvent, $command, $commandSignals, $input, $output) { + $this->dispatcher->dispatch($signalEvent, ConsoleEvents::SIGNAL); + $exitCode = $signalEvent->getExitCode(); + + if (null !== $alarmEvent) { + if (false !== $exitCode) { + $alarmEvent->setExitCode($exitCode); + } else { + $alarmEvent->abortExit(); + } + $this->dispatcher->dispatch($alarmEvent); + $exitCode = $alarmEvent->getExitCode(); + } + + // If the command is signalable, we call the handleSignal() method + if (\in_array($signal, $commandSignals, true)) { + $exitCode = $command->handleSignal($signal, $exitCode); + } + + if (\SIGALRM === $signal) { + $this->scheduleAlarm(); + } + + if (false !== $exitCode) { + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode, $signal); + $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE); + + exit($event->getExitCode()); + } + }); + } + + // then we register command signals, but not if already handled after the dispatcher + $commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent); + } + + foreach ($commandSignals as $signal) { + $signalRegistry->register($signal, function (int $signal) use ($command): void { + if (\SIGALRM === $signal) { + $this->scheduleAlarm(); + } + + if (false !== $exitCode = $command->handleSignal($signal)) { + exit($exitCode); + } + }); + } + } + + if (null === $this->dispatcher) { + try { + return $command->run($input, $output); + } finally { + if ($registeredSignals) { + $this->getSignalRegistry()->popPreviousHandlers(); + } + } + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $e = null; + + try { + $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND); + + if ($event->commandShouldRun()) { + $exitCode = $command->run($input, $output); + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + } catch (\Throwable $e) { + $event = new ConsoleErrorEvent($input, $output, $e, $command); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + $e = $event->getError(); + + if (0 === $exitCode = $event->getExitCode()) { + $e = null; + } + } finally { + if ($registeredSignals) { + $this->getSignalRegistry()->popPreviousHandlers(); + } + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE); + + if (null !== $e) { + throw $e; + } + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + */ + protected function getCommandName(InputInterface $input): ?string + { + return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + */ + protected function getDefaultInputDefinition(): InputDefinition + { + return new InputDefinition([ + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the '.$this->defaultCommand.' command'), + new InputOption('--silent', null, InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Only errors are displayed. All other output is suppressed'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + ]); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] + */ + protected function getDefaultCommands(): array + { + return [new HelpCommand(), new ListCommand(), new CompleteCommand(), new DumpCompletionCommand()]; + } + + /** + * Gets the default helper set with the helpers that should always be available. + */ + protected function getDefaultHelperSet(): HelperSet + { + return new HelperSet([ + new FormatterHelper(), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + ]); + } + + /** + * Returns abbreviated suggestions in string format. + */ + private function getAbbreviationSuggestions(array $abbrevs): string + { + return ' '.implode("\n ", $abbrevs); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + */ + public function extractNamespace(string $name, ?int $limit = null): string + { + $parts = explode(':', $name, -1); + + return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @return string[] + */ + private function findAlternatives(string $name, iterable $collection): array + { + $threshold = 1e3; + $alternatives = []; + + $collectionParts = []; + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + if ($lev <= \strlen($subname) / 3 || '' !== $subname && str_contains($parts[$i], $subname)) { + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, static fn ($lev) => $lev < 2 * $threshold); + ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @return $this + */ + public function setDefaultCommand(string $commandName, bool $isSingleCommand = false): static + { + $this->defaultCommand = explode('|', ltrim($commandName, '|'))[0]; + + if ($isSingleCommand) { + // Ensure the command exist + $this->find($commandName); + + $this->singleCommand = true; + } + + return $this; + } + + /** + * @internal + */ + public function isSingleCommand(): bool + { + return $this->singleCommand; + } + + private function splitStringByWidth(string $string, int $width): array + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // additionally, array_slice() is not enough as some character has doubled width. + // we need a function to split string not by character count but by string width + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = []; + $line = ''; + + $offset = 0; + while (preg_match('/.{1,10000}/u', $utf8String, $m, 0, $offset)) { + $offset += \strlen($m[0]); + + foreach (preg_split('//u', $m[0]) as $char) { + // test if $char could be appended to current line + if (Helper::width($line.$char) <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + } + + $lines[] = \count($lines) ? str_pad($line, $width) : $line; + + return mb_convert_encoding($lines, $encoding, 'utf8'); + } + + /** + * Returns all namespaces of the command name. + * + * @return string[] + */ + private function extractAllNamespaces(string $name): array + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = []; + + foreach ($parts as $part) { + if ($namespaces) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } + + private function init(): void + { + if ($this->initialized) { + return; + } + $this->initialized = true; + + if ((new \ReflectionMethod($this, 'add'))->getDeclaringClass()->getName() !== (new \ReflectionMethod($this, 'addCommand'))->getDeclaringClass()->getName()) { + $adder = $this->add(...); + } else { + $adder = $this->addCommand(...); + } + + foreach ($this->getDefaultCommands() as $command) { + $adder($command); + } + } +} diff --git a/upload/api/vendor/symfony/console/Attribute/Argument.php b/upload/api/vendor/symfony/console/Attribute/Argument.php new file mode 100644 index 00000000..33b7a86b --- /dev/null +++ b/upload/api/vendor/symfony/console/Attribute/Argument.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +use Symfony\Component\Console\Attribute\Reflection\ReflectionMember; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\Suggestion; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\String\UnicodeString; + +#[\Attribute(\Attribute::TARGET_PARAMETER | \Attribute::TARGET_PROPERTY)] +class Argument +{ + private const ALLOWED_TYPES = ['string', 'bool', 'int', 'float', 'array']; + + private string|bool|int|float|array|null $default = null; + private array|\Closure $suggestedValues; + private ?int $mode = null; + /** + * @var string|class-string<\BackedEnum> + */ + private string $typeName = ''; + private ?InteractiveAttributeInterface $interactiveAttribute = null; + + /** + * Represents a console command definition. + * + * If unset, the `name` value will be inferred from the parameter definition. + * + * @param array|callable(CompletionInput):list $suggestedValues The values used for input completion + */ + public function __construct( + public string $description = '', + public string $name = '', + array|callable $suggestedValues = [], + ) { + $this->suggestedValues = \is_callable($suggestedValues) ? $suggestedValues(...) : $suggestedValues; + } + + /** + * @internal + */ + public static function tryFrom(\ReflectionParameter|\ReflectionProperty $member): ?self + { + $reflection = new ReflectionMember($member); + + if (!$self = $reflection->getAttribute(self::class)) { + return null; + } + + $type = $reflection->getType(); + $name = $reflection->getName(); + + if (!$type instanceof \ReflectionNamedType) { + throw new LogicException(\sprintf('The %s "$%s" of "%s" must have a named type. Untyped, Union or Intersection types are not supported for command arguments.', $reflection->getMemberName(), $name, $reflection->getSourceName())); + } + + $self->typeName = $type->getName(); + $isBackedEnum = is_subclass_of($self->typeName, \BackedEnum::class); + + if (!\in_array($self->typeName, self::ALLOWED_TYPES, true) && !$isBackedEnum) { + throw new LogicException(\sprintf('The type "%s" on %s "$%s" of "%s" is not supported as a command argument. Only "%s" types and backed enums are allowed.', $self->typeName, $reflection->getMemberName(), $name, $reflection->getSourceName(), implode('", "', self::ALLOWED_TYPES))); + } + + if (!$self->name) { + $self->name = (new UnicodeString($name))->kebab(); + } + + $self->default = $reflection->hasDefaultValue() ? $reflection->getDefaultValue() : null; + + $isOptional = $reflection->hasDefaultValue() || $reflection->isNullable(); + $self->mode = $isOptional ? InputArgument::OPTIONAL : InputArgument::REQUIRED; + if ('array' === $self->typeName) { + $self->mode |= InputArgument::IS_ARRAY; + } + + if (\is_array($self->suggestedValues) && !\is_callable($self->suggestedValues) && 2 === \count($self->suggestedValues) && ($instance = $reflection->getSourceThis()) && $instance::class === $self->suggestedValues[0] && \is_callable([$instance, $self->suggestedValues[1]])) { + // In case that the callback is declared as a static method `[Foo::class, 'methodName']` - yet it is not callable, + // while non-static method `[Foo $instance, 'methodName']` would be callable, we transform the callback on the fly into a non-static version. + $self->suggestedValues = [$instance, $self->suggestedValues[1]]; + } + + if ($isBackedEnum && !$self->suggestedValues) { + $self->suggestedValues = array_column($self->typeName::cases(), 'value'); + } + + $self->interactiveAttribute = Ask::tryFrom($member, $self->name); + + if ($self->interactiveAttribute && $isOptional) { + throw new LogicException(\sprintf('The %s "$%s" argument of "%s" cannot be both interactive and optional.', $reflection->getMemberName(), $self->name, $reflection->getSourceName())); + } + + return $self; + } + + /** + * @internal + */ + public function toInputArgument(): InputArgument + { + $suggestedValues = \is_callable($this->suggestedValues) ? ($this->suggestedValues)(...) : $this->suggestedValues; + + return new InputArgument($this->name, $this->mode, $this->description, $this->default, $suggestedValues); + } + + /** + * @internal + */ + public function resolveValue(InputInterface $input): mixed + { + $value = $input->getArgument($this->name); + + if (is_subclass_of($this->typeName, \BackedEnum::class) && (\is_string($value) || \is_int($value))) { + return $this->typeName::tryFrom($value) ?? throw InvalidArgumentException::fromEnumValue($this->name, $value, $this->suggestedValues); + } + + return $value; + } + + /** + * @internal + */ + public function getInteractiveAttribute(): ?InteractiveAttributeInterface + { + return $this->interactiveAttribute; + } + + /** + * @internal + */ + public function isRequired(): bool + { + return InputArgument::REQUIRED === (InputArgument::REQUIRED & $this->mode); + } +} diff --git a/upload/api/vendor/symfony/console/Attribute/AsCommand.php b/upload/api/vendor/symfony/console/Attribute/AsCommand.php new file mode 100644 index 00000000..02f15620 --- /dev/null +++ b/upload/api/vendor/symfony/console/Attribute/AsCommand.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +/** + * Service tag to autoconfigure commands. + * + * @final since Symfony 7.3 + */ +#[\Attribute(\Attribute::TARGET_CLASS)] +class AsCommand +{ + /** + * @param string $name The name of the command, used when calling it (i.e. "cache:clear") + * @param string|null $description The description of the command, displayed with the help page + * @param string[] $aliases The list of aliases of the command. The command will be executed when using one of them (i.e. "cache:clean") + * @param bool $hidden If true, the command won't be shown when listing all the available commands, but it can still be run as any other command + * @param string|null $help The help content of the command, displayed with the help page + * @param string[] $usages The list of usage examples, displayed with the help page + */ + public function __construct( + public string $name, + public ?string $description = null, + array $aliases = [], + bool $hidden = false, + public ?string $help = null, + public array $usages = [], + ) { + if (!$hidden && !$aliases) { + return; + } + + $name = explode('|', $name); + $name = array_merge($name, $aliases); + + if ($hidden && '' !== $name[0]) { + array_unshift($name, ''); + } + + $this->name = implode('|', $name); + } +} diff --git a/upload/api/vendor/symfony/console/Attribute/Ask.php b/upload/api/vendor/symfony/console/Attribute/Ask.php new file mode 100644 index 00000000..771d90ad --- /dev/null +++ b/upload/api/vendor/symfony/console/Attribute/Ask.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +use Symfony\Component\Console\Attribute\Reflection\ReflectionMember; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +#[\Attribute(\Attribute::TARGET_PARAMETER | \Attribute::TARGET_PROPERTY)] +class Ask implements InteractiveAttributeInterface +{ + public ?\Closure $normalizer; + public ?\Closure $validator; + private \Closure $closure; + + /** + * @param string $question The question to ask the user + * @param string|bool|int|float|null $default The default answer to return if the user enters nothing + * @param bool $hidden Whether the user response must be hidden or not + * @param bool $multiline Whether the user response should accept newline characters + * @param bool $trimmable Whether the user response must be trimmed or not + * @param int|null $timeout The maximum time the user has to answer the question in seconds + * @param callable|null $validator The validator for the question + * @param int|null $maxAttempts The maximum number of attempts allowed to answer the question. + * Null means an unlimited number of attempts + */ + public function __construct( + public string $question, + public string|bool|int|float|null $default = null, + public bool $hidden = false, + public bool $multiline = false, + public bool $trimmable = true, + public ?int $timeout = null, + ?callable $normalizer = null, + ?callable $validator = null, + public ?int $maxAttempts = null, + ) { + $this->normalizer = $normalizer ? $normalizer(...) : null; + $this->validator = $validator ? $validator(...) : null; + } + + /** + * @internal + */ + public static function tryFrom(\ReflectionParameter|\ReflectionProperty $member, string $name): ?self + { + $reflection = new ReflectionMember($member); + + if (!$self = $reflection->getAttribute(self::class)) { + return null; + } + + $type = $reflection->getType(); + + if (!$type instanceof \ReflectionNamedType) { + throw new LogicException(\sprintf('The %s "$%s" of "%s" must have a named type. Untyped, Union or Intersection types are not supported for interactive questions.', $reflection->getMemberName(), $name, $reflection->getSourceName())); + } + + if ('array' === $type->getName() && null !== $self->default) { + throw new LogicException(\sprintf('The "%s::$default" value is not supported for the array "$%s" of "%s", because an empty answer ends the collection.', self::class, $name, $reflection->getSourceName())); + } + + $self->closure = function (SymfonyStyle $io, InputInterface $input) use ($self, $reflection, $name, $type) { + if ($reflection->isProperty() && isset($this->{$reflection->getName()})) { + return; + } + + if ($reflection->isParameter() && !\in_array($input->getArgument($name), [null, []], true)) { + return; + } + + if ('bool' === $type->getName()) { + $self->default ??= false; + + if (!\is_bool($self->default)) { + throw new LogicException(\sprintf('The "%s::$default" value for the %s "$%s" of "%s" must be a boolean.', self::class, $reflection->getMemberName(), $name, $reflection->getSourceName())); + } + + $question = new ConfirmationQuestion($self->question, $self->default); + } else { + $question = new Question($self->question, $self->default); + } + $question->setHidden($self->hidden); + $question->setMultiline($self->multiline); + $question->setTrimmable($self->trimmable); + $question->setTimeout($self->timeout); + + if (!$self->validator && $reflection->isProperty() && 'array' !== $type->getName()) { + $self->validator = fn (mixed $value): mixed => $this->{$reflection->getName()} = $value; + } + + $question->setValidator($self->validator); + $question->setMaxAttempts($self->maxAttempts); + + if ($self->normalizer) { + $question->setNormalizer($self->normalizer); + } elseif (is_subclass_of($type->getName(), \BackedEnum::class)) { + /** @var class-string<\BackedEnum> $backedType */ + $backedType = $reflection->getType()->getName(); + $question->setNormalizer(static fn (string|int $value) => $backedType::tryFrom($value) ?? throw InvalidArgumentException::fromEnumValue($reflection->getName(), $value, array_column($backedType::cases(), 'value'))); + } + + if ('array' === $type->getName()) { + $value = []; + while (null !== $v = $io->askQuestion($question)) { + if ($question->isTrimmable()) { + $v = trim($v); + } + if ("\x4" === $v || \PHP_EOL === $v || '' === $v) { + break; + } + $value[] = $v; + } + } else { + $value = $io->askQuestion($question); + } + + if (null === $value && !$reflection->isNullable()) { + return; + } + + if ($reflection->isProperty()) { + $this->{$reflection->getName()} = $value; + } else { + $input->setArgument($name, $value); + } + }; + + return $self; + } + + /** + * @internal + */ + public function getFunction(object $instance): \ReflectionFunction + { + return new \ReflectionFunction($this->closure->bindTo($instance, $instance::class)); + } +} diff --git a/upload/api/vendor/symfony/console/Attribute/Interact.php b/upload/api/vendor/symfony/console/Attribute/Interact.php new file mode 100644 index 00000000..188071ff --- /dev/null +++ b/upload/api/vendor/symfony/console/Attribute/Interact.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +use Symfony\Component\Console\Exception\LogicException; + +#[\Attribute(\Attribute::TARGET_METHOD)] +class Interact implements InteractiveAttributeInterface +{ + private \ReflectionMethod $method; + + /** + * @internal + */ + public static function tryFrom(\ReflectionMethod $method): ?self + { + /** @var self|null $self */ + if (!$self = ($method->getAttributes(self::class)[0] ?? null)?->newInstance()) { + return null; + } + + if (!$method->isPublic() || $method->isStatic()) { + throw new LogicException(\sprintf('The interactive method "%s::%s()" must be public and non-static.', $method->getDeclaringClass()->getName(), $method->getName())); + } + + if ('__invoke' === $method->getName()) { + throw new LogicException(\sprintf('The "%s::__invoke()" method cannot be used as an interactive method.', $method->getDeclaringClass()->getName())); + } + + $self->method = $method; + + return $self; + } + + /** + * @internal + */ + public function getFunction(object $instance): \ReflectionFunction + { + return new \ReflectionFunction($this->method->getClosure($instance)); + } +} diff --git a/upload/api/vendor/symfony/console/Attribute/InteractiveAttributeInterface.php b/upload/api/vendor/symfony/console/Attribute/InteractiveAttributeInterface.php new file mode 100644 index 00000000..0b02d7d2 --- /dev/null +++ b/upload/api/vendor/symfony/console/Attribute/InteractiveAttributeInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +/** + * @internal + */ +interface InteractiveAttributeInterface +{ + public function getFunction(object $instance): \ReflectionFunction; +} diff --git a/upload/api/vendor/symfony/console/Attribute/MapInput.php b/upload/api/vendor/symfony/console/Attribute/MapInput.php new file mode 100644 index 00000000..0b508361 --- /dev/null +++ b/upload/api/vendor/symfony/console/Attribute/MapInput.php @@ -0,0 +1,188 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +use Symfony\Component\Console\Attribute\Reflection\ReflectionMember; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Interaction\Interaction; + +/** + * Maps a command input into an object (DTO). + */ +#[\Attribute(\Attribute::TARGET_PARAMETER | \Attribute::TARGET_PROPERTY)] +final class MapInput +{ + /** + * @var array + */ + private array $definition = []; + + private \ReflectionClass $class; + + /** + * @var list + */ + private array $interactiveAttributes = []; + + /** + * @internal + */ + public static function tryFrom(\ReflectionParameter|\ReflectionProperty $member): ?self + { + $reflection = new ReflectionMember($member); + + if (!$self = $reflection->getAttribute(self::class)) { + return null; + } + + $type = $reflection->getType(); + + if (!$type instanceof \ReflectionNamedType) { + throw new LogicException(\sprintf('The input %s "%s" must have a named type.', $reflection->getMemberName(), $member->name)); + } + + if (!class_exists($class = $type->getName())) { + throw new LogicException(\sprintf('The input class "%s" does not exist.', $type->getName())); + } + + $self->class = new \ReflectionClass($class); + + foreach ($self->class->getProperties() as $property) { + if ($argument = Argument::tryFrom($property)) { + $self->definition[$property->name] = $argument; + } elseif ($option = Option::tryFrom($property)) { + $self->definition[$property->name] = $option; + } elseif ($input = self::tryFrom($property)) { + $self->definition[$property->name] = $input; + } + + if (isset($self->definition[$property->name]) && (!$property->isPublic() || $property->isStatic())) { + throw new LogicException(\sprintf('The input property "%s::$%s" must be public and non-static.', $self->class->name, $property->name)); + } + } + + if (!$self->definition) { + throw new LogicException(\sprintf('The input class "%s" must have at least one argument or option.', $self->class->name)); + } + + foreach ($self->class->getMethods() as $method) { + if ($attribute = Interact::tryFrom($method)) { + $self->interactiveAttributes[] = $attribute; + } + } + + return $self; + } + + /** + * @internal + */ + public function resolveValue(InputInterface $input): object + { + $instance = $this->class->newInstanceWithoutConstructor(); + + foreach ($this->definition as $name => $spec) { + // ignore required arguments that are not set yet (may happen in interactive mode) + if ($spec instanceof Argument && $spec->isRequired() && \in_array($input->getArgument($spec->name), [null, []], true)) { + continue; + } + + $instance->$name = $spec->resolveValue($input); + } + + return $instance; + } + + /** + * @internal + */ + public function setValue(InputInterface $input, object $object): void + { + foreach ($this->definition as $name => $spec) { + $property = $this->class->getProperty($name); + + if (!$property->isInitialized($object) || \in_array($value = $property->getValue($object), [null, []], true)) { + continue; + } + + match (true) { + $spec instanceof Argument => $input->setArgument($spec->name, $value), + $spec instanceof Option => $input->setOption($spec->name, $value), + $spec instanceof self => $spec->setValue($input, $value), + default => throw new LogicException('Unexpected specification type.'), + }; + } + } + + /** + * @return iterable + */ + public function getArguments(): iterable + { + foreach ($this->definition as $spec) { + if ($spec instanceof Argument) { + yield $spec; + } elseif ($spec instanceof self) { + yield from $spec->getArguments(); + } + } + } + + /** + * @return iterable