diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f1e858b0c70..5d1a10e15c3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -60,7 +60,7 @@ jobs: tools: none - name: Run PHP-CS-Fixer - run: ./tools/php-cs-fixer fix --dry-run --show-progress=dots --using-cache=no --verbose + run: ./tools/php-cs-fixer check --show-progress=dots --using-cache=no --verbose static-analysis: name: Static Analysis diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index e6c9f823eff..4f5fa13f07b 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -8,6 +8,8 @@ file that was distributed with this source code. EOF; +require __DIR__ . '/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/bootstrap.php'; + $finder = PhpCsFixer\Finder::create() ->files() ->in(__DIR__ . '/src') @@ -29,6 +31,7 @@ $config = new PhpCsFixer\Config; $config->setFinder($finder) + ->registerCustomFixers(new PhpCsFixerCustomFixers\Fixers) ->setRiskyAllowed(true) ->setRules([ 'align_multiline_comment' => true, @@ -371,6 +374,11 @@ ], 'void_return' => true, 'whitespace_after_comma_in_array' => true, + PhpCsFixerCustomFixers\Fixer\PhpdocTagNoNamedArgumentsFixer::name() => [ + 'directory' => __DIR__ . '/src/', + 'description' => 'Parameter names are not covered by the backward compatibility promise for PHPUnit' + ] + ]); $config->setCacheFile(__DIR__ . '/.php-cs-fixer.cache/' . json_decode((string) @file_get_contents('composer.json'), true)["extra"]["branch-alias"]["dev-main"] ?? 'unknown'); diff --git a/src/Event/Exception/Exception.php b/src/Event/Exception/Exception.php index 88b526d208c..25bf06c6b39 100644 --- a/src/Event/Exception/Exception.php +++ b/src/Event/Exception/Exception.php @@ -9,6 +9,9 @@ */ namespace PHPUnit\Event; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ interface Exception extends \PHPUnit\Exception { } diff --git a/src/Exception.php b/src/Exception.php index 21721b70f67..7a8302e20d0 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -11,6 +11,9 @@ use Throwable; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ interface Exception extends Throwable { } diff --git a/src/Framework/TestRunner/ChildProcessResultProcessor.php b/src/Framework/TestRunner/ChildProcessResultProcessor.php index ae2ded385ae..466ff33bc78 100644 --- a/src/Framework/TestRunner/ChildProcessResultProcessor.php +++ b/src/Framework/TestRunner/ChildProcessResultProcessor.php @@ -19,6 +19,9 @@ use PHPUnit\Runner\CodeCoverage; use PHPUnit\TestRunner\TestResult\PassedTests; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ final readonly class ChildProcessResultProcessor { private Facade $eventFacade; diff --git a/src/Metadata/Exception/Exception.php b/src/Metadata/Exception/Exception.php index 0faa9b7c0b1..5d562f1a2fa 100644 --- a/src/Metadata/Exception/Exception.php +++ b/src/Metadata/Exception/Exception.php @@ -9,6 +9,9 @@ */ namespace PHPUnit\Metadata; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ interface Exception extends \PHPUnit\Exception { } diff --git a/src/Metadata/Exception/InvalidVersionRequirementException.php b/src/Metadata/Exception/InvalidVersionRequirementException.php index cb82e841187..359f723c1dc 100644 --- a/src/Metadata/Exception/InvalidVersionRequirementException.php +++ b/src/Metadata/Exception/InvalidVersionRequirementException.php @@ -11,6 +11,9 @@ use RuntimeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ final class InvalidVersionRequirementException extends RuntimeException implements Exception { } diff --git a/src/Metadata/Exception/NoVersionRequirementException.php b/src/Metadata/Exception/NoVersionRequirementException.php index 8351067962a..299652cf0ab 100644 --- a/src/Metadata/Exception/NoVersionRequirementException.php +++ b/src/Metadata/Exception/NoVersionRequirementException.php @@ -11,6 +11,9 @@ use RuntimeException; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ final class NoVersionRequirementException extends RuntimeException implements Exception { } diff --git a/src/TextUI/Output/Default/UnexpectedOutputPrinter.php b/src/TextUI/Output/Default/UnexpectedOutputPrinter.php index af669906c18..d8fe3a191f8 100644 --- a/src/TextUI/Output/Default/UnexpectedOutputPrinter.php +++ b/src/TextUI/Output/Default/UnexpectedOutputPrinter.php @@ -16,6 +16,9 @@ use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\TextUI\Output\Printer; +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + */ final readonly class UnexpectedOutputPrinter implements PrintedUnexpectedOutputSubscriber { private Printer $printer; diff --git a/tools/.php-cs-fixer/composer.json b/tools/.php-cs-fixer/composer.json new file mode 100644 index 00000000000..e18c70d239c --- /dev/null +++ b/tools/.php-cs-fixer/composer.json @@ -0,0 +1,6 @@ +{ + "require-dev": { + "kubawerlos/php-cs-fixer-custom-fixers": "^3.27", + "php-cs-fixer/shim": "^3.75" + } +} diff --git a/tools/.php-cs-fixer/composer.lock b/tools/.php-cs-fixer/composer.lock new file mode 100644 index 00000000000..4133b5da751 --- /dev/null +++ b/tools/.php-cs-fixer/composer.lock @@ -0,0 +1,123 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "0d921fb33403a79910ac9d59a7d8fb57", + "packages": [], + "packages-dev": [ + { + "name": "kubawerlos/php-cs-fixer-custom-fixers", + "version": "v3.27.0", + "source": { + "type": "git", + "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", + "reference": "d860473d16b906c7945206177edc7d112357a706" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/d860473d16b906c7945206177edc7d112357a706", + "reference": "d860473d16b906c7945206177edc7d112357a706", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-tokenizer": "*", + "friendsofphp/php-cs-fixer": "^3.61.1", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6.22 || 10.5.45 || ^11.5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpCsFixerCustomFixers\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kuba Werłos", + "email": "werlos@gmail.com" + } + ], + "description": "A set of custom fixers for PHP CS Fixer", + "support": { + "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.27.0" + }, + "funding": [ + { + "url": "https://github.com/kubawerlos", + "type": "github" + } + ], + "time": "2025-06-10T20:53:07+00:00" + }, + { + "name": "php-cs-fixer/shim", + "version": "v3.75.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "eea219a577085bd13ff0cb644a422c20798316c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/eea219a577085bd13ff0cb644a422c20798316c7", + "reference": "eea219a577085bd13ff0cb644a422c20798316c7", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.75.0" + }, + "time": "2025-03-31T18:45:02+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/tools/.php-cs-fixer/vendor/autoload.php b/tools/.php-cs-fixer/vendor/autoload.php new file mode 100644 index 00000000000..d9a23e905f5 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/autoload.php @@ -0,0 +1,22 @@ +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/php-cs-fixer/shim/php-cs-fixer'); + } +} + +return include __DIR__ . '/..'.'/php-cs-fixer/shim/php-cs-fixer'; diff --git a/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.bat b/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.bat new file mode 100755 index 00000000000..ec8869f156c --- /dev/null +++ b/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.bat @@ -0,0 +1,5 @@ +@ECHO OFF +setlocal DISABLEDELAYEDEXPANSION +SET BIN_TARGET=%~dp0/php-cs-fixer +SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 +php "%BIN_TARGET%" %* diff --git a/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.phar b/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.phar new file mode 100755 index 00000000000..a1e1ffe6aa8 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.phar @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/php-cs-fixer/shim/php-cs-fixer.phar'); + } +} + +return include __DIR__ . '/..'.'/php-cs-fixer/shim/php-cs-fixer.phar'; diff --git a/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.phar.bat b/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.phar.bat new file mode 100755 index 00000000000..c69ff410c4b --- /dev/null +++ b/tools/.php-cs-fixer/vendor/bin/php-cs-fixer.phar.bat @@ -0,0 +1,5 @@ +@ECHO OFF +setlocal DISABLEDELAYEDEXPANSION +SET BIN_TARGET=%~dp0/php-cs-fixer.phar +SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 +php "%BIN_TARGET%" %* diff --git a/tools/.php-cs-fixer/vendor/composer/ClassLoader.php b/tools/.php-cs-fixer/vendor/composer/ClassLoader.php new file mode 100644 index 00000000000..7824d8f7eaf --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/ClassLoader.php @@ -0,0 +1,579 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/tools/.php-cs-fixer/vendor/composer/InstalledVersions.php b/tools/.php-cs-fixer/vendor/composer/InstalledVersions.php new file mode 100644 index 00000000000..2052022fd8e --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/InstalledVersions.php @@ -0,0 +1,396 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool + */ + private static $installedIsLocalDir; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + $copiedLocalDir = false; + + if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; + } + } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array() && !$copiedLocalDir) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/tools/.php-cs-fixer/vendor/composer/LICENSE b/tools/.php-cs-fixer/vendor/composer/LICENSE new file mode 100644 index 00000000000..f27399a042d --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +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/tools/.php-cs-fixer/vendor/composer/autoload_classmap.php b/tools/.php-cs-fixer/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000000..0fb0a2c194b --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/composer/InstalledVersions.php', +); diff --git a/tools/.php-cs-fixer/vendor/composer/autoload_namespaces.php b/tools/.php-cs-fixer/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000000..15a2ff3ad6d --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/kubawerlos/php-cs-fixer-custom-fixers/src'), +); diff --git a/tools/.php-cs-fixer/vendor/composer/autoload_real.php b/tools/.php-cs-fixer/vendor/composer/autoload_real.php new file mode 100644 index 00000000000..32e8a5689d2 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/autoload_real.php @@ -0,0 +1,36 @@ +register(true); + + return $loader; + } +} diff --git a/tools/.php-cs-fixer/vendor/composer/autoload_static.php b/tools/.php-cs-fixer/vendor/composer/autoload_static.php new file mode 100644 index 00000000000..0eb1d20ece4 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/autoload_static.php @@ -0,0 +1,36 @@ + + array ( + 'PhpCsFixerCustomFixers\\' => 23, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'PhpCsFixerCustomFixers\\' => + array ( + 0 => __DIR__ . '/..' . '/kubawerlos/php-cs-fixer-custom-fixers/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit3457f0272a028217b66ee19b6e4c5737::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit3457f0272a028217b66ee19b6e4c5737::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit3457f0272a028217b66ee19b6e4c5737::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/tools/.php-cs-fixer/vendor/composer/installed.json b/tools/.php-cs-fixer/vendor/composer/installed.json new file mode 100644 index 00000000000..4d85a584a88 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/installed.json @@ -0,0 +1,119 @@ +{ + "packages": [ + { + "name": "kubawerlos/php-cs-fixer-custom-fixers", + "version": "v3.27.0", + "version_normalized": "3.27.0.0", + "source": { + "type": "git", + "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", + "reference": "d860473d16b906c7945206177edc7d112357a706" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/d860473d16b906c7945206177edc7d112357a706", + "reference": "d860473d16b906c7945206177edc7d112357a706", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-tokenizer": "*", + "friendsofphp/php-cs-fixer": "^3.61.1", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6.22 || 10.5.45 || ^11.5.7" + }, + "time": "2025-06-10T20:53:07+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "PhpCsFixerCustomFixers\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kuba Werłos", + "email": "werlos@gmail.com" + } + ], + "description": "A set of custom fixers for PHP CS Fixer", + "support": { + "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.27.0" + }, + "funding": [ + { + "url": "https://github.com/kubawerlos", + "type": "github" + } + ], + "install-path": "../kubawerlos/php-cs-fixer-custom-fixers" + }, + { + "name": "php-cs-fixer/shim", + "version": "v3.75.0", + "version_normalized": "3.75.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "eea219a577085bd13ff0cb644a422c20798316c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/eea219a577085bd13ff0cb644a422c20798316c7", + "reference": "eea219a577085bd13ff0cb644a422c20798316c7", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "time": "2025-03-31T18:45:02+00:00", + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.75.0" + }, + "install-path": "../php-cs-fixer/shim" + } + ], + "dev": true, + "dev-package-names": [ + "kubawerlos/php-cs-fixer-custom-fixers", + "php-cs-fixer/shim" + ] +} diff --git a/tools/.php-cs-fixer/vendor/composer/installed.php b/tools/.php-cs-fixer/vendor/composer/installed.php new file mode 100644 index 00000000000..f5c0d7a4aa1 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/composer/installed.php @@ -0,0 +1,47 @@ + array( + 'name' => '__root__', + 'pretty_version' => '11.5.x-dev', + 'version' => '11.5.9999999.9999999-dev', + 'reference' => '04669f839e9277f3e956438b79ad3a0a9f2e611b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => '11.5.x-dev', + 'version' => '11.5.9999999.9999999-dev', + 'reference' => '04669f839e9277f3e956438b79ad3a0a9f2e611b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'friendsofphp/php-cs-fixer' => array( + 'dev_requirement' => true, + 'replaced' => array( + 0 => 'v3.75.0', + ), + ), + 'kubawerlos/php-cs-fixer-custom-fixers' => array( + 'pretty_version' => 'v3.27.0', + 'version' => '3.27.0.0', + 'reference' => 'd860473d16b906c7945206177edc7d112357a706', + 'type' => 'library', + 'install_path' => __DIR__ . '/../kubawerlos/php-cs-fixer-custom-fixers', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'php-cs-fixer/shim' => array( + 'pretty_version' => 'v3.75.0', + 'version' => '3.75.0.0', + 'reference' => 'eea219a577085bd13ff0cb644a422c20798316c7', + 'type' => 'application', + 'install_path' => __DIR__ . '/../php-cs-fixer/shim', + 'aliases' => array(), + 'dev_requirement' => true, + ), + ), +); diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/CHANGELOG.md b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/CHANGELOG.md new file mode 100644 index 00000000000..36ffcd30a1c --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/CHANGELOG.md @@ -0,0 +1,257 @@ +# CHANGELOG for PHP CS Fixer: custom fixers + +## v3.27.0 +- Add PhpdocTagNoNamedArgumentsFixer + +## v3.26.0 +- Add TypedClassConstantFixer + +## v3.25.0 +- Add ForeachUseValueFixer +- Add NoUselessWriteVisibilityFixer +- Add TrimKeyFixer +- ReadonlyPromotedPropertiesFixer - support asymmetric visibility + +## v3.24.0 +- Add PhpUnitRequiresConstraintFixer + +## v3.23.0 +- Add ClassConstantUsageFixer + +## v3.22.0 +- NoSuperfluousConcatenationFixer - add option "keep_concatenation_for_different_quotes" +- NoPhpStormGeneratedCommentFixer - handle more comments +- Update minimum PHP CS Fixer version to 3.61.1 + +## v3.21.0 +- Deprecate PhpdocArrayStyleFixer - use "phpdoc_array_type" +- NoUselessParenthesisFixer - keep parentheses around `and`, `xor` and `or` +- Update minimum PHP CS Fixer version to 3.50.0 + +## v3.20.0 +- Deprecate PhpdocTypeListFixer - use "phpdoc_list_type" +- Update minimum PHP CS Fixer version to 3.49.0 + +## v3.19.0 +- Deprecate NumericLiteralSeparatorFixer - use "numeric_literal_separator" +- Update minimum PHP CS Fixer version to 3.47.0 + +## v3.18.0 +- Add PhpdocTypeListFixer + +## v3.17.0 +- PhpdocNoIncorrectVarAnnotationFixer - support promoted properties + +## v3.16.0 +- Deprecate DataProviderReturnTypeFixer - use "php_unit_data_provider_return_type" +- Update minimum PHP CS Fixer version to 3.22.0 + +## v3.15.0 +- Deprecate DataProviderNameFixer - use "php_unit_data_provider_name" +- Deprecate PhpdocParamOrderFixer - use "phpdoc_param_order" +- Update minimum PHP CS Fixer version to 3.19.0 + +## v3.14.0 +- Add EmptyFunctionBodyFixer +- Deprecate DataProviderStaticFixer - use "php_unit_data_provider_static" +- Update minimum PHP CS Fixer version to 3.16.0 + +## v3.13.0 +- MultilinePromotedPropertiesFixer - add option "keep_blank_lines" + +## v3.12.0 +- MultilinePromotedPropertiesFixer - add option "minimum_number_of_parameters" +- Add `bootstrap.php` + +## v3.11.0 +- Add ReadonlyPromotedPropertiesFixer + +## v3.10.0 +- Do not require `friendsofphp/php-cs-fixer` as dependency (to allow using `php-cs-fixer/shim`) + +## v3.9.0 +- Add PhpdocTypesCommaSpacesFixer + +## v3.8.0 +- Update minimum PHP version to 7.4 +- Update minimum PHP CS Fixer version to 3.6.0 +- DataProviderStaticFixer - add option "force" +- Deprecate InternalClassCasingFixer + +## v3.7.0 +- Add NoTrailingCommaInSinglelineFixer + +## v3.6.0 +- Add IssetToArrayKeyExistsFixer +- Add PhpdocVarAnnotationToAssertFixer + +## v3.5.0 +- Add NoUselessDirnameCallFixer + +## v3.4.0 +- Add DeclareAfterOpeningTagFixer + +## v3.3.0 +- Add ConstructorEmptyBracesFixer +- PhpdocNoIncorrectVarAnnotationFixer - remove more incorrect annotations + +## v3.2.0 +- Add PhpUnitAssertArgumentsOrderFixer +- Add PhpUnitDedicatedAssertFixer +- PromotedConstructorPropertyFixer - add option "promote_only_existing_properties" +- NoUselessCommentFixer - support PHPDoc like `/** ClassName */` + +## v3.1.0 +- Add MultilinePromotedPropertiesFixer +- Add PhpdocArrayStyleFixer +- Add PromotedConstructorPropertyFixer +- Restore PhpCsFixerCustomFixers\Analyzer\SwitchAnalyzer (as PhpCsFixer\Tokenizer\Analyzer\SwitchAnalyzer got removed in PHP CS Fixer 3.2.0) + +## v3.0.0 +- Drop support for PHP CS Fixer v2 +- Add StringableInterfaceFixer +- Remove NoUselessSprintfFixer - use "no_useless_sprintf" +- Remove OperatorLinebreakFixer - use "operator_linebreak" +- NoCommentedOutCodeFixer - do not remove URLs +- NoDuplicatedArrayKeyFixer - add option "ignore_expressions" +- NoUselessParenthesisFixer - fix expressions +- PhpdocNoIncorrectVarAnnotationFixer - handle class properties when variable names are different and constants with visibility + +## v2.5.0 +- Add PHP CS Fixer v3 support + +## v2.4.0 +- Allow PHP 8 +- Update PHP CS Fixer to v2.17 +- Deprecate NoUselessSprintfFixer - use "no_useless_sprintf" +- Deprecate OperatorLinebreakFixer - use "operator_linebreak" +- Remove PhpCsFixerCustomFixers\Analyzer\ReferenceAnalyzer - use PhpCsFixer\Tokenizer\Analyzer\ReferenceAnalyzer +- Remove PhpCsFixerCustomFixers\Analyzer\SwitchAnalyzer - use PhpCsFixer\Tokenizer\Analyzer\SwitchAnalyzer + +## v2.3.0 +- Add NoUselessParenthesisFixer +- Add NoUselessStrlenFixer +- DataProviderNameFixer - handle snake_case naming + +## v2.2.0 +- Feature: DataProviderNameFixer - add options "prefix" and "suffix" + +## v2.1.0 +- Add CommentedOutFunctionFixer +- Add NoDuplicatedArrayKeyFixer +- Add NumericLiteralSeparatorFixer + +## v2.0.0 +- Drop PHP 7.1 support +- Remove ImplodeCallFixer - use "implode_call" +- Remove NoTwoConsecutiveEmptyLinesFixer - use "no_extra_blank_lines" +- Remove NoUnneededConcatenationFixer - use NoSuperfluousConcatenationFixer +- Remove NoUselessClassCommentFixer - use NoUselessCommentFixer +- Remove NoUselessConstructorCommentFixer - use NoUselessCommentFixer +- Remove NullableParamStyleFixer - use "nullable_type_declaration_for_default_null_value" +- Remove PhpdocVarAnnotationCorrectOrderFixer - use "phpdoc_var_annotation_correct_order" +- Remove SingleLineThrowFixer - use "single_line_throw" + +## v1.17.0 +- Update PHP CS Fixer to v2.16 +- Add DataProviderStaticFixer +- Add NoSuperfluousConcatenationFixer +- Add PhpdocTypesTrimFixer +- Feature: NoSuperfluousConcatenationFixer - add option "allow_preventing_trailing_spaces" +- Feature: NoSuperfluousConcatenationFixer - handle concatenation of single and double quoted strings together +- Deprecate NoUnneededConcatenationFixer +- Deprecate NullableParamStyleFixer +- Deprecate SingleLineThrowFixer +- Allow symfony/finder 5.0 +- Add Windows OS support with AppVeyor + +## v1.16.0 +- Add PhpdocOnlyAllowedAnnotationsFixer +- Feature: OperatorLinebreakFixer - handle object operators + +## v1.15.0 +- Add CommentSurroundedBySpacesFixer +- Add DataProviderReturnTypeFixer +- Add NoDuplicatedImportsFixer + +## v1.14.0 +- Add DataProviderNameFixer +- Add NoUselessSprintfFixer +- Add PhpUnitNoUselessReturnFixer +- Add SingleLineThrowFixer +- Feature: NoCommentedOutCodeFixer - handle class method + +## v1.13.0 +- Update PHP CS Fixer to v2.14 +- OperatorLinebreakFixer - respect no whitespace around operator +- OperatorLinebreakFixer - support concatenation operator +- Deprecate PhpdocVarAnnotationCorrectOrderFixer + +## v1.12.0 +- Add NoCommentedOutCodeFixer +- Add NoUselessCommentFixer +- Add NullableParamStyleFixer +- Deprecate NoUselessClassCommentFixer +- Deprecate NoUselessConstructorCommentFixer +- Feature: OperatorLinebreakFixer - handle ternary operator +- Fix: NoImportFromGlobalNamespaceFixer - class without namespace +- Fix: NoUselessClassCommentFixer - comment detection +- Fix: TokenRemover - remove last element of file +- Fix: TokenRemover - remove item in line after code +- Fix: NoImportFromGlobalNamespaceFixer - constant named the same as global imported class + +## v1.11.0 +- Add PhpdocParamOrderFixer +- Add InternalClassCasingFixer +- Add SingleSpaceAfterStatementFixer +- Add SingleSpaceBeforeStatementFixer +- Add OperatorLinebreakFixer +- Add MultilineCommentOpeningClosingAloneFixer + +## v1.10.0 +- Add NoUnneededConcatenationFixer +- Add PhpdocNoSuperfluousParamFixer +- Deprecate ImplodeCallFixer +- Deprecate NoTwoConsecutiveEmptyLinesFixer + +## v1.9.0 +- Add NoNullableBooleanTypeFixer + +## v1.8.0 +- Add PhpdocSelfAccessorFixer + +## v1.7.0 +- Add NoReferenceInFunctionDefinitionFixer +- Add NoImportFromGlobalNamespaceFixer + +## v1.6.0 +- Add ImplodeCallFixer +- Add PhpdocSingleLineVarFixer + +## v1.5.0 +- Add NoUselessDoctrineRepositoryCommentFixer + +## v1.4.0 +- Add NoDoctrineMigrationsGeneratedCommentFixer + +## v1.3.0 +- Add PhpdocVarAnnotationCorrectOrderFixer +- Remove @var without type at the beginning in PhpdocNoIncorrectVarAnnotationFixer + +## v1.2.0 +- Add PhpdocNoIncorrectVarAnnotationFixer + +## v1.1.0 +- Update PHP CS Fixer to v2.12 +- Add NoUselessConstructorCommentFixer +- Add PhpdocParamTypeFixer +- Feature: code coverage +- Feature: create Travis stages +- Feature: verify correctness for PHP CS Fixer (without smote tests) +- Fix: false positive class comment + +## v1.0.0 +- Add NoLeadingSlashInGlobalNamespaceFixer +- Add NoPhpStormGeneratedCommentFixer +- Add NoTwoConsecutiveEmptyLinesFixer +- Add NoUselessClassCommentFixer diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/LICENSE b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/LICENSE new file mode 100644 index 00000000000..c67d51a4b94 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Kuba Werłos + +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/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/README.md b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/README.md new file mode 100644 index 00000000000..f8431475fbe --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/README.md @@ -0,0 +1,764 @@ + + +# PHP CS Fixer: custom fixers + +[![Latest stable version](https://img.shields.io/packagist/v/kubawerlos/php-cs-fixer-custom-fixers.svg?label=current%20version)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers) +[![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net) +[![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE) +![Tests](https://img.shields.io/badge/tests-3788-brightgreen.svg) +[![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers) + +[![CI status](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions/workflows/ci.yaml/badge.svg)](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions/workflows/ci.yaml) +[![Code coverage](https://img.shields.io/coveralls/github/kubawerlos/php-cs-fixer-custom-fixers/main.svg)](https://coveralls.io/github/kubawerlos/php-cs-fixer-custom-fixers?branch=main) +[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fkubawerlos%2Fphp-cs-fixer-custom-fixers%2Fmain)](https://dashboard.stryker-mutator.io/reports/github.com/kubawerlos/php-cs-fixer-custom-fixers/main) +[![Psalm type coverage](https://shepherd.dev/github/kubawerlos/php-cs-fixer-custom-fixers/coverage.svg)](https://shepherd.dev/github/kubawerlos/php-cs-fixer-custom-fixers) + +A set of custom fixers for [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer). + +## Installation +PHP CS Fixer: custom fixers can be installed by running: +```bash +composer require --dev kubawerlos/php-cs-fixer-custom-fixers +``` + + +## Usage +In your PHP CS Fixer configuration register fixers and use them: +```diff + registerCustomFixers(new PhpCsFixerCustomFixers\Fixers()) + ->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], ++ PhpCsFixerCustomFixers\Fixer\NoLeadingSlashInGlobalNamespaceFixer::name() => true, ++ PhpCsFixerCustomFixers\Fixer\PhpdocNoSuperfluousParamFixer::name() => true, + ]); +``` +:warning: When PHP CS Fixer is installed via [`php-cs-fixer/shim`](https://github.com/PHP-CS-Fixer/shim) package, +requiring bootstrap may be needed to load `PhpCsFixerCustomFixers` classes: +```php +require __DIR__ . '/vendor/kubawerlos/php-cs-fixer-custom-fixers/bootstrap.php'; +``` + + +## Fixers +#### ClassConstantUsageFixer +Class constant must be used instead of a copy of string. +```diff + $value) { +- $product *= $elements[$key]; ++ $product *= $value; + } +``` + +#### InternalClassCasingFixer +Classes defined internally by an extension or the core must be referenced with the correct case. + DEPRECATED: use `class_reference_name_casing` instead. +```diff + addSql("UPDATE t1 SET col1 = col1 + 1"); + } + public function down(Schema $schema) + { +- // this down() migration is auto-generated, please modify it to your needs + $this->addSql("UPDATE t1 SET col1 = col1 - 1"); + } + } +``` + +#### NoDuplicatedArrayKeyFixer +There must be no duplicate array keys. +Configuration options: +- `ignore_expressions` (`bool`): whether to keep duplicated expressions (as they might return different values) or not; defaults to `true` +```diff + 1, + "bar" => 2, + "foo" => 3, + ]; +``` + +#### NoDuplicatedImportsFixer +There must be no duplicate `use` statements. +```diff + 0; ++$isEmpty = $string === ''; ++$isNotEmpty = $string !== ''; +``` + +#### NoUselessWriteVisibilityFixer +There must be no useless write visibility. +```diff + markTestSkipped(); +- return; + } + } +``` + +#### PhpUnitRequiresConstraintFixer +Assertions and attributes for PHP and PHPUnit versions must have explicit version constraint and space after comparison operator. +```diff + = 8.1 + */ + public function testFoo() {} + + /** +- * @requires PHP <8.3 ++ * @requires PHP < 8.3 + */ + public function testBar() {} + +- #[\PHPUnit\Framework\Attributes\RequiresPhpunit('12.0')] ++ #[\PHPUnit\Framework\Attributes\RequiresPhpunit('>= 12.0')] + public function testBaz() {} + } +``` + +#### PhpdocArrayStyleFixer +Generic array style should be used in PHPDoc. + DEPRECATED: use `phpdoc_array_type` instead. +```diff + + */ + function foo() { return [1, 2]; } +``` + +#### PhpdocNoIncorrectVarAnnotationFixer +The `@var` annotations must be used correctly in code. +```diff + ++ * @param list + */ + function foo($x) {} +``` + +#### PhpdocTypesCommaSpacesFixer +PHPDoc types commas must not be preceded by a whitespace, and must be succeeded by a single whitespace. +```diff +- */ ++ */ +``` + +#### PhpdocTypesTrimFixer +PHPDoc types must be trimmed. +```diff + bar = $bar; ++ public function __construct(private string $bar) { + } + } +``` + +#### ReadonlyPromotedPropertiesFixer +Promoted properties must be declared as read-only. + *Risky: when property is written.* +```diff + bar(); ++$foo = new Foo(); ++echo $foo->bar(); +``` + +#### SingleSpaceBeforeStatementFixer +Statements not preceded by a line break must be preceded by a single space. +```diff + 'v1', +- 'option 2 or 3' => 'v23', ++ 'option 1' => 'v1', ++ 'option 2 or 3' => 'v23', + ]; +``` + +#### TypedClassConstantFixer +Class constants must have a type. +```diff + startIndex = $startIndex; + $this->endIndex = $endIndex; + $this->isConstant = $isConstant; + } + + public function getStartIndex(): int + { + return $this->startIndex; + } + + public function getEndIndex(): int + { + return $this->endIndex; + } + + public function isConstant(): bool + { + return $this->isConstant; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/ArrayElementAnalysis.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/ArrayElementAnalysis.php new file mode 100644 index 00000000000..c3b63593bf6 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/ArrayElementAnalysis.php @@ -0,0 +1,51 @@ +keyStartIndex = $keyStartIndex; + $this->keyEndIndex = $keyEndIndex; + $this->valueStartIndex = $valueStartIndex; + $this->valueEndIndex = $valueEndIndex; + } + + public function getKeyStartIndex(): ?int + { + return $this->keyStartIndex; + } + + public function getKeyEndIndex(): ?int + { + return $this->keyEndIndex; + } + + public function getValueStartIndex(): int + { + return $this->valueStartIndex; + } + + public function getValueEndIndex(): int + { + return $this->valueEndIndex; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/CaseAnalysis.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/CaseAnalysis.php new file mode 100644 index 00000000000..615822cf831 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/CaseAnalysis.php @@ -0,0 +1,30 @@ +colonIndex = $colonIndex; + } + + public function getColonIndex(): int + { + return $this->colonIndex; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/ConstructorAnalysis.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/ConstructorAnalysis.php new file mode 100644 index 00000000000..4c12f0650f1 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/ConstructorAnalysis.php @@ -0,0 +1,201 @@ +tokens = $tokens; + $this->constructorIndex = $constructorIndex; + } + + public function getConstructorIndex(): int + { + return $this->constructorIndex; + } + + /** + * @return list + */ + public function getConstructorParameterNames(): array + { + $openParenthesis = $this->tokens->getNextTokenOfKind($this->constructorIndex, ['(']); + \assert(\is_int($openParenthesis)); + $closeParenthesis = $this->tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); + + $constructorParameterNames = []; + for ($index = $openParenthesis + 1; $index < $closeParenthesis; $index++) { + if (!$this->tokens[$index]->isGivenKind(\T_VARIABLE)) { + continue; + } + + $constructorParameterNames[] = $this->tokens[$index]->getContent(); + } + + return $constructorParameterNames; + } + + /** + * @return array + */ + public function getConstructorPromotableParameters(): array + { + $openParenthesis = $this->tokens->getNextTokenOfKind($this->constructorIndex, ['(']); + \assert(\is_int($openParenthesis)); + $closeParenthesis = $this->tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); + + $constructorPromotableParameters = []; + for ($index = $openParenthesis + 1; $index < $closeParenthesis; $index++) { + if (!$this->tokens[$index]->isGivenKind(\T_VARIABLE)) { + continue; + } + + $typeIndex = $this->tokens->getPrevMeaningfulToken($index); + \assert(\is_int($typeIndex)); + if ($this->tokens[$typeIndex]->equalsAny(['(', ',', [\T_CALLABLE], [\T_ELLIPSIS]])) { + continue; + } + + $visibilityIndex = $this->tokens->getPrevTokenOfKind( + $index, + [ + '(', + ',', + [CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE], + [CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED], + [CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC], + ], + ); + \assert(\is_int($visibilityIndex)); + if (!$this->tokens[$visibilityIndex]->equalsAny(['(', ','])) { + continue; + } + + $constructorPromotableParameters[$index] = $this->tokens[$index]->getContent(); + } + + return $constructorPromotableParameters; + } + + /** + * @return array + */ + public function getConstructorPromotableAssignments(): array + { + $openParenthesis = $this->tokens->getNextTokenOfKind($this->constructorIndex, ['(']); + \assert(\is_int($openParenthesis)); + $closeParenthesis = $this->tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); + + $openBrace = $this->tokens->getNextTokenOfKind($closeParenthesis, ['{']); + \assert(\is_int($openBrace)); + $closeBrace = $this->tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openBrace); + + $variables = []; + $properties = []; + $propertyToVariableMap = []; + + for ($index = $openBrace + 1; $index < $closeBrace; $index++) { + if (!$this->tokens[$index]->isGivenKind(\T_VARIABLE)) { + continue; + } + + $semicolonIndex = $this->tokens->getNextMeaningfulToken($index); + \assert(\is_int($semicolonIndex)); + if (!$this->tokens[$semicolonIndex]->equals(';')) { + continue; + } + + $propertyIndex = $this->getPropertyIndex($index, $openBrace); + if ($propertyIndex === null) { + continue; + } + + $properties[$propertyIndex] = $this->tokens[$propertyIndex]->getContent(); + $variables[$index] = $this->tokens[$index]->getContent(); + $propertyToVariableMap[$propertyIndex] = $index; + } + + foreach ($this->getDuplicatesIndices($properties) as $duplicate) { + unset($variables[$propertyToVariableMap[$duplicate]]); + } + + foreach ($this->getDuplicatesIndices($variables) as $duplicate) { + unset($variables[$duplicate]); + } + + return \array_flip($variables); + } + + private function getPropertyIndex(int $index, int $openBrace): ?int + { + $assignmentIndex = $this->tokens->getPrevMeaningfulToken($index); + \assert(\is_int($assignmentIndex)); + if (!$this->tokens[$assignmentIndex]->equals('=')) { + return null; + } + + $propertyIndex = $this->tokens->getPrevMeaningfulToken($assignmentIndex); + if (!$this->tokens[$propertyIndex]->isGivenKind(\T_STRING)) { + return null; + } + \assert(\is_int($propertyIndex)); + + $objectOperatorIndex = $this->tokens->getPrevMeaningfulToken($propertyIndex); + \assert(\is_int($objectOperatorIndex)); + + $thisIndex = $this->tokens->getPrevMeaningfulToken($objectOperatorIndex); + \assert(\is_int($thisIndex)); + if (!$this->tokens[$thisIndex]->equals([\T_VARIABLE, '$this'])) { + return null; + } + + $prevThisIndex = $this->tokens->getPrevMeaningfulToken($thisIndex); + \assert(\is_int($prevThisIndex)); + if ($prevThisIndex > $openBrace && !$this->tokens[$prevThisIndex]->equalsAny(['}', ';'])) { + return null; + } + + return $propertyIndex; + } + + /** + * @param array $array + * + * @return array + */ + private function getDuplicatesIndices(array $array): array + { + $duplicates = []; + $values = []; + foreach ($array as $key => $value) { + if (\array_key_exists($value, $values)) { + \assert(\is_int($values[$value])); + $duplicates[$values[$value]] = $values[$value]; + + $duplicates[$key] = $key; + } + $values[$value] = $key; + } + + return $duplicates; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/SwitchAnalysis.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/SwitchAnalysis.php new file mode 100644 index 00000000000..305976c04d7 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/Analysis/SwitchAnalysis.php @@ -0,0 +1,52 @@ + */ + private array $cases = []; + + /** + * @param list $cases + */ + public function __construct(int $casesStart, int $casesEnd, array $cases) + { + $this->casesStart = $casesStart; + $this->casesEnd = $casesEnd; + $this->cases = $cases; + } + + public function getCasesStart(): int + { + return $this->casesStart; + } + + public function getCasesEnd(): int + { + return $this->casesEnd; + } + + /** + * @return list + */ + public function getCases(): array + { + return $this->cases; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/ArrayAnalyzer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/ArrayAnalyzer.php new file mode 100644 index 00000000000..ba6297ead5a --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/ArrayAnalyzer.php @@ -0,0 +1,126 @@ + + */ + public function getElements(Tokens $tokens, int $index): array + { + if ($tokens[$index]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) { + $arrayContentStartIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($arrayContentStartIndex)); + + $arrayContentEndIndex = $tokens->getPrevMeaningfulToken($tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index)); + \assert(\is_int($arrayContentEndIndex)); + + return $this->getElementsForArrayContent($tokens, $arrayContentStartIndex, $arrayContentEndIndex); + } + + if ($tokens[$index]->isGivenKind(\T_ARRAY)) { + $arrayOpenBraceIndex = $tokens->getNextTokenOfKind($index, ['(']); + \assert(\is_int($arrayOpenBraceIndex)); + + $arrayContentStartIndex = $tokens->getNextMeaningfulToken($arrayOpenBraceIndex); + \assert(\is_int($arrayContentStartIndex)); + + $arrayContentEndIndex = $tokens->getPrevMeaningfulToken($tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $arrayOpenBraceIndex)); + \assert(\is_int($arrayContentEndIndex)); + + return $this->getElementsForArrayContent($tokens, $arrayContentStartIndex, $arrayContentEndIndex); + } + + throw new \InvalidArgumentException(\sprintf('Index %d is not an array.', $index)); + } + + /** + * @return list + */ + private function getElementsForArrayContent(Tokens $tokens, int $startIndex, int $endIndex): array + { + $elements = []; + + $index = $startIndex; + while ($endIndex >= $index = $this->nextCandidateIndex($tokens, $index)) { + if (!$tokens[$index]->equals(',')) { + continue; + } + + $elementEndIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($elementEndIndex)); + + $elements[] = $this->createArrayElementAnalysis($tokens, $startIndex, $elementEndIndex); + + $startIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($startIndex)); + } + + if ($startIndex <= $endIndex) { + $elements[] = $this->createArrayElementAnalysis($tokens, $startIndex, $endIndex); + } + + return $elements; + } + + private function createArrayElementAnalysis(Tokens $tokens, int $startIndex, int $endIndex): ArrayElementAnalysis + { + $index = $startIndex; + while ($endIndex > $index = $this->nextCandidateIndex($tokens, $index)) { + if (!$tokens[$index]->isGivenKind(\T_DOUBLE_ARROW)) { + continue; + } + + $keyEndIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($keyEndIndex)); + + $valueStartIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($valueStartIndex)); + + return new ArrayElementAnalysis($startIndex, $keyEndIndex, $valueStartIndex, $endIndex); + } + + return new ArrayElementAnalysis(null, null, $startIndex, $endIndex); + } + + private function nextCandidateIndex(Tokens $tokens, int $index): int + { + if ($tokens[$index]->equals('{')) { + return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index) + 1; + } + + if ($tokens[$index]->equals('(')) { + return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index) + 1; + } + + if ($tokens[$index]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) { + return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index) + 1; + } + + if ($tokens[$index]->isGivenKind(\T_ARRAY)) { + $arrayOpenBraceIndex = $tokens->getNextTokenOfKind($index, ['(']); + \assert(\is_int($arrayOpenBraceIndex)); + + return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $arrayOpenBraceIndex) + 1; + } + + return $index + 1; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/ConstructorAnalyzer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/ConstructorAnalyzer.php new file mode 100644 index 00000000000..b869087c88d --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/ConstructorAnalyzer.php @@ -0,0 +1,66 @@ +isGivenKind(\T_CLASS)) { + throw new \InvalidArgumentException(\sprintf('Index %d is not a class.', $classIndex)); + } + + $tokensAnalyzer = new TokensAnalyzer($tokens); + + foreach ($tokensAnalyzer->getClassyElements() as $index => $element) { + if ($element['classIndex'] !== $classIndex) { + continue; + } + + if (!$this->isConstructor($tokens, $index, $element)) { + continue; + } + + $constructorAttributes = $tokensAnalyzer->getMethodAttributes($index); + if ($constructorAttributes['abstract']) { + return null; + } + + return new ConstructorAnalysis($tokens, $index); + } + + return null; + } + + /** + * @param array $element + */ + private function isConstructor(Tokens $tokens, int $index, array $element): bool + { + if ($element['type'] !== 'method') { + return false; + } + + $functionNameIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($functionNameIndex)); + + return $tokens[$functionNameIndex]->equals([\T_STRING, '__construct'], false); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/FunctionAnalyzer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/FunctionAnalyzer.php new file mode 100644 index 00000000000..bd51cef6515 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/FunctionAnalyzer.php @@ -0,0 +1,118 @@ + + */ + public static function getFunctionArguments(Tokens $tokens, int $index): array + { + $argumentsRange = self::getArgumentsRange($tokens, $index); + if ($argumentsRange === null) { + return []; + } + + [$argumentStartIndex, $argumentsEndIndex] = $argumentsRange; + + $arguments = []; + $index = $currentArgumentStart = $argumentStartIndex; + while ($index < $argumentsEndIndex) { + $blockType = Tokens::detectBlockType($tokens[$index]); + if ($blockType !== null && $blockType['isStart']) { + $index = $tokens->findBlockEnd($blockType['type'], $index); + continue; + } + + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + + if (!$tokens[$index]->equals(',')) { + continue; + } + + $currentArgumentEnd = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($currentArgumentEnd)); + + $arguments[] = self::createArgumentAnalysis($tokens, $currentArgumentStart, $currentArgumentEnd); + + $currentArgumentStart = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($currentArgumentStart)); + } + + $arguments[] = self::createArgumentAnalysis($tokens, $currentArgumentStart, $argumentsEndIndex); + + return $arguments; + } + + /** + * @return null|array{int, int} + */ + private static function getArgumentsRange(Tokens $tokens, int $index): ?array + { + if (!$tokens[$index]->isGivenKind([\T_ISSET, \T_STRING])) { + throw new \InvalidArgumentException(\sprintf('Index %d is not a function.', $index)); + } + + $openParenthesis = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($openParenthesis)); + if (!$tokens[$openParenthesis]->equals('(')) { + throw new \InvalidArgumentException(\sprintf('Index %d is not a function.', $index)); + } + + $closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); + + $argumentsEndIndex = $tokens->getPrevMeaningfulToken($closeParenthesis); + \assert(\is_int($argumentsEndIndex)); + + if ($openParenthesis === $argumentsEndIndex) { + return null; + } + if ($tokens[$argumentsEndIndex]->equals(',')) { + $argumentsEndIndex = $tokens->getPrevMeaningfulToken($argumentsEndIndex); + \assert(\is_int($argumentsEndIndex)); + } + + $argumentStartIndex = $tokens->getNextMeaningfulToken($openParenthesis); + \assert(\is_int($argumentStartIndex)); + + return [$argumentStartIndex, $argumentsEndIndex]; + } + + private static function createArgumentAnalysis(Tokens $tokens, int $startIndex, int $endIndex): ArgumentAnalysis + { + $isConstant = true; + + for ($index = $startIndex; $index <= $endIndex; $index++) { + if ($tokens[$index]->isGivenKind(\T_VARIABLE)) { + $isConstant = false; + } + if ($tokens[$index]->equals('(')) { + $prevParenthesisIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevParenthesisIndex)); + + if (!$tokens[$prevParenthesisIndex]->isGivenKind(\T_ARRAY)) { + $isConstant = false; + } + } + } + + return new ArgumentAnalysis($startIndex, $endIndex, $isConstant); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/SwitchAnalyzer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/SwitchAnalyzer.php new file mode 100644 index 00000000000..d2aefc5d008 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Analyzer/SwitchAnalyzer.php @@ -0,0 +1,111 @@ +isGivenKind(\T_SWITCH)) { + throw new \InvalidArgumentException(\sprintf('Index %d is not "switch".', $switchIndex)); + } + + $casesStartIndex = $this->getCasesStart($tokens, $switchIndex); + $casesEndIndex = $this->getCasesEnd($tokens, $casesStartIndex); + + $cases = []; + $index = $casesStartIndex; + while ($index < $casesEndIndex) { + $index = $this->getNextSameLevelToken($tokens, $index); + + if (!$tokens[$index]->isGivenKind([\T_CASE, \T_DEFAULT])) { + continue; + } + + $caseAnalysis = $this->getCaseAnalysis($tokens, $index); + + $cases[] = $caseAnalysis; + } + + return new SwitchAnalysis($casesStartIndex, $casesEndIndex, $cases); + } + + private function getCasesStart(Tokens $tokens, int $switchIndex): int + { + $parenthesisStartIndex = $tokens->getNextMeaningfulToken($switchIndex); + \assert(\is_int($parenthesisStartIndex)); + $parenthesisEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $parenthesisStartIndex); + + $casesStartIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex); + \assert(\is_int($casesStartIndex)); + + return $casesStartIndex; + } + + private function getCasesEnd(Tokens $tokens, int $casesStartIndex): int + { + if ($tokens[$casesStartIndex]->equals('{')) { + return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $casesStartIndex); + } + + $index = $casesStartIndex; + while ($index < $tokens->count()) { + $index = $this->getNextSameLevelToken($tokens, $index); + + if ($tokens[$index]->isGivenKind(\T_ENDSWITCH)) { + break; + } + } + + $afterEndswitchIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($afterEndswitchIndex)); + + return $tokens[$afterEndswitchIndex]->equals(';') ? $afterEndswitchIndex : $index; + } + + private function getCaseAnalysis(Tokens $tokens, int $index): CaseAnalysis + { + while ($index < $tokens->count()) { + $index = $this->getNextSameLevelToken($tokens, $index); + + if ($tokens[$index]->equalsAny([':', ';'])) { + break; + } + } + + return new CaseAnalysis($index); + } + + private function getNextSameLevelToken(Tokens $tokens, int $index): int + { + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + + if ($tokens[$index]->isGivenKind(\T_SWITCH)) { + return (new self())->getSwitchAnalysis($tokens, $index)->getCasesEnd(); + } + + $blockType = Tokens::detectBlockType($tokens[$index]); + if ($blockType !== null && $blockType['isStart']) { + return $tokens->findBlockEnd($blockType['type'], $index) + 1; + } + + return $index; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/AbstractFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/AbstractFixer.php new file mode 100644 index 00000000000..f73c3d11369 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/AbstractFixer.php @@ -0,0 +1,38 @@ +isTokenKindFound(\T_DOC_COMMENT); + } + + final public function isRisky(): bool + { + return false; + } + + final public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_DOC_COMMENT])) { + continue; + } + + $docBlock = new DocBlock($tokens[$index]->getContent()); + + foreach ($docBlock->getAnnotations() as $annotation) { + if (!$annotation->supportTypes()) { + continue; + } + + $typeExpression = $annotation->getTypeExpression(); + if ($typeExpression === null) { + continue; + } + + $type = $typeExpression->toString(); + $type = $this->fixType($type); + $annotation->setTypes([$type]); + } + + $newContent = $docBlock->getContent(); + if ($newContent === $tokens[$index]->getContent()) { + continue; + } + + $tokens[$index] = new Token([\T_DOC_COMMENT, $newContent]); + } + } + + abstract protected function fixType(string $type): string; +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ClassConstantUsageFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ClassConstantUsageFixer.php new file mode 100644 index 00000000000..12578cc34f5 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ClassConstantUsageFixer.php @@ -0,0 +1,161 @@ +isAllTokenKindsFound([\T_CLASS, \T_CONSTANT_ENCAPSED_STRING]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CLASS)) { + continue; + } + + $openParenthesisIndex = $tokens->getNextTokenOfKind($index, ['{']); + \assert(\is_int($openParenthesisIndex)); + + $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openParenthesisIndex); + + $this->fixClass($tokens, $openParenthesisIndex, $closeParenthesisIndex); + } + } + + private function fixClass(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): void + { + [$constantsMap, $constantsIndices] = $this->getClassConstants($tokens, $openParenthesisIndex, $closeParenthesisIndex); + + for ($index = $closeParenthesisIndex; $index > $openParenthesisIndex; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) { + continue; + } + + if (!isset($constantsMap[$tokens[$index]->getContent()])) { + continue; + } + + if (isset($constantsIndices[$index])) { + continue; + } + + $tokens->overrideRange( + $index, + $index, + [ + new Token([\T_STRING, 'self']), + new Token([\T_DOUBLE_COLON, '::']), + new Token([\T_STRING, $constantsMap[$tokens[$index]->getContent()]]), + ], + ); + } + } + + /** + * @return array{array, array} + */ + private function getClassConstants(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): array + { + $constants = []; + $constantsIndices = []; + for ($index = $openParenthesisIndex; $index < $closeParenthesisIndex; $index++) { + if (!$tokens[$index]->isGivenKind(\T_CONST)) { + continue; + } + + $assignTokenIndex = $tokens->getNextTokenOfKind($index, ['=']); + \assert(\is_int($assignTokenIndex)); + + $constantNameIndex = $tokens->getPrevMeaningfulToken($assignTokenIndex); + \assert(\is_int($constantNameIndex)); + + $constantValueIndex = $tokens->getNextMeaningfulToken($assignTokenIndex); + \assert(\is_int($constantValueIndex)); + + $constantsIndices[$constantValueIndex] = true; + + if (!$tokens[$constantValueIndex]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) { + continue; + } + + $constants[$tokens[$constantNameIndex]->getContent()] = $tokens[$constantValueIndex]->getContent(); + } + + return [$this->getClassConstantsMap($constants), $constantsIndices]; + } + + /** + * @param array $constants + * + * @return array + */ + private function getClassConstantsMap(array $constants): array + { + $map = []; + $valuesCount = []; + + foreach ($constants as $name => $value) { + $map[$value] = $name; + $valuesCount[$value] = ($valuesCount[$value] ?? 0) + 1; + + if ($valuesCount[$value] > 1) { + unset($map[$value]); + } + } + + return $map; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/CommentSurroundedBySpacesFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/CommentSurroundedBySpacesFixer.php new file mode 100644 index 00000000000..8175d2fa996 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/CommentSurroundedBySpacesFixer.php @@ -0,0 +1,84 @@ +isAnyTokenKindsFound([\T_COMMENT, \T_DOC_COMMENT]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_COMMENT, \T_DOC_COMMENT])) { + continue; + } + + // ensure whitespace at the beginning + $newContent = Preg::replace( + '/^(\\/\\/|#(?!\\[)|\\/\\*+)(?!(?:\\/|\\*|\\s|$))/', + '$1 ', + $tokens[$index]->getContent(), + ); + + // ensure whitespace at the end + $newContent = Preg::replace( + '/(?getContent()) { + continue; + } + + $tokens[$index] = new Token([\strpos($newContent, '/** ') === 0 ? \T_DOC_COMMENT : \T_COMMENT, $newContent]); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/CommentedOutFunctionFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/CommentedOutFunctionFixer.php new file mode 100644 index 00000000000..2f51df1c49b --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/CommentedOutFunctionFixer.php @@ -0,0 +1,237 @@ + + * + * @phpstan-type _InputConfig array{functions?: list} + * @phpstan-type _Config array{functions: list} + * + * @no-named-arguments + */ +final class CommentedOutFunctionFixer extends AbstractFixer implements ConfigurableFixerInterface +{ + /** @var list */ + private array $functions = ['print_r', 'var_dump', 'var_export']; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'The configured functions must be commented out.', + [new CodeSample('setDefault($this->functions) + ->setAllowedTypes(['array']) + ->getOption(), + ]); + } + + /** + * @param array> $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('functions', $configuration)) { + $this->functions = $configuration['functions']; + } + } + + /** + * Must run before CommentSurroundedBySpacesFixer, NoCommentedOutCodeFixer. + */ + public function getPriority(): int + { + return 57; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isTokenKindFound(\T_STRING); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$this->isFunctionToFix($tokens, $index)) { + continue; + } + + $startIndex = $index; + + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + + if ($tokens[$prevIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $startIndex = $prevIndex; + } + + if (!$this->isPreviousTokenSeparateStatement($tokens, $startIndex)) { + continue; + } + + $indexParenthesisStart = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($indexParenthesisStart)); + + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $indexParenthesisStart); + + $semicolonIndex = $tokens->getNextMeaningfulToken($endIndex); + \assert(\is_int($semicolonIndex)); + + if (!$tokens[$semicolonIndex]->equalsAny([';', [\T_CLOSE_TAG]])) { + continue; + } + + if ($tokens[$semicolonIndex]->equals(';')) { + $endIndex = $semicolonIndex; + } + + $this->fixBlock($tokens, $startIndex, $endIndex); + } + } + + private function isFunctionToFix(Tokens $tokens, int $index): bool + { + if (!$tokens[$index]->isGivenKind(\T_STRING)) { + return false; + } + + if (!\in_array(\strtolower($tokens[$index]->getContent()), $this->functions, true)) { + return false; + } + + return (new FunctionsAnalyzer())->isGlobalFunctionCall($tokens, $index); + } + + private function isPreviousTokenSeparateStatement(Tokens $tokens, int $index): bool + { + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + + if ($tokens[$prevIndex]->equalsAny([';', '{', '}', [\T_OPEN_TAG]])) { + return true; + } + + $switchAnalyzer = new SwitchAnalyzer(); + + if (!$tokens[$prevIndex]->equals(':')) { // can be part of ternary operator or from switch/case + return false; + } + + for ($i = $index; $i > 0; $i--) { + if (!$tokens[$i]->isGivenKind(\T_SWITCH)) { + continue; + } + foreach ($switchAnalyzer->getSwitchAnalysis($tokens, $i)->getCases() as $caseAnalysis) { + if ($caseAnalysis->getColonIndex() === $prevIndex) { + return true; + } + } + } + + return false; + } + + private function fixBlock(Tokens $tokens, int $startIndex, int $endIndex): void + { + if ($this->canUseSingleLineComment($tokens, $startIndex, $endIndex)) { + $this->fixBlockWithSingleLineComments($tokens, $startIndex, $endIndex); + + return; + } + + $tokens->overrideRange( + $startIndex, + $endIndex, + [new Token([\T_COMMENT, '/*' . $tokens->generatePartialCode($startIndex, $endIndex) . '*/'])], + ); + } + + private function canUseSingleLineComment(Tokens $tokens, int $startIndex, int $endIndex): bool + { + if (!$tokens->offsetExists($endIndex + 1)) { + return true; + } + + if (Preg::match('/^\\R/', $tokens[$endIndex + 1]->getContent())) { + return true; + } + + for ($index = $startIndex; $index < $endIndex; $index++) { + if (\strpos($tokens[$index]->getContent(), '*/') !== false) { + return true; + } + } + + return false; + } + + private function fixBlockWithSingleLineComments(Tokens $tokens, int $startIndex, int $endIndex): void + { + $codeToCommentOut = $tokens->generatePartialCode($startIndex, $endIndex); + + $prefix = '//'; + if ($tokens[$startIndex - 1]->isWhitespace()) { + $startIndex--; + $prefix = Preg::replace('/(^|\\R)(\\h*$)/D', '$1//$2', $tokens[$startIndex]->getContent()); + } + $codeToCommentOut = $prefix . \str_replace("\n", "\n//", $codeToCommentOut); + + if ($tokens->offsetExists($endIndex + 1)) { + if (!Preg::match('/^\\R/', $tokens[$endIndex + 1]->getContent())) { + $codeToCommentOut .= "\n"; + if ($tokens[$endIndex + 1]->isWhitespace()) { + $endIndex++; + $codeToCommentOut .= $tokens[$endIndex]->getContent(); + } + } + } + + $newTokens = Tokens::fromCode('clearAt(0); + $newTokens->clearEmptyTokens(); + + $tokens->overrideRange( + $startIndex, + $endIndex, + $newTokens, + ); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ConstructorEmptyBracesFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ConstructorEmptyBracesFixer.php new file mode 100644 index 00000000000..aa68f364f79 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ConstructorEmptyBracesFixer.php @@ -0,0 +1,96 @@ +isTokenKindFound('{'); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $constructorAnalyzer = new ConstructorAnalyzer(); + + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CLASS)) { + continue; + } + + $constructorAnalysis = $constructorAnalyzer->findNonAbstractConstructor($tokens, $index); + if ($constructorAnalysis === null) { + continue; + } + + $openParenthesisIndex = $tokens->getNextTokenOfKind($constructorAnalysis->getConstructorIndex(), ['(']); + \assert(\is_int($openParenthesisIndex)); + + $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex); + + $openBraceIndex = $tokens->getNextMeaningfulToken($closeParenthesisIndex); + \assert(\is_int($openBraceIndex)); + + $closeBraceIndex = $tokens->getNextNonWhitespace($openBraceIndex); + \assert(\is_int($closeBraceIndex)); + if (!$tokens[$closeBraceIndex]->equals('}')) { + continue; + } + + $tokens->ensureWhitespaceAtIndex($openBraceIndex + 1, 0, ''); + $tokens->ensureWhitespaceAtIndex($closeParenthesisIndex + 1, 0, ' '); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderNameFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderNameFixer.php new file mode 100644 index 00000000000..17645fde0e3 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderNameFixer.php @@ -0,0 +1,114 @@ + + * + * @phpstan-type _InputConfig array{prefix?: string, suffix?: string} + * @phpstan-type _Config array{prefix: string, suffix: string} + * + * @no-named-arguments + */ +final class DataProviderNameFixer extends AbstractFixer implements ConfigurableFixerInterface, DeprecatedFixerInterface +{ + private PhpUnitDataProviderNameFixer $phpUnitDataProviderNameFixer; + private string $prefix = 'provide'; + private string $suffix = 'Cases'; + + public function __construct() + { + $this->phpUnitDataProviderNameFixer = new PhpUnitDataProviderNameFixer(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + $this->phpUnitDataProviderNameFixer->getDefinition()->getSummary(), + [ + new CodeSample( + 'setAllowedTypes(['string']) + ->setDefault($this->prefix) + ->getOption(), + (new FixerOptionBuilder('suffix', 'suffix to be added at the end"')) + ->setAllowedTypes(['string']) + ->setDefault($this->suffix) + ->getOption(), + ]); + } + + public function configure(array $configuration): void + { + $this->phpUnitDataProviderNameFixer->configure($configuration); + } + + public function getPriority(): int + { + return $this->phpUnitDataProviderNameFixer->getPriority(); + } + + public function isCandidate(Tokens $tokens): bool + { + return $this->phpUnitDataProviderNameFixer->isCandidate($tokens); + } + + public function isRisky(): bool + { + return $this->phpUnitDataProviderNameFixer->isRisky(); + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $this->phpUnitDataProviderNameFixer->fix($file, $tokens); + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [$this->phpUnitDataProviderNameFixer->getName()]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderReturnTypeFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderReturnTypeFixer.php new file mode 100644 index 00000000000..6bd86654df0 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderReturnTypeFixer.php @@ -0,0 +1,87 @@ +phpUnitDataProviderReturnTypeFixer = new PhpUnitDataProviderReturnTypeFixer(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + $this->phpUnitDataProviderReturnTypeFixer->getDefinition()->getSummary(), + [ + new CodeSample( + 'phpUnitDataProviderReturnTypeFixer->getPriority(); + } + + public function isCandidate(Tokens $tokens): bool + { + return $this->phpUnitDataProviderReturnTypeFixer->isCandidate($tokens); + } + + public function isRisky(): bool + { + return $this->phpUnitDataProviderReturnTypeFixer->isRisky(); + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $this->phpUnitDataProviderReturnTypeFixer->fix($file, $tokens); + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [$this->phpUnitDataProviderReturnTypeFixer->getName()]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderStaticFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderStaticFixer.php new file mode 100644 index 00000000000..f44332fa813 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DataProviderStaticFixer.php @@ -0,0 +1,115 @@ + + * + * @phpstan-type _InputConfig array{force?: bool} + * @phpstan-type _Config array{force: bool} + * + * @no-named-arguments + */ +final class DataProviderStaticFixer extends AbstractFixer implements ConfigurableFixerInterface, DeprecatedFixerInterface +{ + private bool $force = false; + private PhpUnitDataProviderStaticFixer $phpUnitDataProviderStaticFixer; + + public function __construct() + { + $this->phpUnitDataProviderStaticFixer = new PhpUnitDataProviderStaticFixer(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + $this->phpUnitDataProviderStaticFixer->getDefinition()->getSummary(), + [ + new CodeSample( + 'setAllowedTypes(['bool']) + ->setDefault($this->force) + ->getOption(), + ]); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('force', $configuration)) { + $this->force = $configuration['force']; + } + $this->phpUnitDataProviderStaticFixer->configure(['force' => $this->force]); + } + + public function getPriority(): int + { + return $this->phpUnitDataProviderStaticFixer->getPriority(); + } + + public function isCandidate(Tokens $tokens): bool + { + return $this->phpUnitDataProviderStaticFixer->isCandidate($tokens); + } + + public function isRisky(): bool + { + return $this->phpUnitDataProviderStaticFixer->isRisky(); + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $this->phpUnitDataProviderStaticFixer->fix($file, $tokens); + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [$this->phpUnitDataProviderStaticFixer->getName()]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DeclareAfterOpeningTagFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DeclareAfterOpeningTagFixer.php new file mode 100644 index 00000000000..2bf6fdd38d1 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/DeclareAfterOpeningTagFixer.php @@ -0,0 +1,107 @@ +isTokenKindFound(\T_DECLARE); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + if (!$tokens[0]->isGivenKind(\T_OPEN_TAG)) { + return; + } + + $openingTagTokenContent = $tokens[0]->getContent(); + + $declareIndex = $tokens->getNextTokenOfKind(0, [[\T_DECLARE]]); + \assert(\is_int($declareIndex)); + + $openParenthesisIndex = $tokens->getNextMeaningfulToken($declareIndex); + \assert(\is_int($openParenthesisIndex)); + + $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex); + + if (\stripos($tokens->generatePartialCode($openParenthesisIndex, $closeParenthesisIndex), 'strict_types') === false) { + return; + } + + $tokens[0] = new Token([\T_OPEN_TAG, \substr($openingTagTokenContent, 0, 5) . ' ']); + + if ($declareIndex <= 2) { + $tokens->clearRange(1, $declareIndex - 1); + + return; + } + + $semicolonIndex = $tokens->getNextMeaningfulToken($closeParenthesisIndex); + \assert(\is_int($semicolonIndex)); + + $tokensToInsert = []; + for ($index = $declareIndex; $index <= $semicolonIndex; $index++) { + $tokensToInsert[] = $tokens[$index]; + } + + if ($tokens[1]->isGivenKind(\T_WHITESPACE)) { + $tokens[1] = new Token([\T_WHITESPACE, \substr($openingTagTokenContent, 5) . $tokens[1]->getContent()]); + } else { + $tokensToInsert[] = new Token([\T_WHITESPACE, \substr($openingTagTokenContent, 5)]); + } + + if ($tokens[$semicolonIndex + 1]->isGivenKind(\T_WHITESPACE)) { + $content = Preg::replace('/^(\\R?)(?=\\R)/', '', $tokens[$semicolonIndex + 1]->getContent()); + + $tokens->ensureWhitespaceAtIndex($semicolonIndex + 1, 0, $content); + } + + $tokens->clearRange($declareIndex + 1, $semicolonIndex); + TokenRemover::removeWithLinesIfPossible($tokens, $declareIndex); + + $tokens->insertAt(1, $tokensToInsert); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/EmptyFunctionBodyFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/EmptyFunctionBodyFixer.php new file mode 100644 index 00000000000..6dca86337be --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/EmptyFunctionBodyFixer.php @@ -0,0 +1,80 @@ +isTokenKindFound(\T_FUNCTION); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_FUNCTION)) { + continue; + } + + $openBraceIndex = $tokens->getNextTokenOfKind($index, ['{', ';']); + \assert(\is_int($openBraceIndex)); + if (!$tokens[$openBraceIndex]->equals('{')) { + continue; + } + + $closeBraceIndex = $tokens->getNextNonWhitespace($openBraceIndex); + \assert(\is_int($closeBraceIndex)); + if (!$tokens[$closeBraceIndex]->equals('}')) { + continue; + } + + $tokens->ensureWhitespaceAtIndex($openBraceIndex + 1, 0, ''); + + $beforeOpenBraceIndex = $tokens->getPrevNonWhitespace($openBraceIndex); + if (!$tokens[$beforeOpenBraceIndex]->isGivenKind([\T_COMMENT, \T_DOC_COMMENT])) { + $tokens->ensureWhitespaceAtIndex($openBraceIndex - 1, 1, ' '); + } + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ForeachUseValueFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ForeachUseValueFixer.php new file mode 100644 index 00000000000..b4f898165d7 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ForeachUseValueFixer.php @@ -0,0 +1,198 @@ +>='], + // other assignments + [\T_COALESCE_EQUAL, '??='], + [\T_CONCAT_EQUAL, '.='], + ]; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Value from `foreach` must not be used if possible.', + [new CodeSample( + <<<'PHP' + $value) { + $product *= $elements[$key]; + } + + PHP, + )], + '', + 'when the value is re-used or being sorted', + ); + } + + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAllTokenKindsFound([\T_FOREACH, \T_VARIABLE]); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_FOREACH)) { + continue; + } + + $openParenthesisIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($openParenthesisIndex)); + + $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex); + + $variables = $this->getForeachVariableNames($tokens, $openParenthesisIndex); + if ($variables === null) { + continue; + } + + $blockStartIndex = $tokens->getNextMeaningfulToken($closeParenthesisIndex); + \assert(\is_int($blockStartIndex)); + + if ($tokens[$blockStartIndex]->equals('{')) { + $blockEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $blockStartIndex); + } elseif ($tokens[$blockStartIndex]->equals(':')) { + $blockEndIndex = (new AlternativeSyntaxAnalyzer())->findAlternativeSyntaxBlockEnd($tokens, $index); + } else { + continue; + } + + $this->fixForeachBody($tokens, $blockStartIndex, $blockEndIndex, ...$variables); + } + } + + /** + * @return null|array{Token, string, string} + */ + private function getForeachVariableNames(Tokens $tokens, int $openParenthesisIndex): ?array + { + $arrayIndex = $tokens->getNextMeaningfulToken($openParenthesisIndex); + \assert(\is_int($arrayIndex)); + + $asIndex = $tokens->getNextMeaningfulToken($arrayIndex); + \assert(\is_int($asIndex)); + if (!$tokens[$asIndex]->isGivenKind(\T_AS)) { + return null; + } + + $keyIndex = $tokens->getNextMeaningfulToken($asIndex); + \assert(\is_int($keyIndex)); + if (!$tokens[$keyIndex]->isGivenKind(\T_VARIABLE)) { + return null; + } + + $doubleArrayIndex = $tokens->getNextMeaningfulToken($keyIndex); + \assert(\is_int($doubleArrayIndex)); + if (!$tokens[$doubleArrayIndex]->isGivenKind(\T_DOUBLE_ARROW)) { + return null; + } + + $variableIndex = $tokens->getNextMeaningfulToken($doubleArrayIndex); + \assert(\is_int($variableIndex)); + if (!$tokens[$variableIndex]->isGivenKind(\T_VARIABLE)) { + return null; + } + + return [ + $tokens[$arrayIndex], + $tokens[$keyIndex]->getContent(), + $tokens[$variableIndex]->getContent(), + ]; + } + + private function fixForeachBody( + Tokens $tokens, + int $openBraceIndex, + int $closeBraceIndex, + Token $arrayToken, + string $keyName, + string $variableName + ): void { + $sequence = [ + $arrayToken, '[', [\T_VARIABLE, $keyName], ']']; + + $index = $openBraceIndex; + while (($found = $tokens->findSequence($sequence, $index, $closeBraceIndex)) !== null) { + $startIndex = \array_key_first($found); + $endIndex = \array_key_last($found); + + $index = $endIndex; + + if ($this->isInUnset($tokens, $startIndex)) { + continue; + } + + $nextIndex = $tokens->getNextMeaningfulToken($endIndex); + \assert(\is_int($nextIndex)); + if ($tokens[$nextIndex]->equalsAny(self::NOT_ALLOWED_NEXT_TOKENS)) { + continue; + } + + $tokens->overrideRange($startIndex, $endIndex, [new Token([\T_VARIABLE, $variableName])]); + } + } + + private function isInUnset(Tokens $tokens, int $startIndex): bool + { + $openParenthesisIndex = $tokens->getPrevMeaningfulToken($startIndex); + \assert(\is_int($openParenthesisIndex)); + if (!$tokens[$openParenthesisIndex]->equals('(')) { + return false; + } + + $unsetIndex = $tokens->getPrevMeaningfulToken($openParenthesisIndex); + \assert(\is_int($unsetIndex)); + + return $tokens[$unsetIndex]->isGivenKind(\T_UNSET); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/InternalClassCasingFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/InternalClassCasingFixer.php new file mode 100644 index 00000000000..42b03765311 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/InternalClassCasingFixer.php @@ -0,0 +1,71 @@ +classReferenceNameCasingFixer = new ClassReferenceNameCasingFixer(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Classes defined internally by an extension or the core must be referenced with the correct case.', + [new CodeSample("classReferenceNameCasingFixer->getPriority(); + } + + public function isCandidate(Tokens $tokens): bool + { + return $this->classReferenceNameCasingFixer->isCandidate($tokens); + } + + public function isRisky(): bool + { + return $this->classReferenceNameCasingFixer->isRisky(); + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $this->classReferenceNameCasingFixer->fix($file, $tokens); + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [$this->classReferenceNameCasingFixer->getName()]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/IssetToArrayKeyExistsFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/IssetToArrayKeyExistsFixer.php new file mode 100644 index 00000000000..0fee4081a71 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/IssetToArrayKeyExistsFixer.php @@ -0,0 +1,105 @@ +isTokenKindFound(\T_ISSET); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_ISSET)) { + continue; + } + + if (\count(FunctionAnalyzer::getFunctionArguments($tokens, $index)) !== 1) { + continue; + } + + $openParenthesis = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($openParenthesis)); + + $closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); + + $closeBrackets = $tokens->getPrevMeaningfulToken($closeParenthesis); + \assert(\is_int($closeBrackets)); + if (!$tokens[$closeBrackets]->equals(']')) { + continue; + } + + $openBrackets = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $closeBrackets); + + $keyStartIndex = $tokens->getNextMeaningfulToken($openBrackets); + \assert(\is_int($keyStartIndex)); + $keyEndIndex = $tokens->getPrevMeaningfulToken($closeBrackets); + + $keyTokens = []; + for ($i = $keyStartIndex; $i <= $keyEndIndex; $i++) { + if ($tokens[$i]->equals('')) { + continue; + } + $keyTokens[] = $tokens[$i]; + } + $keyTokens[] = new Token(','); + $keyTokens[] = new Token([\T_WHITESPACE, ' ']); + + $tokens->clearRange($openBrackets, $closeBrackets); + $tokens->insertAt($openParenthesis + 1, $keyTokens); + $tokens[$index] = new Token([\T_STRING, 'array_key_exists']); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/MultilineCommentOpeningClosingAloneFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/MultilineCommentOpeningClosingAloneFixer.php new file mode 100644 index 00000000000..0836bc06348 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/MultilineCommentOpeningClosingAloneFixer.php @@ -0,0 +1,125 @@ +isAnyTokenKindsFound([\T_COMMENT, \T_DOC_COMMENT]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_COMMENT, \T_DOC_COMMENT])) { + continue; + } + + if (!Preg::match('/\\R/', $tokens[$index]->getContent())) { + continue; + } + + $this->fixOpening($tokens, $index); + $this->fixClosing($tokens, $index); + } + } + + private function fixOpening(Tokens $tokens, int $index): void + { + if (Preg::match('#^/\\*+\\R#', $tokens[$index]->getContent())) { + return; + } + + Preg::match('#\\R(\\h*)#', $tokens[$index]->getContent(), $matches); + + $indent = $matches[1] . '*'; + + Preg::match('#^(?/\\*+)(?.*?)(?\\R)(?.*)$#s', $tokens[$index]->getContent(), $matches); + if ($matches === []) { + return; + } + + $opening = $matches['opening']; + $beforeNewline = $matches['before_newline']; + $newline = $matches['newline']; + $afterNewline = $matches['after_newline']; + + if ($beforeNewline[0] !== ' ') { + $indent .= ' '; + } + + if (Preg::match('#^\\h+$#', $beforeNewline)) { + $insert = ''; + } else { + $insert = $newline . $indent . $beforeNewline; + } + + $newContent = $opening . $insert . $newline . $afterNewline; + + if ($newContent !== $tokens[$index]->getContent()) { + $tokens[$index] = new Token([Preg::match('~/\\*{2}\\s~', $newContent) ? \T_DOC_COMMENT : \T_COMMENT, $newContent]); + } + } + + private function fixClosing(Tokens $tokens, int $index): void + { + if (Preg::match('#\\R\\h*\\*+/$#', $tokens[$index]->getContent())) { + return; + } + + Preg::match('#\\R(\\h*)#', $tokens[$index]->getContent(), $matches); + + $indent = $matches[1]; + + $newContent = Preg::replace('#(\\R)(.+?)\\h*(\\*+/)$#', \sprintf('$1$2$1%s$3', $indent), $tokens[$index]->getContent()); + + if ($newContent !== $tokens[$index]->getContent()) { + $id = $tokens[$index]->getId(); + \assert(\is_int($id)); + + $tokens[$index] = new Token([$id, $newContent]); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/MultilinePromotedPropertiesFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/MultilinePromotedPropertiesFixer.php new file mode 100644 index 00000000000..289fccda141 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/MultilinePromotedPropertiesFixer.php @@ -0,0 +1,206 @@ + + * + * @phpstan-type _InputConfig array{keep_blank_lines?: bool, minimum_number_of_parameters?: int} + * @phpstan-type _Config array{keep_blank_lines: bool, minimum_number_of_parameters: int} + * + * @no-named-arguments + */ +final class MultilinePromotedPropertiesFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface +{ + private int $minimumNumberOfParameters = 1; + private bool $keepBlankLines = false; + private WhitespacesFixerConfig $whitespacesConfig; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Promoted properties must be on separate lines.', + [ + new VersionSpecificCodeSample( + 'setAllowedTypes(['bool']) + ->setDefault($this->keepBlankLines) + ->getOption(), + (new FixerOptionBuilder('minimum_number_of_parameters', 'minimum number of parameters in the constructor to fix')) + ->setAllowedTypes(['int']) + ->setDefault($this->minimumNumberOfParameters) + ->getOption(), + ]); + } + + /** + * @param array{minimum_number_of_parameters?: int, keep_blank_lines?: bool} $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('minimum_number_of_parameters', $configuration)) { + $this->minimumNumberOfParameters = $configuration['minimum_number_of_parameters']; + } + if (\array_key_exists('keep_blank_lines', $configuration)) { + $this->keepBlankLines = $configuration['keep_blank_lines']; + } + } + + public function setWhitespacesConfig(WhitespacesFixerConfig $config): void + { + $this->whitespacesConfig = $config; + } + + /** + * Must run before BracesPositionFixer. + * Must run after PromotedConstructorPropertyFixer. + */ + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAnyTokenKindsFound([ + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, + ]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $constructorAnalyzer = new ConstructorAnalyzer(); + + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CLASS)) { + continue; + } + + $constructorAnalysis = $constructorAnalyzer->findNonAbstractConstructor($tokens, $index); + if ($constructorAnalysis === null) { + continue; + } + + $openParenthesis = $tokens->getNextTokenOfKind($constructorAnalysis->getConstructorIndex(), ['(']); + \assert(\is_int($openParenthesis)); + $closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); + + if (!$this->shouldBeFixed($tokens, $openParenthesis, $closeParenthesis)) { + continue; + } + + $this->fixParameters($tokens, $openParenthesis, $closeParenthesis); + } + } + + private function shouldBeFixed(Tokens $tokens, int $openParenthesis, int $closeParenthesis): bool + { + $promotedParameterFound = false; + $minimumNumberOfParameters = 0; + for ($index = $openParenthesis + 1; $index < $closeParenthesis; $index++) { + if ($tokens[$index]->isGivenKind(\T_VARIABLE)) { + $minimumNumberOfParameters++; + } + if ( + $tokens[$index]->isGivenKind([ + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, + ]) + ) { + $promotedParameterFound = true; + } + } + + return $promotedParameterFound && $minimumNumberOfParameters >= $this->minimumNumberOfParameters; + } + + private function fixParameters(Tokens $tokens, int $openParenthesis, int $closeParenthesis): void + { + $indent = WhitespacesAnalyzer::detectIndent($tokens, $openParenthesis); + + $tokens->ensureWhitespaceAtIndex( + $closeParenthesis - 1, + 1, + $this->whitespacesConfig->getLineEnding() . $indent, + ); + + $index = $tokens->getPrevMeaningfulToken($closeParenthesis); + \assert(\is_int($index)); + + while ($index > $openParenthesis) { + $index = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($index)); + + $blockType = Tokens::detectBlockType($tokens[$index]); + if ($blockType !== null && !$blockType['isStart']) { + $index = $tokens->findBlockStart($blockType['type'], $index); + continue; + } + + if (!$tokens[$index]->equalsAny(['(', ','])) { + continue; + } + + $this->fixParameter($tokens, $index + 1, $indent); + } + } + + private function fixParameter(Tokens $tokens, int $index, string $indent): void + { + if ($this->keepBlankLines && $tokens[$index]->isWhitespace() && \str_contains($tokens[$index]->getContent(), "\n")) { + return; + } + + $tokens->ensureWhitespaceAtIndex( + $index, + 0, + $this->whitespacesConfig->getLineEnding() . $indent . $this->whitespacesConfig->getIndent(), + ); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoCommentedOutCodeFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoCommentedOutCodeFixer.php new file mode 100644 index 00000000000..2aba0806c74 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoCommentedOutCodeFixer.php @@ -0,0 +1,143 @@ +isAnyTokenKindsFound([\T_COMMENT, \T_DOC_COMMENT]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $commentsAnalyzer = new CommentsAnalyzer(); + + $index = 0; + while (++$index < $tokens->count()) { + if (!$tokens[$index]->isGivenKind([\T_COMMENT, \T_DOC_COMMENT])) { + continue; + } + + if (\strpos($tokens[$index]->getContent(), '/*') === 0) { + $commentIndices = [$index]; + } else { + $commentIndices = $commentsAnalyzer->getCommentBlockIndices($tokens, $index); + } + + $indicesToRemove = $this->getIndicesToRemove($tokens, $commentIndices); + + if ($indicesToRemove === []) { + continue; + } + + foreach ($indicesToRemove as $indexToRemove) { + TokenRemover::removeWithLinesIfPossible($tokens, $indexToRemove); + } + + $index = \max($indicesToRemove); + } + } + + /** + * @param list $commentIndices + * + * @return list + */ + private function getIndicesToRemove(Tokens $tokens, array $commentIndices): array + { + $indicesToRemove = []; + $testedIndices = []; + $content = ''; + + foreach ($commentIndices as $index) { + $content .= \PHP_EOL . $this->getContent($tokens[$index]); + $testedIndices[] = $index; + + if (\rtrim($content) === '') { + continue; + } + + foreach (Preg::split('/\\s+/u', $content) as $line) { + if (\filter_var($line, \FILTER_VALIDATE_URL) !== false) { + return []; + } + } + + if ( + $this->isCorrectSyntax('isCorrectSyntax('getContent(); + + if (\strpos($content, '/*') === 0) { + $content = Preg::replace('~^/\\*+|\\R\\s*\\*\\s+|\\*+/$~', \PHP_EOL, $content); + } + + return \ltrim($content, '#/'); + } + + private function isCorrectSyntax(string $content): bool + { + try { + @Tokens::fromCode($content); + } catch (\CompileError $error) { + return false; + } + + return true; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDoctrineMigrationsGeneratedCommentFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDoctrineMigrationsGeneratedCommentFixer.php new file mode 100644 index 00000000000..59f24ef394a --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDoctrineMigrationsGeneratedCommentFixer.php @@ -0,0 +1,89 @@ +addSql("UPDATE t1 SET col1 = col1 + 1"); + } + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql("UPDATE t1 SET col1 = col1 - 1"); + } +} +')], + '', + ); + } + + /** + * Must run before NoExtraBlankLinesFixer. + */ + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAnyTokenKindsFound([\T_COMMENT, \T_DOC_COMMENT]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_COMMENT, \T_DOC_COMMENT])) { + continue; + } + + if ( + \strpos($tokens[$index]->getContent(), 'Auto-generated Migration: Please modify to your needs!') === false + && \strpos($tokens[$index]->getContent(), 'this up() migration is auto-generated, please modify it to your needs') === false + && \strpos($tokens[$index]->getContent(), 'this down() migration is auto-generated, please modify it to your needs') === false + ) { + continue; + } + + TokenRemover::removeWithLinesIfPossible($tokens, $index); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDuplicatedArrayKeyFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDuplicatedArrayKeyFixer.php new file mode 100644 index 00000000000..1a6dddfb8d7 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDuplicatedArrayKeyFixer.php @@ -0,0 +1,153 @@ + + * + * @phpstan-type _InputConfig array{ignore_expressions?: bool} + * @phpstan-type _Config array{ignore_expressions: bool} + * + * @no-named-arguments + */ +final class NoDuplicatedArrayKeyFixer extends AbstractFixer implements ConfigurableFixerInterface +{ + private bool $ignoreExpressions = true; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'There must be no duplicate array keys.', + [new CodeSample(' 1, + "bar" => 2, + "foo" => 3, +]; +')], + '', + ); + } + + public function getConfigurationDefinition(): FixerConfigurationResolverInterface + { + return new FixerConfigurationResolver([ + (new FixerOptionBuilder('ignore_expressions', 'whether to keep duplicated expressions (as they might return different values) or not')) + ->setAllowedTypes(['bool']) + ->setDefault($this->ignoreExpressions) + ->getOption(), + ]); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('ignore_expressions', $configuration)) { + $this->ignoreExpressions = $configuration['ignore_expressions']; + } + } + + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAnyTokenKindsFound([\T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) { + continue; + } + + $this->fixArray($tokens, $index); + } + } + + private function fixArray(Tokens $tokens, int $index): void + { + $arrayAnalyzer = new ArrayAnalyzer(); + + $foundKeys = []; + foreach (\array_reverse($arrayAnalyzer->getElements($tokens, $index)) as $arrayElementAnalysis) { + $key = $this->getKeyContentIfPossible($tokens, $arrayElementAnalysis); + if ($key === null) { + continue; + } + if (\in_array($key, $foundKeys, true)) { + $startIndex = $arrayElementAnalysis->getKeyStartIndex(); + \assert(\is_int($startIndex)); + + $endIndex = $tokens->getNextMeaningfulToken($arrayElementAnalysis->getValueEndIndex()); + \assert(\is_int($endIndex)); + + if ($tokens[$endIndex + 1]->isWhitespace() && Preg::match('/^\\h+$/', $tokens[$endIndex + 1]->getContent())) { + $endIndex++; + } + + $tokens->clearRange($startIndex + 1, $endIndex); + TokenRemover::removeWithLinesIfPossible($tokens, $startIndex); + } + $foundKeys[] = $key; + } + } + + private function getKeyContentIfPossible(Tokens $tokens, ArrayElementAnalysis $arrayElementAnalysis): ?string + { + if ($arrayElementAnalysis->getKeyStartIndex() === null) { + return null; + } + + $keyEndIndex = $arrayElementAnalysis->getKeyEndIndex(); + \assert(\is_int($keyEndIndex)); + + $content = ''; + for ($index = $keyEndIndex; $index >= $arrayElementAnalysis->getKeyStartIndex(); $index--) { + if ($tokens[$index]->isWhitespace() || $tokens[$index]->isComment()) { + continue; + } + + if ($this->ignoreExpressions && $tokens[$index]->equalsAny([[\T_VARIABLE], '('])) { + return null; + } + + $content .= $tokens[$index]->getContent(); + } + + return $content; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDuplicatedImportsFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDuplicatedImportsFixer.php new file mode 100644 index 00000000000..cc96a625081 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoDuplicatedImportsFixer.php @@ -0,0 +1,96 @@ +isTokenKindFound(\T_USE); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens); + + foreach ((new NamespacesAnalyzer())->getDeclarations($tokens) as $namespace) { + $currentNamespaceUseDeclarations = \array_filter( + $useDeclarations, + static fn (NamespaceUseAnalysis $useDeclaration): bool => $useDeclaration->getStartIndex() >= $namespace->getScopeStartIndex() + && $useDeclaration->getEndIndex() <= $namespace->getScopeEndIndex(), + ); + + $foundDeclarations = []; + foreach ($currentNamespaceUseDeclarations as $useDeclaration) { + $key = \sprintf('key_%d_%s', $useDeclaration->getType(), $useDeclaration->getShortName()); + if (\in_array($key, $foundDeclarations, true)) { + $this->removeUseDeclaration($tokens, $useDeclaration); + } + $foundDeclarations[] = $key; + } + } + } + + private function removeUseDeclaration(Tokens $tokens, NamespaceUseAnalysis $useDeclaration): void + { + $removeUseDeclaration = static function ( + NoUnusedImportsFixer $noUnusedImportsFixer, + Tokens $tokens, + NamespaceUseAnalysis $useDeclaration + ): void { + // @phpstan-ignore method.private + $noUnusedImportsFixer->removeUseDeclaration($tokens, $useDeclaration); + }; + + $noUnusedImportsFixer = new NoUnusedImportsFixer(); + + $removeUseDeclaration = \Closure::bind($removeUseDeclaration, null, $noUnusedImportsFixer); + + $removeUseDeclaration($noUnusedImportsFixer, $tokens, $useDeclaration); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoImportFromGlobalNamespaceFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoImportFromGlobalNamespaceFixer.php new file mode 100644 index 00000000000..13b930ab9f5 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoImportFromGlobalNamespaceFixer.php @@ -0,0 +1,193 @@ +isTokenKindFound(\T_USE); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + foreach (\array_reverse((new NamespacesAnalyzer())->getDeclarations($tokens)) as $namespace) { + $this->fixImports($tokens, $namespace->getScopeStartIndex(), $namespace->getScopeEndIndex(), $namespace->getFullName() === ''); + } + } + + private function fixImports(Tokens $tokens, int $startIndex, int $endIndex, bool $isInGlobalNamespace): void + { + $importedClassesIndices = self::getImportCandidateIndices($tokens, $startIndex, $endIndex); + + if (!$isInGlobalNamespace) { + for ($index = $endIndex; $index > $startIndex; $index--) { + if ($tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + $importedClassesIndices = $this->updateComment($tokens, $importedClassesIndices, $index); + continue; + } + + if (!$tokens[$index]->isGivenKind(\T_STRING)) { + continue; + } + + $importedClassesIndices = $this->updateUsage($tokens, $importedClassesIndices, $index); + } + } + + self::clearImports($tokens, $importedClassesIndices); + } + + /** + * @return array + */ + private static function getImportCandidateIndices(Tokens $tokens, int $startIndex, int $endIndex): array + { + $importedClassesIndices = []; + + foreach (\array_keys($tokens->findGivenKind(\T_USE, $startIndex, $endIndex)) as $index) { + $classNameIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($classNameIndex)); + + if ($tokens[$classNameIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $classNameIndex = $tokens->getNextMeaningfulToken($classNameIndex); + \assert(\is_int($classNameIndex)); + } + + $semicolonIndex = $tokens->getNextMeaningfulToken($classNameIndex); + \assert(\is_int($semicolonIndex)); + + if (!$tokens[$semicolonIndex]->equals(';')) { + continue; + } + + $importedClassesIndices[$tokens[$classNameIndex]->getContent()] = $classNameIndex; + } + + return $importedClassesIndices; + } + + /** + * @param array $importedClassesIndices + * + * @return array + */ + private function updateComment(Tokens $tokens, array $importedClassesIndices, int $index): array + { + $content = $tokens[$index]->getContent(); + + foreach ($importedClassesIndices as $importedClassName => $importedClassIndex) { + $content = Preg::replace(\sprintf('/\\b(?getContent()) { + $tokens[$index] = new Token([\T_DOC_COMMENT, $content]); + } + + return $importedClassesIndices; + } + + /** + * @param array $importedClassesIndices + * + * @return array + */ + private function updateUsage(Tokens $tokens, array $importedClassesIndices, int $index): array + { + if (!\in_array($tokens[$index]->getContent(), \array_keys($importedClassesIndices), true)) { + return $importedClassesIndices; + } + + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + + if ($tokens[$prevIndex]->isGivenKind([\T_CONST, \T_DOUBLE_COLON, \T_FUNCTION, \T_NS_SEPARATOR, \T_OBJECT_OPERATOR, \T_USE])) { + return $importedClassesIndices; + } + + $nextIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($nextIndex)); + + if ($tokens[$nextIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $importedClassesIndices[$tokens[$index]->getContent()] = null; + + return $importedClassesIndices; + } + + $tokens->insertAt($index, new Token([\T_NS_SEPARATOR, '\\'])); + + return $importedClassesIndices; + } + + /** + * @param array $importedClassesIndices + */ + private static function clearImports(Tokens $tokens, array $importedClassesIndices): void + { + foreach ($importedClassesIndices as $importedClassIndex) { + if ($importedClassIndex === null) { + continue; + } + $useIndex = $tokens->getPrevTokenOfKind($importedClassIndex, [[\T_USE]]); + \assert(\is_int($useIndex)); + + $semicolonIndex = $tokens->getNextTokenOfKind($importedClassIndex, [';']); + \assert(\is_int($semicolonIndex)); + + $tokens->clearRange($useIndex, $semicolonIndex); + TokenRemover::removeWithLinesIfPossible($tokens, $useIndex); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoLeadingSlashInGlobalNamespaceFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoLeadingSlashInGlobalNamespaceFixer.php new file mode 100644 index 00000000000..b00e3965971 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoLeadingSlashInGlobalNamespaceFixer.php @@ -0,0 +1,115 @@ +isTokenKindFound(\T_NS_SEPARATOR); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $index = 0; + while (++$index < $tokens->count()) { + $index = $this->skipNamespacedCode($tokens, $index); + + if (!$this->isToRemove($tokens, $index)) { + continue; + } + + $tokens->clearTokenAndMergeSurroundingWhitespace($index); + } + } + + private function isToRemove(Tokens $tokens, int $index): bool + { + if (!$tokens[$index]->isGivenKind(\T_NS_SEPARATOR)) { + return false; + } + + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + + if ($tokens[$prevIndex]->isGivenKind(\T_STRING)) { + return false; + } + if ($tokens[$prevIndex]->isGivenKind([\T_NEW, CT::T_NULLABLE_TYPE, CT::T_TYPE_COLON])) { + return true; + } + + $nextIndex = $tokens->getTokenNotOfKindSibling($index, 1, [[\T_COMMENT], [\T_DOC_COMMENT], [\T_NS_SEPARATOR], [\T_STRING], [\T_WHITESPACE]]); + \assert(\is_int($nextIndex)); + + if ($tokens[$nextIndex]->isGivenKind(\T_DOUBLE_COLON)) { + return true; + } + + return $tokens[$prevIndex]->equalsAny(['(', ',', [CT::T_TYPE_ALTERNATION]]) && $tokens[$nextIndex]->isGivenKind([\T_VARIABLE, CT::T_TYPE_ALTERNATION]); + } + + private function skipNamespacedCode(Tokens $tokens, int $index): int + { + if (!$tokens[$index]->isGivenKind(\T_NAMESPACE)) { + return $index; + } + + $nextIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($nextIndex)); + + if ($tokens[$nextIndex]->equals('{')) { + return $nextIndex; + } + + $nextIndex = $tokens->getNextTokenOfKind($index, ['{', ';']); + \assert(\is_int($nextIndex)); + + if ($tokens[$nextIndex]->equals(';')) { + return $tokens->count() - 1; + } + + return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoNullableBooleanTypeFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoNullableBooleanTypeFixer.php new file mode 100644 index 00000000000..b8f6a09afff --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoNullableBooleanTypeFixer.php @@ -0,0 +1,78 @@ +isTokenKindFound(\T_STRING); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if ($tokens[$index]->getContent() !== '?') { + continue; + } + + $nextIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($nextIndex)); + + if (!$tokens[$nextIndex]->equals([\T_STRING, 'bool'], false) && !$tokens[$nextIndex]->equals([\T_STRING, 'boolean'], false)) { + continue; + } + + $nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex); + \assert(\is_int($nextNextIndex)); + + if (!$tokens[$nextNextIndex]->isGivenKind(\T_VARIABLE) && $tokens[$nextNextIndex]->getContent() !== '{') { + continue; + } + + $tokens->clearTokenAndMergeSurroundingWhitespace($index); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoPhpStormGeneratedCommentFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoPhpStormGeneratedCommentFixer.php new file mode 100644 index 00000000000..577618fdf23 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoPhpStormGeneratedCommentFixer.php @@ -0,0 +1,72 @@ +isAnyTokenKindsFound([\T_COMMENT, \T_DOC_COMMENT]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_COMMENT, \T_DOC_COMMENT])) { + continue; + } + + if (!Preg::match('/\\*\\h+Created by( JetBrains)? PhpStorm/i', $tokens[$index]->getContent(), $matches)) { + continue; + } + + TokenRemover::removeWithLinesIfPossible($tokens, $index); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoReferenceInFunctionDefinitionFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoReferenceInFunctionDefinitionFixer.php new file mode 100644 index 00000000000..0a647141915 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoReferenceInFunctionDefinitionFixer.php @@ -0,0 +1,92 @@ +isAnyTokenKindsFound([\T_FUNCTION]); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_FUNCTION)) { + continue; + } + + $indices = $this->getArgumentStartIndices($tokens, $index); + + foreach ($indices as $i) { + if ($tokens[$i]->getContent() === '&') { + $tokens->clearTokenAndMergeSurroundingWhitespace($i); + } + } + } + } + + /** + * @return list + */ + private function getArgumentStartIndices(Tokens $tokens, int $functionNameIndex): array + { + $argumentsAnalyzer = new ArgumentsAnalyzer(); + + $openParenthesis = $tokens->getNextTokenOfKind($functionNameIndex, ['(']); + \assert(\is_int($openParenthesis)); + + $closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); + + $indices = []; + + foreach (\array_keys($argumentsAnalyzer->getArguments($tokens, $openParenthesis, $closeParenthesis)) as $startIndexCandidate) { + $index = $tokens->getNextMeaningfulToken($startIndexCandidate - 1); + \assert(\is_int($index)); + + $indices[] = $index; + } + + return $indices; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoSuperfluousConcatenationFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoSuperfluousConcatenationFixer.php new file mode 100644 index 00000000000..3376e71a341 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoSuperfluousConcatenationFixer.php @@ -0,0 +1,233 @@ + + * + * @phpstan-type _InputConfig array{allow_preventing_trailing_spaces?: bool} + * @phpstan-type _Config array{allow_preventing_trailing_spaces: bool} + * + * @no-named-arguments + */ +final class NoSuperfluousConcatenationFixer extends AbstractFixer implements ConfigurableFixerInterface +{ + private bool $allowPreventingTrailingSpaces = false; + private bool $keepConcatenationForDifferentQuotes = false; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'There must be no superfluous concatenation of literal strings.', + [new CodeSample("setAllowedTypes(['bool']) + ->setDefault($this->allowPreventingTrailingSpaces) + ->getOption(), + (new FixerOptionBuilder('keep_concatenation_for_different_quotes', 'whether to keep concatenation if single-quoted and double-quoted would be concatenated')) + ->setAllowedTypes(['bool']) + ->setDefault($this->keepConcatenationForDifferentQuotes) + ->getOption(), + ]); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('allow_preventing_trailing_spaces', $configuration)) { + $this->allowPreventingTrailingSpaces = $configuration['allow_preventing_trailing_spaces']; + } + if (\array_key_exists('keep_concatenation_for_different_quotes', $configuration)) { + $this->keepConcatenationForDifferentQuotes = $configuration['keep_concatenation_for_different_quotes']; + } + } + + /** + * Must run after SingleLineThrowFixer. + */ + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAllTokenKindsFound(['.', \T_CONSTANT_ENCAPSED_STRING]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->equals('.')) { + continue; + } + + $firstIndex = $this->getFirstIndex($tokens, $index); + if ($firstIndex === null) { + continue; + } + + $secondIndex = $this->getSecondIndex($tokens, $index); + if ($secondIndex === null) { + continue; + } + + if ( + $this->keepConcatenationForDifferentQuotes + && \substr($tokens[$firstIndex]->getContent(), 0, 1) !== \substr($tokens[$secondIndex]->getContent(), 0, 1) + ) { + continue; + } + + $this->fixConcat($tokens, $firstIndex, $secondIndex); + } + } + + private function getFirstIndex(Tokens $tokens, int $index): ?int + { + $firstIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($firstIndex)); + + if (!$tokens[$firstIndex]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) { + return null; + } + if (!$this->areOnlyHorizontalWhitespacesBetween($tokens, $firstIndex, $index)) { + return null; + } + + return $firstIndex; + } + + private function getSecondIndex(Tokens $tokens, int $index): ?int + { + $secondIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($secondIndex)); + + if (!$tokens[$secondIndex]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) { + return null; + } + if (!$this->areOnlyHorizontalWhitespacesBetween($tokens, $index, $secondIndex)) { + return null; + } + + return $secondIndex; + } + + private function areOnlyHorizontalWhitespacesBetween(Tokens $tokens, int $indexStart, int $indexEnd): bool + { + for ($index = $indexStart + 1; $index < $indexEnd; $index++) { + if (!$tokens[$index]->isGivenKind(\T_WHITESPACE)) { + return false; + } + if (Preg::match('/\\R/', $tokens[$index]->getContent())) { + return false; + } + } + + return true; + } + + private function fixConcat(Tokens $tokens, int $firstIndex, int $secondIndex): void + { + $prefix = ''; + $firstContent = $tokens[$firstIndex]->getContent(); + $secondContent = $tokens[$secondIndex]->getContent(); + + if ( + $this->allowPreventingTrailingSpaces + && Preg::match('/\\h(\\\'|")$/', $firstContent) + && Preg::match('/^(\\\'|")\\R/', $secondContent) + ) { + return; + } + + if (\strtolower($firstContent[0]) === 'b') { + $prefix = $firstContent[0]; + $firstContent = \ltrim($firstContent, 'bB'); + } + + $secondContent = \ltrim($secondContent, 'bB'); + + $border = $firstContent[0] === '"' || $secondContent[0] === '"' ? '"' : "'"; + + $tokens->overrideRange( + $firstIndex, + $secondIndex, + [ + new Token( + [\T_CONSTANT_ENCAPSED_STRING, + $prefix . $border . $this->getContentForBorder($firstContent, $border, true) . $this->getContentForBorder($secondContent, $border, false) . $border, + ], + ), + ], + ); + } + + private function getContentForBorder(string $content, string $targetBorder, bool $escapeDollarWhenIsLastCharacter): string + { + $currentBorder = $content[0]; + $content = \substr($content, 1, -1); + + if ($content === '') { + return ''; + } + + if ($currentBorder === '"') { + if ($escapeDollarWhenIsLastCharacter && $content[\strlen($content) - 1] === '$') { + $content = \substr($content, 0, -1) . '\\$'; + } + + return $content; + } + if ($targetBorder === "'") { + return $content; + } + + // unescape single quote + $content = \str_replace('\\\'', '\'', $content); + + // escape dollar sign + $content = \str_replace('$', '\\$', $content); + + // escape double quote + return Preg::replace( + '/(?isAnyTokenKindsFound([\T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN, CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, '(']); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index >= 0; $index--) { + if (!$tokens[$index]->equalsAny([')', [CT::T_ARRAY_SQUARE_BRACE_CLOSE], [CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE]])) { + continue; + } + + $this->removeCommas($tokens, $index); + } + } + + private function removeCommas(Tokens $tokens, int $index): void + { + $commaIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($commaIndex)); + + while ($tokens[$commaIndex]->equals(',')) { + if ($tokens->isPartialCodeMultiline($commaIndex, $index)) { + return; + } + + $tokens->removeLeadingWhitespace($commaIndex); + $tokens->removeTrailingWhitespace($commaIndex); + $tokens->clearAt($commaIndex); + + $commaIndex = $tokens->getPrevMeaningfulToken($commaIndex); + \assert(\is_int($commaIndex)); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessCommentFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessCommentFixer.php new file mode 100644 index 00000000000..4681eeac613 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessCommentFixer.php @@ -0,0 +1,137 @@ +isAnyTokenKindsFound([\T_COMMENT, \T_DOC_COMMENT]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_COMMENT, \T_DOC_COMMENT])) { + continue; + } + + $newContent = $this->getNewContent($tokens, $index); + + if ($newContent === $tokens[$index]->getContent()) { + continue; + } + + $id = $tokens[$index]->getId(); + \assert(\is_int($id)); + + $tokens[$index] = new Token([$id, $newContent]); + } + } + + private function getNewContent(Tokens $tokens, int $index): string + { + $content = $tokens[$index]->getContent(); + + $nextIndex = $tokens->getTokenNotOfKindSibling( + $index, + 1, + [[\T_WHITESPACE], [\T_COMMENT], [\T_ABSTRACT], [\T_FINAL], [\T_PUBLIC], [\T_PROTECTED], [\T_PRIVATE], [\T_STATIC]], + ); + + if ($nextIndex === null) { + return $content; + } + + if ($tokens[$nextIndex]->isGivenKind([\T_CLASS, \T_INTERFACE, \T_TRAIT])) { + $classyNameIndex = $tokens->getNextMeaningfulToken($nextIndex); + \assert(\is_int($classyNameIndex)); + + $content = Preg::replace( + \sprintf('~ + \\R? + (?<=\\n|\\r|\\r\\n|^\\#|^/{2}|^/\\*[^\\*\\s]|^/\\*{2}) + \\h*\\**\\h* + ( + (class|interface|trait)\\h+([a-zA-Z\\d\\\\]+) + | + %s + ) + \\.? + \\h* + (?=\\R|\\*/$|$) + ~ix', $tokens[$classyNameIndex]->getContent()), + '', + $content, + ); + } elseif ($tokens[$nextIndex]->isGivenKind(\T_FUNCTION)) { + $content = Preg::replace( + '/\\R?(?<=\\n|\\r|\\r\\n|^#|^\\/\\/|^\\/\\*|^\\/\\*\\*)\\h+\\**\\h*((adds?|gets?|removes?|sets?)\\h+[A-Za-z0-9\\\\_]+|([A-Za-z0-9\\\\_]+\\h+)?constructor).?(?=\\R|$)/i', + '', + $content, + ); + } else { + return $content; + } + + if ($content === '/***/') { + $content = '/** */'; + } + + return $content; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessDirnameCallFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessDirnameCallFixer.php new file mode 100644 index 00000000000..26114486b03 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessDirnameCallFixer.php @@ -0,0 +1,166 @@ +isTokenKindFound(\T_DIR); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_DIR)) { + continue; + } + + $prevInserts = $this->getPrevTokensUpdates($tokens, $index); + if ($prevInserts === null) { + continue; + } + + $nextInserts = $this->getNextTokensUpdates($tokens, $index); + if ($nextInserts === null) { + continue; + } + + foreach ($prevInserts + $nextInserts as $i => $content) { + if ($content === '') { + $tokens->clearTokenAndMergeSurroundingWhitespace($i); + } else { + $tokens[$i] = new Token([\T_CONSTANT_ENCAPSED_STRING, $content]); + } + } + } + } + + /** + * @return null|array + */ + private function getPrevTokensUpdates(Tokens $tokens, int $index): ?array + { + $updates = []; + + $openParenthesisIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($openParenthesisIndex)); + if (!$tokens[$openParenthesisIndex]->equals('(')) { + return null; + } + $updates[$openParenthesisIndex] = ''; + + $dirnameCallIndex = $tokens->getPrevMeaningfulToken($openParenthesisIndex); + \assert(\is_int($dirnameCallIndex)); + if (!$tokens[$dirnameCallIndex]->equals([\T_STRING, 'dirname'], false)) { + return null; + } + if (!(new FunctionsAnalyzer())->isGlobalFunctionCall($tokens, $dirnameCallIndex)) { + return null; + } + $updates[$dirnameCallIndex] = ''; + + $namespaceSeparatorIndex = $tokens->getPrevMeaningfulToken($dirnameCallIndex); + \assert(\is_int($namespaceSeparatorIndex)); + if ($tokens[$namespaceSeparatorIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $updates[$namespaceSeparatorIndex] = ''; + } + + return $updates; + } + + /** + * @return null|array + */ + private function getNextTokensUpdates(Tokens $tokens, int $index): ?array + { + $depthLevel = 1; + $updates = []; + + $commaOrClosingParenthesisIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($commaOrClosingParenthesisIndex)); + if ($tokens[$commaOrClosingParenthesisIndex]->equals(',')) { + $updates[$commaOrClosingParenthesisIndex] = ''; + $afterCommaIndex = $tokens->getNextMeaningfulToken($commaOrClosingParenthesisIndex); + \assert(\is_int($afterCommaIndex)); + if ($tokens[$afterCommaIndex]->isGivenKind(\T_LNUMBER)) { + $depthLevel = (int) $tokens[$afterCommaIndex]->getContent(); + $updates[$afterCommaIndex] = ''; + $commaOrClosingParenthesisIndex = $tokens->getNextMeaningfulToken($afterCommaIndex); + \assert(\is_int($commaOrClosingParenthesisIndex)); + } + } + + if ($tokens[$commaOrClosingParenthesisIndex]->equals(',')) { + $updates[$commaOrClosingParenthesisIndex] = ''; + $commaOrClosingParenthesisIndex = $tokens->getNextMeaningfulToken($commaOrClosingParenthesisIndex); + \assert(\is_int($commaOrClosingParenthesisIndex)); + } + $closingParenthesisIndex = $commaOrClosingParenthesisIndex; + + if (!$tokens[$closingParenthesisIndex]->equals(')')) { + return null; + } + $updates[$closingParenthesisIndex] = ''; + + $concatenationIndex = $tokens->getNextMeaningfulToken($closingParenthesisIndex); + \assert(\is_int($concatenationIndex)); + if (!$tokens[$concatenationIndex]->equals('.')) { + return null; + } + + $stringIndex = $tokens->getNextMeaningfulToken($concatenationIndex); + \assert(\is_int($stringIndex)); + if (!$tokens[$stringIndex]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) { + return null; + } + + $stringContent = $tokens[$stringIndex]->getContent(); + $updates[$stringIndex] = \substr($stringContent, 0, 1) . \str_repeat('/..', $depthLevel) . \substr($stringContent, 1); + + return $updates; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessDoctrineRepositoryCommentFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessDoctrineRepositoryCommentFixer.php new file mode 100644 index 00000000000..e50ba2c5048 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessDoctrineRepositoryCommentFixer.php @@ -0,0 +1,71 @@ +isTokenKindFound(\T_DOC_COMMENT); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + continue; + } + + if (\strpos($tokens[$index]->getContent(), 'This class was generated by the Doctrine ORM') === false) { + continue; + } + + TokenRemover::removeWithLinesIfPossible($tokens, $index); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessParenthesisFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessParenthesisFixer.php new file mode 100644 index 00000000000..c942b08dc21 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessParenthesisFixer.php @@ -0,0 +1,231 @@ +isAnyTokenKindsFound(['(', CT::T_BRACE_CLASS_INSTANTIATION_OPEN]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = 0; $index < $tokens->count(); $index++) { + if (!$tokens[$index]->equalsAny(['(', [CT::T_BRACE_CLASS_INSTANTIATION_OPEN]])) { + continue; + } + + /** @var array{type: Tokens::BLOCK_TYPE_*, isStart: bool} $blockType */ + $blockType = Tokens::detectBlockType($tokens[$index]); + $blockEndIndex = $tokens->findBlockEnd($blockType['type'], $index); + + if (!$this->isBlockToRemove($tokens, $index, $blockEndIndex)) { + continue; + } + + $this->clearWhitespace($tokens, $index + 1); + $this->clearWhitespace($tokens, $blockEndIndex - 1); + $tokens->clearTokenAndMergeSurroundingWhitespace($index); + $tokens->clearTokenAndMergeSurroundingWhitespace($blockEndIndex); + + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + + if ($tokens[$prevIndex]->isGivenKind([\T_RETURN, \T_THROW])) { + $tokens->ensureWhitespaceAtIndex($prevIndex + 1, 0, ' '); + } + } + } + + private function isBlockToRemove(Tokens $tokens, int $startIndex, int $endIndex): bool + { + if ($this->isParenthesisBlockInside($tokens, $startIndex, $endIndex)) { + return true; + } + + $prevStartIndex = $tokens->getPrevMeaningfulToken($startIndex); + \assert(\is_int($prevStartIndex)); + $nextEndIndex = $tokens->getNextMeaningfulToken($endIndex); + \assert(\is_int($nextEndIndex)); + + if ((new BlocksAnalyzer())->isBlock($tokens, $prevStartIndex, $nextEndIndex)) { + return true; + } + + if ($tokens[$nextEndIndex]->equalsAny(['(', '{', [\T_DOUBLE_ARROW], [CT::T_USE_LAMBDA], [CT::T_TYPE_COLON]])) { + return false; + } + + if ($this->isForbiddenBeforeOpenParenthesis($tokens, $prevStartIndex)) { + return false; + } + + if ($this->isExpressionInside($tokens, $startIndex, $endIndex)) { + return true; + } + + if ($this->hasLowPrecedenceLogicOperator($tokens, $startIndex, $endIndex)) { + return false; + } + + return $tokens[$prevStartIndex]->equalsAny(['=', [\T_RETURN], [\T_THROW]]) && $tokens[$nextEndIndex]->equals(';'); + } + + private function isForbiddenBeforeOpenParenthesis(Tokens $tokens, int $index): bool + { + if ( + $tokens[$index]->isGivenKind([ + \T_ARRAY, + \T_CLASS, + \T_ELSEIF, + \T_EMPTY, + \T_EVAL, + \T_EXIT, + \T_HALT_COMPILER, + \T_IF, + \T_ISSET, + \T_LIST, + \T_STATIC, + \T_STRING, + \T_SWITCH, + \T_UNSET, + \T_VARIABLE, + \T_WHILE, + CT::T_CLASS_CONSTANT, + ]) + ) { + return true; + } + + /** @var null|array{isStart: bool, type: int} $blockType */ + $blockType = Tokens::detectBlockType($tokens[$index]); + + return $blockType !== null && !$blockType['isStart']; + } + + private function isParenthesisBlockInside(Tokens $tokens, int $startIndex, int $endIndex): bool + { + $nextStartIndex = $tokens->getNextMeaningfulToken($startIndex); + \assert(\is_int($nextStartIndex)); + + if (!$tokens[$nextStartIndex]->equalsAny(['(', [CT::T_BRACE_CLASS_INSTANTIATION_OPEN]])) { + return false; + } + + $prevIndex = $tokens->getPrevMeaningfulToken($endIndex); + \assert(\is_int($prevIndex)); + + return (new BlocksAnalyzer())->isBlock($tokens, $nextStartIndex, $prevIndex); + } + + private function isExpressionInside(Tokens $tokens, int $startIndex, int $endIndex): bool + { + $index = $tokens->getNextMeaningfulToken($startIndex); + \assert(\is_int($index)); + + while ($index < $endIndex) { + if ( + !$tokens[$index]->isGivenKind([ + \T_CONSTANT_ENCAPSED_STRING, + \T_DNUMBER, + \T_DOUBLE_COLON, + \T_LNUMBER, + \T_OBJECT_OPERATOR, + \T_STRING, + \T_VARIABLE, + ]) && !$tokens[$index]->isMagicConstant() + ) { + return false; + } + + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + } + + return true; + } + + private function hasLowPrecedenceLogicOperator(Tokens $tokens, int $startIndex, int $endIndex): bool + { + $index = $tokens->getNextMeaningfulToken($startIndex); + \assert(\is_int($index)); + + while ($index < $endIndex) { + if ( + $tokens[$index]->isGivenKind([ + \T_LOGICAL_XOR, + \T_LOGICAL_AND, + \T_LOGICAL_OR, + ]) + ) { + return true; + } + + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + } + + return false; + } + + private function clearWhitespace(Tokens $tokens, int $index): void + { + if (!$tokens[$index]->isWhitespace()) { + return; + } + + $prevIndex = $tokens->getNonEmptySibling($index, -1); + \assert(\is_int($prevIndex)); + + if ($tokens[$prevIndex]->isComment()) { + $tokens->ensureWhitespaceAtIndex($index, 0, \rtrim($tokens[$index]->getContent(), " \t")); + + return; + } + + $tokens->clearAt($index); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessStrlenFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessStrlenFixer.php new file mode 100644 index 00000000000..8a0396ad67c --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessStrlenFixer.php @@ -0,0 +1,183 @@ + 0; +', + ), + ], + '', + 'when the function `strlen` is overridden', + ); + } + + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isTokenKindFound(\T_LNUMBER) && $tokens->isAnyTokenKindsFound(['>', '<', \T_IS_IDENTICAL, \T_IS_NOT_IDENTICAL, \T_IS_EQUAL, \T_IS_NOT_EQUAL]); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $argumentsAnalyzer = new ArgumentsAnalyzer(); + $functionsAnalyzer = new FunctionsAnalyzer(); + + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->equalsAny([[\T_STRING, 'strlen'], [\T_STRING, 'mb_strlen']], false)) { + continue; + } + + if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) { + continue; + } + + $openParenthesisIndex = $tokens->getNextTokenOfKind($index, ['(']); + \assert(\is_int($openParenthesisIndex)); + + $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex); + + if ($argumentsAnalyzer->countArguments($tokens, $openParenthesisIndex, $closeParenthesisIndex) !== 1) { + continue; + } + + $tokensToRemove = [ + $index => 1, + $openParenthesisIndex => 1, + $closeParenthesisIndex => -1, + ]; + + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + + $startIndex = $index; + if ($tokens[$prevIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $startIndex = $prevIndex; + $tokensToRemove[$prevIndex] = 1; + } + + if (!$this->transformCondition($tokens, $startIndex, $closeParenthesisIndex)) { + continue; + } + + $this->removeTokenAndSiblingWhitespace($tokens, $tokensToRemove); + } + } + + private function transformCondition(Tokens $tokens, int $startIndex, int $endIndex): bool + { + if ($this->transformConditionLeft($tokens, $startIndex)) { + return true; + } + + return $this->transformConditionRight($tokens, $endIndex); + } + + private function transformConditionLeft(Tokens $tokens, int $index): bool + { + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + + $changeCondition = false; + if ($tokens[$prevIndex]->equals('<')) { + $changeCondition = true; + } elseif (!$tokens[$prevIndex]->isGivenKind([\T_IS_IDENTICAL, \T_IS_NOT_IDENTICAL, \T_IS_EQUAL, \T_IS_NOT_EQUAL])) { + return false; + } + + $prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex); + \assert(\is_int($prevPrevIndex)); + + if (!$tokens[$prevPrevIndex]->equals([\T_LNUMBER, '0'])) { + return false; + } + + if ($changeCondition) { + $tokens[$prevIndex] = new Token([\T_IS_NOT_IDENTICAL, '!==']); + } + + $tokens[$prevPrevIndex] = new Token([\T_CONSTANT_ENCAPSED_STRING, '\'\'']); + + return true; + } + + private function transformConditionRight(Tokens $tokens, int $index): bool + { + $nextIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($nextIndex)); + + $changeCondition = false; + if ($tokens[$nextIndex]->equals('>')) { + $changeCondition = true; + } elseif (!$tokens[$nextIndex]->isGivenKind([\T_IS_IDENTICAL, \T_IS_NOT_IDENTICAL, \T_IS_EQUAL, \T_IS_NOT_EQUAL])) { + return false; + } + + $nextNextIndex = $tokens->getNextMeaningfulToken($nextIndex); + \assert(\is_int($nextNextIndex)); + + if (!$tokens[$nextNextIndex]->equals([\T_LNUMBER, '0'])) { + return false; + } + + if ($changeCondition) { + $tokens[$nextIndex] = new Token([\T_IS_NOT_IDENTICAL, '!==']); + } + + $tokens[$nextNextIndex] = new Token([\T_CONSTANT_ENCAPSED_STRING, '\'\'']); + + return true; + } + + /** + * @param array $tokensToRemove + */ + private function removeTokenAndSiblingWhitespace(Tokens $tokens, array $tokensToRemove): void + { + foreach ($tokensToRemove as $index => $direction) { + $tokens->clearAt($index); + + if ($tokens[$index + $direction]->isWhitespace()) { + $tokens->clearAt($index + $direction); + } + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessWriteVisibilityFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessWriteVisibilityFixer.php new file mode 100644 index 00000000000..519ba921854 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NoUselessWriteVisibilityFixer.php @@ -0,0 +1,107 @@ +> */ + private array $predecessorKindMap; + + public function __construct() + { + if (\defined('T_PUBLIC_SET')) { + $this->predecessorKindMap = [ + \T_PUBLIC_SET => [\T_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC], + \T_PROTECTED_SET => [\T_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED], + \T_PRIVATE_SET => [\T_PRIVATE, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE], + ]; + } + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'There must be no useless write visibility.', + [new CodeSample( + <<<'PHP' + isAnyTokenKindsFound(\array_keys($this->predecessorKindMap)); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + foreach ($tokens->findGivenKind(\array_keys($this->predecessorKindMap)) as $kind => $elements) { + foreach (\array_keys($elements) as $index) { + $this->fixVisibility($tokens, $index, $kind, $kind === \T_PUBLIC_SET); + } + } + } + + private function fixVisibility(Tokens $tokens, int $index, int $kind, bool $makePublicIfNone): void + { + $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + if ($tokens[$prevIndex]->isGivenKind(\T_ABSTRACT)) { + $prevIndex = $tokens->getPrevMeaningfulToken($prevIndex); + \assert(\is_int($prevIndex)); + } + + if (!$tokens[$prevIndex]->isGivenKind($this->predecessorKindMap[$kind])) { + if ($makePublicIfNone) { + $prevDeciderIndex = $tokens->getPrevTokenOfKind($index, ['(', ';', '{']); + \assert(\is_int($prevDeciderIndex)); + $kind = $tokens[$prevDeciderIndex]->equals('(') ? CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC : \T_PUBLIC; + $tokens[$index] = new Token([$kind, 'public']); + } + + return; + } + + $tokens->clearAt($index); + + if ($tokens[$index + 1]->isWhitespace()) { + $tokens->clearAt($index + 1); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NumericLiteralSeparatorFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NumericLiteralSeparatorFixer.php new file mode 100644 index 00000000000..05317272220 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/NumericLiteralSeparatorFixer.php @@ -0,0 +1,233 @@ + + * + * @phpstan-type _InputConfig array{binary?: bool, decimal?: bool, float?: bool, hexadecimal?: bool, octal?: bool} + * @phpstan-type _Config array{binary: bool, decimal: bool, float: bool, hexadecimal: bool, octal: bool} + * + * @no-named-arguments + */ +final class NumericLiteralSeparatorFixer extends AbstractFixer implements ConfigurableFixerInterface, DeprecatedFixerInterface +{ + private ?bool $binarySeparator = false; + private ?bool $decimalSeparator = false; + private ?bool $floatSeparator = false; + private ?bool $hexadecimalSeparator = false; + private ?bool $octalSeparator = false; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Numeric literals must have configured separators.', + [new VersionSpecificCodeSample( + 'setAllowedTypes(['bool', 'null']) + ->setDefault($this->binarySeparator) + ->getOption(), + (new FixerOptionBuilder('decimal', 'whether add, remove or ignore separators in decimal numbers.')) + ->setAllowedTypes(['bool', 'null']) + ->setDefault($this->decimalSeparator) + ->getOption(), + (new FixerOptionBuilder('float', 'whether add, remove or ignore separators in float numbers.')) + ->setAllowedTypes(['bool', 'null']) + ->setDefault($this->floatSeparator) + ->getOption(), + (new FixerOptionBuilder('hexadecimal', 'whether add, remove or ignore separators in hexadecimal numbers.')) + ->setAllowedTypes(['bool', 'null']) + ->setDefault($this->hexadecimalSeparator) + ->getOption(), + (new FixerOptionBuilder('octal', 'whether add, remove or ignore separators in octal numbers.')) + ->setAllowedTypes(['bool', 'null']) + ->setDefault($this->octalSeparator) + ->getOption(), + ]); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + $this->binarySeparator = \array_key_exists('binary', $configuration) ? $configuration['binary'] : $this->binarySeparator; + $this->decimalSeparator = \array_key_exists('decimal', $configuration) ? $configuration['decimal'] : $this->decimalSeparator; + $this->floatSeparator = \array_key_exists('float', $configuration) ? $configuration['float'] : $this->floatSeparator; + $this->hexadecimalSeparator = \array_key_exists('hexadecimal', $configuration) ? $configuration['hexadecimal'] : $this->hexadecimalSeparator; + $this->octalSeparator = \array_key_exists('octal', $configuration) ? $configuration['octal'] : $this->octalSeparator; + } + + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAnyTokenKindsFound([\T_DNUMBER, \T_LNUMBER]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_DNUMBER, \T_LNUMBER])) { + continue; + } + + $content = $tokens[$index]->getContent(); + $newContent = $this->getNewContent($content); + + if ($content !== $newContent) { + $id = $tokens[$index]->getId(); + \assert(\is_int($id)); + + $tokens[$index] = new Token([$id, $newContent]); + } + } + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [(new NLSFixer())->getName()]; + } + + private function getNewContent(string $content): string + { + if (\strpos($content, '.') !== false) { + $content = $this->updateContent($content, null, '.', 3, $this->floatSeparator); + $content = $this->updateContent($content, '.', 'e', 3, $this->floatSeparator, false); + + return $this->updateContent($content, 'e', null, 3, $this->floatSeparator); + } + + if (\stripos($content, '0b') === 0) { + return $this->updateContent($content, 'b', null, 8, $this->binarySeparator); + } + + if (\stripos($content, '0x') === 0) { + return $this->updateContent($content, 'x', null, 2, $this->hexadecimalSeparator); + } + + if (Preg::match('/e-?[\\d_]+$/i', $content)) { + $content = $this->updateContent($content, null, 'e', 3, $this->floatSeparator); + + return $this->updateContent($content, 'e', null, 3, $this->floatSeparator); + } + + if (\strpos($content, '0') === 0) { + return $this->updateContent($content, '0', null, 4, $this->octalSeparator); + } + + return $this->updateContent($content, null, null, 3, $this->decimalSeparator); + } + + private function updateContent(string $content, ?string $startCharacter, ?string $endCharacter, int $groupSize, ?bool $addSeparators, bool $fromRight = true): string + { + if ($addSeparators === null) { + return $content; + } + + $startPosition = $this->getStartPosition($content, $startCharacter); + if ($startPosition === null) { + return $content; + } + $endPosition = $this->getEndPosition($content, $endCharacter); + + $substringToUpdate = \substr($content, $startPosition, $endPosition - $startPosition); + $substringToUpdate = \str_replace('_', '', $substringToUpdate); + + if ($addSeparators) { + if ($fromRight) { + $substringToUpdate = \strrev($substringToUpdate); + } + + $substringToUpdate = Preg::replace(\sprintf('/[\\da-fA-F]{%d}(?!-)(?!$)/', $groupSize), '$0_', $substringToUpdate); + + if ($fromRight) { + $substringToUpdate = \strrev($substringToUpdate); + } + } + + return \substr($content, 0, $startPosition) . $substringToUpdate . \substr($content, $endPosition); + } + + private function getStartPosition(string $content, ?string $startCharacter): ?int + { + if ($startCharacter === null) { + return 0; + } + + $startPosition = \stripos($content, $startCharacter); + + if ($startPosition === false) { + return null; + } + + return $startPosition + 1; + } + + private function getEndPosition(string $content, ?string $endCharacter): int + { + if ($endCharacter === null) { + return \strlen($content); + } + + $endPosition = \stripos($content, $endCharacter); + + if ($endPosition === false) { + return \strlen($content); + } + + return $endPosition; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitAssertArgumentsOrderFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitAssertArgumentsOrderFixer.php new file mode 100644 index 00000000000..2142e72ce68 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitAssertArgumentsOrderFixer.php @@ -0,0 +1,174 @@ + true, + 'assertnotequals' => true, + 'assertequalscanonicalizing' => true, + 'assertnotequalscanonicalizing' => true, + 'assertequalsignoringcase' => true, + 'assertnotequalsignoringcase' => true, + 'assertequalswithdelta' => true, + 'assertnotequalswithdelta' => true, + 'assertsame' => true, + 'assertnotsame' => true, + 'assertgreaterthan' => 'assertLessThan', + 'assertgreaterthanorequal' => 'assertLessThanOrEqual', + 'assertlessthan' => 'assertGreaterThan', + 'assertlessthanorequal' => 'assertGreaterThanOrEqual', + ]; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'PHPUnit assertions must have expected argument before actual one.', + [new CodeSample('isAllTokenKindsFound([\T_CLASS, \T_EXTENDS, \T_FUNCTION]); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $phpUnitTestCaseIndicator = new PhpUnitTestCaseIndicator(); + + /** @var list $indices */ + foreach ($phpUnitTestCaseIndicator->findPhpUnitClasses($tokens) as $indices) { + $this->fixArgumentsOrder($tokens, $indices[0], $indices[1]); + } + } + + private function fixArgumentsOrder(Tokens $tokens, int $startIndex, int $endIndex): void + { + for ($index = $startIndex; $index < $endIndex; $index++) { + $newAssertion = self::getNewAssertion($tokens, $index); + if ($newAssertion === null) { + continue; + } + + $arguments = FunctionAnalyzer::getFunctionArguments($tokens, $index); + + if (!self::shouldArgumentsBeSwapped($arguments)) { + continue; + } + + if ($newAssertion !== $tokens[$index]->getContent()) { + $tokens[$index] = new Token([\T_STRING, $newAssertion]); + } + + self::swapArguments($tokens, $arguments); + } + } + + private static function getNewAssertion(Tokens $tokens, int $index): ?string + { + $oldAssertion = $tokens[$index]->getContent(); + + if (!\array_key_exists(\strtolower($oldAssertion), self::ASSERTIONS)) { + return null; + } + + $newAssertion = self::ASSERTIONS[\strtolower($oldAssertion)]; + + $openingBraceIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($openingBraceIndex)); + + if (!$tokens[$openingBraceIndex]->equals('(')) { + return null; + } + + if (!(new FunctionsAnalyzer())->isTheSameClassCall($tokens, $index)) { + return null; + } + + if (!\is_string($newAssertion)) { + return $oldAssertion; + } + + return $newAssertion; + } + + /** + * @param list $arguments + */ + private static function shouldArgumentsBeSwapped(array $arguments): bool + { + if (\count($arguments) < 2) { + return false; + } + + if ($arguments[0]->isConstant()) { + return false; + } + + return $arguments[1]->isConstant(); + } + + /** + * @param list $arguments + */ + private static function swapArguments(Tokens $tokens, array $arguments): void + { + $expectedArgumentTokens = []; // these will be 1st argument + for ($index = $arguments[1]->getStartIndex(); $index <= $arguments[1]->getEndIndex(); $index++) { + $expectedArgumentTokens[] = $tokens[$index]; + } + + $actualArgumentTokens = []; // these will be 2nd argument + for ($index = $arguments[0]->getStartIndex(); $index <= $arguments[0]->getEndIndex(); $index++) { + $actualArgumentTokens[] = $tokens[$index]; + } + + $tokens->overrideRange($arguments[1]->getStartIndex(), $arguments[1]->getEndIndex(), $actualArgumentTokens); + $tokens->overrideRange($arguments[0]->getStartIndex(), $arguments[0]->getEndIndex(), $expectedArgumentTokens); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitDedicatedAssertFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitDedicatedAssertFixer.php new file mode 100644 index 00000000000..12e01bf78b2 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitDedicatedAssertFixer.php @@ -0,0 +1,166 @@ + true, + 'assertnotequals' => true, + 'assertsame' => true, + 'assertnotsame' => true, + ]; + private const REPLACEMENTS_MAP = [ + 'count' => [ + 'positive' => 'assertCount', + 'negative' => 'assertNotCount', + ], + 'get_class' => [ + 'positive' => 'assertInstanceOf', + 'negative' => 'assertNotInstanceOf', + ], + 'sizeof' => [ + 'positive' => 'assertCount', + 'negative' => 'assertNotCount', + ], + ]; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'PHPUnit assertions like `assertCount` and `assertInstanceOf` must be used over `assertEquals`/`assertSame`.', + [new CodeSample('isAllTokenKindsFound([\T_CLASS, \T_EXTENDS, \T_FUNCTION]); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $phpUnitTestCaseIndicator = new PhpUnitTestCaseIndicator(); + + /** @var list $indices */ + foreach ($phpUnitTestCaseIndicator->findPhpUnitClasses($tokens) as $indices) { + $this->fixAssertions($tokens, $indices[0], $indices[1]); + } + } + + private function fixAssertions(Tokens $tokens, int $startIndex, int $endIndex): void + { + for ($index = $startIndex; $index < $endIndex; $index++) { + if (!self::isAssertionToFix($tokens, $index)) { + continue; + } + + $arguments = FunctionAnalyzer::getFunctionArguments($tokens, $index); + if (\count($arguments) < 2) { + continue; + } + + self::fixAssertion($tokens, $index, $arguments[1]); + } + } + + private static function isAssertionToFix(Tokens $tokens, int $index): bool + { + if (!\array_key_exists(\strtolower($tokens[$index]->getContent()), self::ASSERTIONS)) { + return false; + } + + $openingBraceIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($openingBraceIndex)); + + if (!$tokens[$openingBraceIndex]->equals('(')) { + return false; + } + + return (new FunctionsAnalyzer())->isTheSameClassCall($tokens, $index); + } + + private static function fixAssertion(Tokens $tokens, int $assertionIndex, ArgumentAnalysis $secondArgument): void + { + $functionCallIndex = $secondArgument->getStartIndex(); + if ($tokens[$functionCallIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $functionCallIndex = $tokens->getNextMeaningfulToken($functionCallIndex); + \assert(\is_int($functionCallIndex)); + } + + if (!(new FunctionsAnalyzer())->isGlobalFunctionCall($tokens, $functionCallIndex)) { + return; + } + + $arguments = FunctionAnalyzer::getFunctionArguments($tokens, $functionCallIndex); + if (\count($arguments) !== 1) { + return; + } + + $functionName = \strtolower($tokens[$functionCallIndex]->getContent()); + + if (!\array_key_exists($functionName, self::REPLACEMENTS_MAP)) { + return; + } + + $newAssertion = self::REPLACEMENTS_MAP[$functionName][\stripos($tokens[$assertionIndex]->getContent(), 'not', 6) === false ? 'positive' : 'negative']; + + $openParenthesisIndex = $tokens->getNextMeaningfulToken($functionCallIndex); + \assert(\is_int($openParenthesisIndex)); + $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex); + + if ($closeParenthesisIndex < $secondArgument->getEndIndex()) { + return; + } + + $tokens[$assertionIndex] = new Token([\T_STRING, $newAssertion]); + $tokens->clearRange($secondArgument->getStartIndex(), $openParenthesisIndex - 1); + $tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesisIndex); + $tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesisIndex); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitNoUselessReturnFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitNoUselessReturnFixer.php new file mode 100644 index 00000000000..50af76ab967 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitNoUselessReturnFixer.php @@ -0,0 +1,127 @@ + $token[1], + self::FUNCTION_TOKENS, + )), + ), + [new CodeSample('markTestSkipped(); + return; + } +} +')], + 'They will throw an exception anyway.', + 'when original PHPUnit methods are overwritten', + ); + } + + /** + * Must run before NoExtraBlankLinesFixer. + */ + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAllTokenKindsFound([\T_CLASS, \T_EXTENDS, \T_FUNCTION, \T_STRING, \T_RETURN]); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $phpUnitTestCaseIndicator = new PhpUnitTestCaseIndicator(); + + /** @var list $indices */ + foreach ($phpUnitTestCaseIndicator->findPhpUnitClasses($tokens) as $indices) { + $this->removeUselessReturns($tokens, $indices[0], $indices[1]); + } + } + + private function removeUselessReturns(Tokens $tokens, int $startIndex, int $endIndex): void + { + $functionsAnalyzer = new FunctionsAnalyzer(); + + for ($index = $startIndex; $index < $endIndex; $index++) { + if (!$tokens[$index]->equalsAny(self::FUNCTION_TOKENS, false)) { + continue; + } + + $openingBraceIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($openingBraceIndex)); + + if (!$tokens[$openingBraceIndex]->equals('(')) { + continue; + } + + if (!$functionsAnalyzer->isTheSameClassCall($tokens, $index)) { + continue; + } + + $closingBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingBraceIndex); + + $semicolonIndex = $tokens->getNextMeaningfulToken($closingBraceIndex); + \assert(\is_int($semicolonIndex)); + + $returnIndex = $tokens->getNextMeaningfulToken($semicolonIndex); + \assert(\is_int($returnIndex)); + + if (!$tokens[$returnIndex]->isGivenKind(\T_RETURN)) { + continue; + } + + $semicolonAfterReturnIndex = $tokens->getNextTokenOfKind($returnIndex, [';', '(']); + \assert(\is_int($semicolonAfterReturnIndex)); + + while ($tokens[$semicolonAfterReturnIndex]->equals('(')) { + $closingBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $semicolonAfterReturnIndex); + + $semicolonAfterReturnIndex = $tokens->getNextTokenOfKind($closingBraceIndex, [';', '(']); + \assert(\is_int($semicolonAfterReturnIndex)); + } + + $tokens->clearRange($returnIndex, $semicolonAfterReturnIndex - 1); + TokenRemover::removeWithLinesIfPossible($tokens, $semicolonAfterReturnIndex); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitRequiresConstraintFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitRequiresConstraintFixer.php new file mode 100644 index 00000000000..602b6c60de1 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpUnitRequiresConstraintFixer.php @@ -0,0 +1,174 @@ +isAllTokenKindsFound([\T_CLASS, \T_EXTENDS, \T_FUNCTION]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $phpUnitTestCaseIndicator = new PhpUnitTestCaseIndicator(); + + /** @var list $indices */ + foreach ($phpUnitTestCaseIndicator->findPhpUnitClasses($tokens) as $indices) { + $this->fixClass($tokens, $indices[0], $indices[1]); + } + } + + private function fixClass(Tokens $tokens, int $index, int $endIndex): void + { + while ($index < $endIndex) { + $index = $tokens->getNextTokenOfKind($index, ['{', [\T_FUNCTION]]); + if ($index === null || $index >= $endIndex) { + return; + } + + if ($tokens[$index]->equals('{')) { + $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index); + continue; + } + + self::fixMethod($tokens, $index); + } + } + + private static function fixMethod(Tokens $tokens, int $index): void + { + $index = $tokens->getPrevTokenOfKind($index, [';', [\T_DOC_COMMENT], [CT::T_ATTRIBUTE_CLOSE]]); + if ($index === null || $tokens[$index]->equals(';')) { + return; + } + + if ($tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + self::fixPhpDoc($tokens, $index); + } + + if ($tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) { + self::fixAttribute($tokens, $index); + } + } + + private static function fixPhpDoc(Tokens $tokens, int $index): void + { + $tokens[$index] = new Token([ + \T_DOC_COMMENT, + Preg::replaceCallback( + '/(@requires\\s+\\S+\\s+)(.+?)(\\s*)$/m', + static function (array $matches): string { + \assert(\is_string($matches[1])); + \assert(\is_string($matches[2])); + \assert(\is_string($matches[3])); + + return $matches[1] . self::fixString($matches[2]) . $matches[3]; + }, + $tokens[$index]->getContent(), + ), + ]); + } + + private static function fixAttribute(Tokens $tokens, int $index): void + { + $fullyQualifiedNameAnalyzer = new FullyQualifiedNameAnalyzer($tokens); + foreach (AttributeAnalyzer::collect($tokens, $tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $index)) as $attributeAnalysis) { + foreach ($attributeAnalysis->getAttributes() as $attribute) { + $attributeName = \strtolower($fullyQualifiedNameAnalyzer->getFullyQualifiedName($attribute['name'], $attribute['start'], NamespaceUseAnalysis::TYPE_CLASS)); + if ( + $attributeName === 'phpunit\\framework\\attributes\\requiresphp' + || $attributeName === 'phpunit\\framework\\attributes\\requiresphpunit' + ) { + $stringIndex = $tokens->getPrevMeaningfulToken($attribute['end']); + \assert(\is_int($stringIndex)); + if (!$tokens[$stringIndex]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) { + continue; + } + + $openParenthesisIndex = $tokens->getPrevMeaningfulToken($stringIndex); + \assert(\is_int($openParenthesisIndex)); + if (!$tokens[$openParenthesisIndex]->equals('(')) { + continue; + } + + $quote = \substr($tokens[$stringIndex]->getContent(), -1, 1); + $tokens[$stringIndex] = new Token([ + \T_CONSTANT_ENCAPSED_STRING, + $quote . self::fixString(\substr($tokens[$stringIndex]->getContent(), 1, -1)) . $quote, + ]); + } + } + } + } + + private static function fixString(string $string): string + { + if (Preg::match('/^[\\d\\.-]+(dev|(RC|alpha|beta)[\\d\\.])?$/', $string)) { + $string = '>=' . $string; + } + + return Preg::replace('/^([<>=!]{0,2})\\s*([\\d\\.-]+(dev|(RC|alpha|beta)[\\d\\.])?)$/', '$1 $2', $string); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocArrayStyleFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocArrayStyleFixer.php new file mode 100644 index 00000000000..c452d8f5e8d --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocArrayStyleFixer.php @@ -0,0 +1,83 @@ +phpdocArrayTypeFixer = new PhpdocArrayTypeFixer(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Generic array style should be used in PHPDoc.', + [ + new CodeSample( + 'phpdocArrayTypeFixer->getPriority(); + } + + public function isCandidate(Tokens $tokens): bool + { + return $this->phpdocArrayTypeFixer->isCandidate($tokens); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $this->phpdocArrayTypeFixer->fix($file, $tokens); + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [$this->phpdocArrayTypeFixer->getName()]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocNoIncorrectVarAnnotationFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocNoIncorrectVarAnnotationFixer.php new file mode 100644 index 00000000000..2475985cb32 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocNoIncorrectVarAnnotationFixer.php @@ -0,0 +1,173 @@ +isTokenKindFound(\T_DOC_COMMENT); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + continue; + } + + // remove ones not having type at the beginning + $this->removeVarAnnotationNotMatchingPattern($tokens, $index, '/@var\\s+[\\?\\\\a-zA-Z_\\x7f-\\xff]/'); + + $nextIndex = $tokens->getNextMeaningfulToken($index); + + if ($nextIndex === null) { + $this->removeVarAnnotationNotMatchingPattern($tokens, $index, null); + continue; + } + + if ($tokens[$nextIndex]->isGivenKind([\T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_VAR, \T_STATIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE])) { + $this->removeForClassElement($tokens, $index, $nextIndex); + continue; + } + + if ($tokens[$nextIndex]->isGivenKind(\T_VARIABLE)) { + $this->removeVarAnnotation($tokens, $index, [$tokens[$nextIndex]->getContent()]); + continue; + } + + if ($tokens[$nextIndex]->isGivenKind([\T_FOR, \T_FOREACH, \T_IF, \T_SWITCH, \T_WHILE])) { + $this->removeVarAnnotationForControl($tokens, $index, $nextIndex); + continue; + } + + $this->removeVarAnnotationNotMatchingPattern($tokens, $index, null); + } + } + + private function removeForClassElement(Tokens $tokens, int $index, int $propertyStartIndex): void + { + $tokenKinds = [\T_NS_SEPARATOR, \T_STATIC, \T_STRING, \T_WHITESPACE, CT::T_ARRAY_TYPEHINT, CT::T_NULLABLE_TYPE, CT::T_TYPE_ALTERNATION]; + + if (\defined('T_READONLY')) { + $tokenKinds[] = CT::T_TYPE_INTERSECTION; + $tokenKinds[] = \T_READONLY; + } + + $variableIndex = $tokens->getTokenNotOfKindsSibling($propertyStartIndex, 1, $tokenKinds); + \assert(\is_int($variableIndex)); + + if (!$tokens[$variableIndex]->isGivenKind(\T_VARIABLE)) { + $this->removeVarAnnotationNotMatchingPattern($tokens, $index, null); + + return; + } + + if (Preg::match('/@var\\h+(.+\\h+)?\\$[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*/', $tokens[$index]->getContent())) { + $this->removeVarAnnotation($tokens, $index, [$tokens[$variableIndex]->getContent()]); + } + } + + /** + * @param list $allowedVariables + */ + private function removeVarAnnotation(Tokens $tokens, int $index, array $allowedVariables): void + { + $this->removeVarAnnotationNotMatchingPattern( + $tokens, + $index, + '/(\\Q' . \implode('\\E|\\Q', $allowedVariables) . '\\E)\\b/i', + ); + } + + private function removeVarAnnotationForControl(Tokens $tokens, int $commentIndex, int $controlIndex): void + { + $index = $tokens->getNextMeaningfulToken($controlIndex); + \assert(\is_int($index)); + + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index); + + $variables = []; + + while ($index < $endIndex) { + $index++; + + if ($tokens[$index]->isGivenKind(\T_VARIABLE)) { + $variables[] = $tokens[$index]->getContent(); + } + } + + $this->removeVarAnnotation($tokens, $commentIndex, $variables); + } + + private function removeVarAnnotationNotMatchingPattern(Tokens $tokens, int $index, ?string $pattern): void + { + $doc = new DocBlock($tokens[$index]->getContent()); + + foreach ($doc->getAnnotationsOfType(['var']) as $annotation) { + if ($pattern === null || !Preg::match($pattern, $annotation->getContent())) { + $annotation->remove(); + } + } + + $content = $doc->getContent(); + + if ($content === $tokens[$index]->getContent()) { + return; + } + + if ($content === '') { + TokenRemover::removeWithLinesIfPossible($tokens, $index); + + return; + } + $tokens[$index] = new Token([\T_DOC_COMMENT, $content]); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocNoSuperfluousParamFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocNoSuperfluousParamFixer.php new file mode 100644 index 00000000000..b826778354c --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocNoSuperfluousParamFixer.php @@ -0,0 +1,148 @@ +isAllTokenKindsFound([\T_DOC_COMMENT, \T_FUNCTION]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = 0; $index < $tokens->count(); $index++) { + if (!$tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + continue; + } + + $functionIndex = $tokens->getTokenNotOfKindSibling($index, 1, [[\T_ABSTRACT], [\T_COMMENT], [\T_FINAL], [\T_PRIVATE], [\T_PROTECTED], [\T_PUBLIC], [\T_STATIC], [\T_WHITESPACE]]); + + if ($functionIndex === null) { + return; + } + + if (!$tokens[$functionIndex]->isGivenKind(\T_FUNCTION)) { + continue; + } + + $paramNames = $this->getParamNames($tokens, $functionIndex); + + $newContent = $this->getFilteredDocComment($tokens[$index]->getContent(), $paramNames); + + if ($newContent === $tokens[$index]->getContent()) { + continue; + } + + if ($newContent === '') { + $tokens->clearTokenAndMergeSurroundingWhitespace($index); + } else { + $tokens[$index] = new Token([\T_DOC_COMMENT, $newContent]); + } + } + } + + /** + * @return list + */ + private function getParamNames(Tokens $tokens, int $functionIndex): array + { + $paramBlockStartIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']); + \assert(\is_int($paramBlockStartIndex)); + + $paramBlockEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $paramBlockStartIndex); + + $paramNames = []; + for ($index = $paramBlockStartIndex; $index < $paramBlockEndIndex; $index++) { + if ($tokens[$index]->isGivenKind(\T_VARIABLE)) { + $paramNames[] = $tokens[$index]->getContent(); + } + } + + return $paramNames; + } + + /** + * @param list $paramNames + */ + private function getFilteredDocComment(string $comment, array $paramNames): string + { + $doc = new DocBlock($comment); + + $foundParamNames = []; + foreach ($doc->getAnnotationsOfType('param') as $annotation) { + $paramName = $this->getParamName($annotation->getContent()); + + if (\in_array($paramName, $paramNames, true) && !\in_array($paramName, $foundParamNames, true)) { + $foundParamNames[] = $paramName; + continue; + } + + $annotation->remove(); + } + + return $doc->getContent(); + } + + private function getParamName(string $annotation): ?string + { + Preg::match('/@param\\s+(?:[^\\$]+)?\\s*(\\$[a-zA-Z_\\x80-\\xff][a-zA-Z0-9_\\x80-\\xff]*)\\b/', $annotation, $matches); + + if (!\array_key_exists(1, $matches)) { + return null; + } + + return $matches[1]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocOnlyAllowedAnnotationsFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocOnlyAllowedAnnotationsFixer.php new file mode 100644 index 00000000000..5e2a27044d5 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocOnlyAllowedAnnotationsFixer.php @@ -0,0 +1,126 @@ + + * + * @phpstan-type _InputConfig array{elements?: list} + * @phpstan-type _Config array{elements: list} + * + * @no-named-arguments + */ +final class PhpdocOnlyAllowedAnnotationsFixer extends AbstractFixer implements ConfigurableFixerInterface +{ + /** @var list */ + private array $elements = []; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Only the listed annotations are allowed in PHPDoc.', + [new CodeSample( + ' ['author', 'version']], + )], + '', + ); + } + + public function getConfigurationDefinition(): FixerConfigurationResolverInterface + { + return new FixerConfigurationResolver([ + (new FixerOptionBuilder('elements', 'list of annotations to keep in PHPDoc')) + ->setAllowedTypes(['array']) + ->setDefault($this->elements) + ->getOption(), + ]); + } + + /** + * @param array> $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('elements', $configuration)) { + $this->elements = $configuration['elements']; + } + } + + /** + * Must run before NoEmptyPhpdocFixer, PhpdocTagNoNamedArgumentsFixer. + * Must run after CommentToPhpdocFixer. + */ + public function getPriority(): int + { + return 4; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isTokenKindFound(\T_DOC_COMMENT); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + continue; + } + + $docBlock = new DocBlock($tokens[$index]->getContent()); + + foreach ($docBlock->getAnnotations() as $annotation) { + if ( + Preg::match('/@([a-zA-Z0-9_\\-\\\\]+)/', $annotation->getContent(), $matches) + && \in_array($matches[1], $this->elements, true) + ) { + continue; + } + + $annotation->remove(); + } + + if ($docBlock->getContent() === '') { + $tokens->clearTokenAndMergeSurroundingWhitespace($index); + continue; + } + + $tokens[$index] = new Token([\T_DOC_COMMENT, $docBlock->getContent()]); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocParamOrderFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocParamOrderFixer.php new file mode 100644 index 00000000000..492172a24e2 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocParamOrderFixer.php @@ -0,0 +1,81 @@ +phpdocParamOrderFixer = new \PhpCsFixer\Fixer\Phpdoc\PhpdocParamOrderFixer(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + $this->phpdocParamOrderFixer->getDefinition()->getSummary(), + [new CodeSample('phpdocParamOrderFixer->getPriority(); + } + + public function isCandidate(Tokens $tokens): bool + { + return $this->phpdocParamOrderFixer->isCandidate($tokens); + } + + public function isRisky(): bool + { + return $this->phpdocParamOrderFixer->isRisky(); + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $this->phpdocParamOrderFixer->fix($file, $tokens); + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [$this->phpdocParamOrderFixer->getName()]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocParamTypeFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocParamTypeFixer.php new file mode 100644 index 00000000000..e0f05615ea6 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocParamTypeFixer.php @@ -0,0 +1,80 @@ +isAnyTokenKindsFound([\T_COMMENT, \T_DOC_COMMENT]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind([\T_DOC_COMMENT])) { + continue; + } + + $newContent = Preg::replace( + '/(@param) {0,7}( *\\$)/i', + '$1 mixed $2', + $tokens[$index]->getContent(), + ); + + if ($newContent === $tokens[$index]->getContent()) { + continue; + } + + $tokens[$index] = new Token([\T_DOC_COMMENT, $newContent]); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocSelfAccessorFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocSelfAccessorFixer.php new file mode 100644 index 00000000000..e45e21fa98e --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocSelfAccessorFixer.php @@ -0,0 +1,143 @@ +isAnyTokenKindsFound([\T_CLASS, \T_INTERFACE]) && $tokens->isTokenKindFound(\T_DOC_COMMENT); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $namespaces = (new NamespacesAnalyzer())->getDeclarations($tokens); + + foreach ($namespaces as $namespace) { + $this->fixPhpdocSelfAccessor($tokens, $namespace->getScopeStartIndex(), $namespace->getScopeEndIndex(), $namespace->getFullName()); + } + } + + private function fixPhpdocSelfAccessor(Tokens $tokens, int $namespaceStartIndex, int $namespaceEndIndex, string $fullName): void + { + $tokensAnalyzer = new TokensAnalyzer($tokens); + + $index = $namespaceStartIndex; + while ($index < $namespaceEndIndex) { + $index++; + + if (!$tokens[$index]->isGivenKind([\T_CLASS, \T_INTERFACE]) || $tokensAnalyzer->isAnonymousClass($index)) { + continue; + } + + $nameIndex = $tokens->getNextTokenOfKind($index, [[\T_STRING]]); + \assert(\is_int($nameIndex)); + + $startIndex = $tokens->getNextTokenOfKind($nameIndex, ['{']); + \assert(\is_int($startIndex)); + + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startIndex); + + $classyName = $tokens[$nameIndex]->getContent(); + + $this->replaceNameOccurrences($tokens, $fullName, $classyName, $startIndex, $endIndex); + + $index = $endIndex; + } + } + + private function replaceNameOccurrences(Tokens $tokens, string $namespace, string $classyName, int $startIndex, int $endIndex): void + { + for ($index = $startIndex; $index < $endIndex; $index++) { + if (!$tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + continue; + } + + $newContent = $this->getNewContent($tokens[$index]->getContent(), $namespace, $classyName); + + if ($newContent === $tokens[$index]->getContent()) { + continue; + } + + $tokens[$index] = new Token([\T_DOC_COMMENT, $newContent]); + } + } + + private function getNewContent(string $content, string $namespace, string $classyName): string + { + $docBlock = new DocBlock($content); + + $fqcn = ($namespace !== '' ? '\\' . $namespace : '') . '\\' . $classyName; + + foreach ($docBlock->getAnnotations() as $annotation) { + if (!$annotation->supportTypes()) { + continue; + } + + $types = []; + foreach ($annotation->getTypes() as $type) { + $type = Preg::replace( + \sprintf('/(?setTypes($types); + } + } + + return $docBlock->getContent(); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocSingleLineVarFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocSingleLineVarFixer.php new file mode 100644 index 00000000000..795fbe2267a --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocSingleLineVarFixer.php @@ -0,0 +1,80 @@ +isTokenKindFound(\T_DOC_COMMENT); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_DOC_COMMENT)) { + continue; + } + + $newContent = Preg::replace( + '#^/\\*\\*[\\s\\*]+(@var[^\\r\\n]+)(?getContent(), + ); + + if ($newContent === $tokens[$index]->getContent()) { + continue; + } + + $tokens[$index] = new Token([\T_DOC_COMMENT, $newContent]); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTagNoNamedArgumentsFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTagNoNamedArgumentsFixer.php new file mode 100644 index 00000000000..c83f08271e1 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTagNoNamedArgumentsFixer.php @@ -0,0 +1,172 @@ + + * + * @phpstan-type _InputConfig array{directory?: string, description?: string} + * @phpstan-type _Config array{directory: string, description: string} + * + * @no-named-arguments + */ +final class PhpdocTagNoNamedArgumentsFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface +{ + private string $description = ''; + private string $directory = ''; + private WhitespacesFixerConfig $whitespacesConfig; + + public function setWhitespacesConfig(WhitespacesFixerConfig $config): void + { + $this->whitespacesConfig = $config; + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'There must be `@no-named-arguments` tag in PHPDoc of a class/enum/interface/trait.', + [new CodeSample(<<<'PHP' + setAllowedTypes(['string']) + ->setDefault('') + ->getOption(), + (new FixerOptionBuilder('directory', 'directory in which apply the changes, empty value will result with current working directory (result of `getcwd` call)')) + ->setAllowedTypes(['string']) + ->setDefault('') + ->getOption(), + ]); + } + + /** + * @param _InputConfig $configuration + */ + public function configure(array $configuration): void + { + /** @var array{directory: string, description: string} $configuration */ + $configuration = $this->getConfigurationDefinition()->resolve($configuration); + + $this->directory = $configuration['directory']; + + if ($this->directory === '') { + $cwd = \getcwd(); + \assert(\is_string($cwd)); + $this->directory = $cwd; + } + + if (!\is_dir($this->directory)) { + throw new InvalidFixerConfigurationException($this->getName(), \sprintf('The directory "%s" does not exists.', $this->directory)); + } + + $this->directory = \realpath($this->directory) . \DIRECTORY_SEPARATOR; + + $this->description = $configuration['description']; + } + + /** + * Must run before PhpdocSeparationFixer. + * Must run after PhpdocOnlyAllowedAnnotationsFixer. + */ + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds()); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + if (!\str_starts_with($file->getRealPath(), $this->directory)) { + return; + } + + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isClassy()) { + continue; + } + + $prevIndex = $tokens->getPrevMeaningfulToken($index); + if ($tokens[$prevIndex]->isGivenKind(\T_NEW)) { + continue; + } + + $this->ensureIsDocBlockWithNoNameArgumentsTag($tokens, $index); + + $docBlockIndex = $tokens->getPrevTokenOfKind($index + 2, [[\T_DOC_COMMENT]]); + \assert(\is_int($docBlockIndex)); + + $content = $tokens[$docBlockIndex]->getContent(); + + $newContent = Preg::replace('/@no-named-arguments.*\\R/', \rtrim('@no-named-arguments ' . $this->description) . $this->whitespacesConfig->getLineEnding(), $content); + + if ($newContent !== $content) { + $tokens[$docBlockIndex] = new Token([\T_DOC_COMMENT, $newContent]); + } + } + } + + private function ensureIsDocBlockWithNoNameArgumentsTag(Tokens $tokens, int $index): void + { + /** @var callable(WhitespacesFixerConfig, Tokens, int): void $ensureIsDocBlockWithTagNoNameArguments */ + static $ensureIsDocBlockWithTagNoNameArguments; + + if ($ensureIsDocBlockWithTagNoNameArguments === null) { + $ensureIsDocBlockWithTagNoNameArguments = \Closure::bind( + static function (WhitespacesFixerConfig $whitespacesConfig, Tokens $tokens, int $index): void { + $phpUnitInternalClassFixer = new PhpUnitInternalClassFixer(); + $phpUnitInternalClassFixer->setWhitespacesConfig($whitespacesConfig); + $phpUnitInternalClassFixer->ensureIsDocBlockWithAnnotation($tokens, $index, 'no-named-arguments', ['internal', 'no-named-arguments'], []); + }, + null, + PhpUnitInternalClassFixer::class, + ); + } + + $ensureIsDocBlockWithTagNoNameArguments($this->whitespacesConfig, $tokens, $index); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypeListFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypeListFixer.php new file mode 100644 index 00000000000..b05b3beb904 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypeListFixer.php @@ -0,0 +1,80 @@ +phpdocListTypeFixer = new PhpdocListTypeFixer(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'PHPDoc type `list` must be used instead of `array` without a key type.', + [new CodeSample(' + */ +function foo($x) {} +')], + '', + ); + } + + /** + * Must run before PhpdocAlignFixer, PhpdocTypesOrderFixer. + * Must run after CommentToPhpdocFixer, PhpdocArrayStyleFixer. + */ + public function getPriority(): int + { + return 1; + } + + public function isCandidate(Tokens $tokens): bool + { + return $this->phpdocListTypeFixer->isCandidate($tokens); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $this->phpdocListTypeFixer->fix($file, $tokens); + } + + /** + * @return list + */ + public function getSuccessorsNames(): array + { + return [$this->phpdocListTypeFixer->getName()]; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypesCommaSpacesFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypesCommaSpacesFixer.php new file mode 100644 index 00000000000..ab7a967826c --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypesCommaSpacesFixer.php @@ -0,0 +1,42 @@ + */\n")], + '', + ); + } + + public function getPriority(): int + { + return 0; + } + + protected function fixType(string $type): string + { + return Preg::replace('/\\h*,\\s*/', ', ', $type); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypesTrimFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypesTrimFixer.php new file mode 100644 index 00000000000..983142d45af --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PhpdocTypesTrimFixer.php @@ -0,0 +1,50 @@ +isAnyTokenKindsFound([\T_DOC_COMMENT, \T_VARIABLE]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($docCommentIndex = $tokens->count() - 1; $docCommentIndex > 0; $docCommentIndex--) { + if (!$tokens[$docCommentIndex]->isGivenKind([\T_DOC_COMMENT])) { + continue; + } + + $variableIndex = $this->getVariableIndex($tokens, $docCommentIndex); + if ($variableIndex === null) { + continue; + } + + $assertTokens = $this->getAssertTokens($tokens, $docCommentIndex, $tokens[$variableIndex]->getContent()); + if ($assertTokens === null) { + continue; + } + + $expressionEndIndex = $this->getExpressionEnd($tokens, $variableIndex); + + if (!$this->canBePlacedAfterExpression($tokens, $expressionEndIndex)) { + continue; + } + + if ($tokens[$variableIndex - 1]->isWhitespace()) { + \array_unshift($assertTokens, new Token([\T_WHITESPACE, $tokens[$variableIndex - 1]->getContent()])); + } + + $tokens->insertAt($expressionEndIndex + 1, $assertTokens); + + TokenRemover::removeWithLinesIfPossible($tokens, $docCommentIndex); + } + } + + private function getVariableIndex(Tokens $tokens, int $docCommentIndex): ?int + { + $prevIndex = $tokens->getPrevMeaningfulToken($docCommentIndex); + if (!$tokens[$prevIndex]->equalsAny([';', '{', '}', [\T_OPEN_TAG]])) { + return null; + } + + $variableIndex = $tokens->getNextMeaningfulToken($docCommentIndex); + if ($variableIndex === null) { + return null; + } + if (!$tokens[$variableIndex]->isGivenKind([\T_VARIABLE])) { + return null; + } + + $assignmentIndex = $tokens->getNextMeaningfulToken($variableIndex); + \assert(\is_int($assignmentIndex)); + + if (!$tokens[$assignmentIndex]->equals('=')) { + return null; + } + + return $variableIndex; + } + + /** + * @return null|list + */ + private function getAssertTokens(Tokens $tokens, int $docCommentIndex, string $variableName): ?array + { + $annotation = $this->getAnnotationForVariable($tokens, $docCommentIndex, $variableName); + if ($annotation === null) { + return null; + } + + $typeExpression = $annotation->getTypeExpression(); + if ($typeExpression === null) { + return null; + } + + $assertCode = 'getTypes() as $type) { + if (\substr($type, 0, 1) === '?') { + $assertions['null'] = $this->getCodeForType('null', $variableName); + $type = \substr($type, 1); + } + $assertions[$type] = $this->getCodeForType($type, $variableName); + } + + try { + $tokens = Tokens::fromCode($assertCode . \implode(' || ', $assertions) . ');'); + } catch (\ParseError $exception) { + return null; + } + + /** @var list $arrayTokens */ + $arrayTokens = $tokens->toArray(); + + return \array_slice($arrayTokens, 1); + } + + private function getAnnotationForVariable(Tokens $tokens, int $docCommentIndex, string $variableName): ?Annotation + { + $docBlock = new DocBlock($tokens[$docCommentIndex]->getContent()); + + if (\count($docBlock->getAnnotations()) !== 1) { + return null; + } + + $varAnnotations = $docBlock->getAnnotationsOfType('var'); + if (\count($varAnnotations) !== 1) { + return null; + } + + $varAnnotation = \reset($varAnnotations); + + if ($varAnnotation->getVariableName() !== $variableName) { + return null; + } + + return $varAnnotation; + } + + private function getCodeForType(string $type, string $variableName): string + { + $typesMap = [ + 'array' => 'is_array', + 'bool' => 'is_bool', + 'boolean' => 'is_bool', + 'callable' => 'is_callable', + 'double' => 'is_float', + 'float' => 'is_float', + 'int' => 'is_int', + 'integer' => 'is_int', + 'iterable' => 'is_iterable', + 'null' => 'is_null', + 'object' => 'is_object', + 'resource' => 'is_resource', + 'string' => 'is_string', + ]; + + if (\array_key_exists(\strtolower($type), $typesMap)) { + return \sprintf('%s(%s)', $typesMap[\strtolower($type)], $variableName); + } + + return \sprintf('%s instanceof %s', $variableName, $type); + } + + private function getExpressionEnd(Tokens $tokens, int $index): int + { + while (!$tokens[$index]->equals(';')) { + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + + $blockType = Tokens::detectBlockType($tokens[$index]); + if ($blockType !== null && $blockType['isStart']) { + $index = $tokens->findBlockEnd($blockType['type'], $index); + } + } + + return $index; + } + + private function canBePlacedAfterExpression(Tokens $tokens, int $expressionEndIndex): bool + { + $afterExpressionIndex = $tokens->getNextMeaningfulToken($expressionEndIndex); + + if ($afterExpressionIndex === null) { + return true; + } + + if ($tokens[$afterExpressionIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $afterExpressionIndex = $tokens->getNextMeaningfulToken($afterExpressionIndex); + } + + return !$tokens[$afterExpressionIndex]->equals([\T_STRING, 'assert'], false); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PromotedConstructorPropertyFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PromotedConstructorPropertyFixer.php new file mode 100644 index 00000000000..140f8f1b0d2 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/PromotedConstructorPropertyFixer.php @@ -0,0 +1,435 @@ + + * + * @phpstan-type _InputConfig array{promote_only_existing_properties?: bool} + * @phpstan-type _Config array{promote_only_existing_properties: bool} + * + * @no-named-arguments + */ +final class PromotedConstructorPropertyFixer extends AbstractFixer implements ConfigurableFixerInterface +{ + /** @var array> */ + private array $tokensToInsert; + + private bool $promoteOnlyExistingProperties = false; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Constructor properties must be promoted if possible.', + [ + new VersionSpecificCodeSample( + 'bar = $bar; + } +} +', + new VersionSpecification(80000), + ), + ], + '', + ); + } + + public function getConfigurationDefinition(): FixerConfigurationResolverInterface + { + return new FixerConfigurationResolver([ + (new FixerOptionBuilder('promote_only_existing_properties', 'whether to promote only properties that are defined in class')) + ->setAllowedTypes(['bool']) + ->setDefault($this->promoteOnlyExistingProperties) + ->getOption(), + ]); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('promote_only_existing_properties', $configuration)) { + $this->promoteOnlyExistingProperties = $configuration['promote_only_existing_properties']; + } + } + + /** + * Must run before ClassAttributesSeparationFixer, ConstructorEmptyBracesFixer, MultilinePromotedPropertiesFixer, NoExtraBlankLinesFixer, ReadonlyPromotedPropertiesFixer. + */ + public function getPriority(): int + { + return 56; + } + + public function isCandidate(Tokens $tokens): bool + { + // @phpstan-ignore greaterOrEqual.alwaysTrue + return \PHP_VERSION_ID >= 80000 && $tokens->isAllTokenKindsFound([\T_CLASS, \T_VARIABLE]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $constructorAnalyzer = new ConstructorAnalyzer(); + $this->tokensToInsert = []; + + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CLASS)) { + continue; + } + + $constructorAnalysis = $constructorAnalyzer->findNonAbstractConstructor($tokens, $index); + if ($constructorAnalysis === null) { + continue; + } + + $this->promoteProperties($tokens, $index, $constructorAnalysis); + } + + \krsort($this->tokensToInsert); + + /** + * @var int $index + * @var list $tokensToInsert + */ + foreach ($this->tokensToInsert as $index => $tokensToInsert) { + $tokens->insertAt($index, $tokensToInsert); + } + } + + private function promoteProperties(Tokens $tokens, int $classIndex, ConstructorAnalysis $constructorAnalysis): void + { + $isDoctrineEntity = $this->isDoctrineEntity($tokens, $classIndex); + $properties = $this->getClassProperties($tokens, $classIndex); + + $constructorParameterNames = $constructorAnalysis->getConstructorParameterNames(); + $constructorPromotableParameters = $constructorAnalysis->getConstructorPromotableParameters(); + $constructorPromotableAssignments = $constructorAnalysis->getConstructorPromotableAssignments(); + + foreach ($constructorPromotableParameters as $constructorParameterIndex => $constructorParameterName) { + if (!\array_key_exists($constructorParameterName, $constructorPromotableAssignments)) { + continue; + } + + $propertyIndex = $this->getPropertyIndex($tokens, $properties, $constructorPromotableAssignments[$constructorParameterName]); + + if (!$this->isPropertyToPromote($tokens, $propertyIndex, $isDoctrineEntity)) { + continue; + } + + $propertyType = $this->getType($tokens, $propertyIndex); + $parameterType = $this->getType($tokens, $constructorParameterIndex); + + if (!$this->typesAllowPromoting($propertyType, $parameterType)) { + continue; + } + + $assignedPropertyIndex = $tokens->getPrevTokenOfKind($constructorPromotableAssignments[$constructorParameterName], [[\T_STRING]]); + $oldParameterName = $tokens[$constructorParameterIndex]->getContent(); + $newParameterName = '$' . $tokens[$assignedPropertyIndex]->getContent(); + if ($oldParameterName !== $newParameterName && \in_array($newParameterName, $constructorParameterNames, true)) { + continue; + } + + $tokensToInsert = $this->removePropertyAndReturnTokensToInsert($tokens, $propertyIndex); + + $this->renameVariable($tokens, $constructorAnalysis->getConstructorIndex(), $oldParameterName, $newParameterName); + + $this->removeAssignment($tokens, $constructorPromotableAssignments[$constructorParameterName]); + $this->updateParameterSignature( + $tokens, + $constructorParameterIndex, + $tokensToInsert, + \substr($propertyType, 0, 1) === '?', + ); + } + } + + private function isDoctrineEntity(Tokens $tokens, int $index): bool + { + $phpDocIndex = $tokens->getPrevNonWhitespace($index); + \assert(\is_int($phpDocIndex)); + + if (!$tokens[$phpDocIndex]->isGivenKind(\T_DOC_COMMENT)) { + return false; + } + + $docBlock = new DocBlock($tokens[$phpDocIndex]->getContent()); + + foreach ($docBlock->getAnnotations() as $annotation) { + if (Preg::match('/\\*\\h+(@Document|@Entity|@Mapping\\\\Entity|@ODM\\\\Document|@ORM\\\\Entity|@ORM\\\\Mapping\\\\Entity)/', $annotation->getContent())) { + return true; + } + } + + return false; + } + + /** + * @param array $properties + */ + private function getPropertyIndex(Tokens $tokens, array $properties, int $assignmentIndex): ?int + { + $propertyNameIndex = $tokens->getPrevTokenOfKind($assignmentIndex, [[\T_STRING]]); + \assert(\is_int($propertyNameIndex)); + + $propertyName = $tokens[$propertyNameIndex]->getContent(); + + foreach ($properties as $name => $index) { + if ($name !== $propertyName) { + continue; + } + + return $index; + } + + return null; + } + + private function isPropertyToPromote(Tokens $tokens, ?int $propertyIndex, bool $isDoctrineEntity): bool + { + if ($propertyIndex === null) { + return !$this->promoteOnlyExistingProperties; + } + + if (!$isDoctrineEntity) { + return true; + } + + $phpDocIndex = $tokens->getPrevTokenOfKind($propertyIndex, [[\T_DOC_COMMENT]]); + \assert(\is_int($phpDocIndex)); + + $variableIndex = $tokens->getNextTokenOfKind($phpDocIndex, ['{', [\T_VARIABLE]]); + + if ($variableIndex !== $propertyIndex) { + return true; + } + + $docBlock = new DocBlock($tokens[$phpDocIndex]->getContent()); + + return \count($docBlock->getAnnotations()) === 0; + } + + private function getType(Tokens $tokens, ?int $variableIndex): string + { + if ($variableIndex === null) { + return ''; + } + + $index = $tokens->getPrevTokenOfKind($variableIndex, ['(', ',', [\T_PRIVATE], [\T_PROTECTED], [\T_PUBLIC], [\T_VAR], [CT::T_ATTRIBUTE_CLOSE]]); + \assert(\is_int($index)); + + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + + $type = ''; + while ($index < $variableIndex) { + $type .= $tokens[$index]->getContent(); + + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + } + + return $type; + } + + private function typesAllowPromoting(string $propertyType, string $parameterType): bool + { + if ($propertyType === '') { + return true; + } + + if (\substr($propertyType, 0, 1) === '?') { + $propertyType = \substr($propertyType, 1); + } + + if (\substr($parameterType, 0, 1) === '?') { + $parameterType = \substr($parameterType, 1); + } + + return \strtolower($propertyType) === \strtolower($parameterType); + } + + /** + * @return array + */ + private function getClassProperties(Tokens $tokens, int $classIndex): array + { + $properties = []; + $tokensAnalyzer = new TokensAnalyzer($tokens); + + foreach ($tokensAnalyzer->getClassyElements() as $index => $element) { + if ($element['classIndex'] !== $classIndex) { + continue; + } + if ($element['type'] !== 'property') { + continue; + } + + $properties[\substr($element['token']->getContent(), 1)] = $index; + } + + return $properties; + } + + /** + * @return list + */ + private function removePropertyAndReturnTokensToInsert(Tokens $tokens, ?int $propertyIndex): array + { + if ($propertyIndex === null) { + return [new Token([\T_PUBLIC, 'public'])]; + } + + $visibilityIndex = $tokens->getPrevTokenOfKind($propertyIndex, [[\T_PRIVATE], [\T_PROTECTED], [\T_PUBLIC], [\T_VAR]]); + \assert(\is_int($visibilityIndex)); + + $prevPropertyIndex = $this->getTokenOfKindSibling($tokens, -1, $propertyIndex, ['{', '}', ';', ',']); + $nextPropertyIndex = $this->getTokenOfKindSibling($tokens, 1, $propertyIndex, [';', ',']); + + $removeFrom = $tokens->getTokenNotOfKindSibling($prevPropertyIndex, 1, [[\T_WHITESPACE], [\T_COMMENT]]); + \assert(\is_int($removeFrom)); + $removeTo = $nextPropertyIndex; + if ($tokens[$prevPropertyIndex]->equals(',')) { + $removeFrom = $prevPropertyIndex; + $removeTo = $propertyIndex; + } elseif ($tokens[$nextPropertyIndex]->equals(',')) { + $removeFrom = $tokens->getPrevMeaningfulToken($propertyIndex); + \assert(\is_int($removeFrom)); + $removeFrom++; + } + + $tokensToInsert = []; + for ($index = $removeFrom; $index <= $visibilityIndex - 1; $index++) { + $tokensToInsert[] = $tokens[$index]; + } + + $visibilityToken = $tokens[$visibilityIndex]; + if ($tokens[$visibilityIndex]->isGivenKind(\T_VAR)) { + $visibilityToken = new Token([\T_PUBLIC, 'public']); + } + $tokensToInsert[] = $visibilityToken; + + $tokens->clearRange($removeFrom + 1, $removeTo); + TokenRemover::removeWithLinesIfPossible($tokens, $removeFrom); + + return $tokensToInsert; + } + + /** + * @param list $tokenKinds + */ + private function getTokenOfKindSibling(Tokens $tokens, int $direction, int $index, array $tokenKinds): int + { + $index += $direction; + + while (!$tokens[$index]->equalsAny($tokenKinds)) { + $blockType = Tokens::detectBlockType($tokens[$index]); + + if ($blockType !== null) { + if ($blockType['isStart']) { + $index = $tokens->findBlockEnd($blockType['type'], $index); + } else { + $index = $tokens->findBlockStart($blockType['type'], $index); + } + } + + $index += $direction; + } + + return $index; + } + + private function renameVariable(Tokens $tokens, int $constructorIndex, string $oldName, string $newName): void + { + $parenthesesOpenIndex = $tokens->getNextTokenOfKind($constructorIndex, ['(']); + \assert(\is_int($parenthesesOpenIndex)); + $parenthesesCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $parenthesesOpenIndex); + $braceOpenIndex = $tokens->getNextTokenOfKind($parenthesesCloseIndex, ['{']); + \assert(\is_int($braceOpenIndex)); + $braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $braceOpenIndex); + + for ($index = $parenthesesOpenIndex; $index < $braceCloseIndex; $index++) { + if ($tokens[$index]->equals([\T_VARIABLE, $oldName])) { + $tokens[$index] = new Token([\T_VARIABLE, $newName]); + } + } + } + + private function removeAssignment(Tokens $tokens, int $variableAssignmentIndex): void + { + $thisIndex = $tokens->getPrevTokenOfKind($variableAssignmentIndex, [[\T_VARIABLE]]); + \assert(\is_int($thisIndex)); + + $propertyEndIndex = $tokens->getNextTokenOfKind($variableAssignmentIndex, [';']); + \assert(\is_int($propertyEndIndex)); + + $tokens->clearRange($thisIndex + 1, $propertyEndIndex); + TokenRemover::removeWithLinesIfPossible($tokens, $thisIndex); + } + + /** + * @param list $tokensToInsert + */ + private function updateParameterSignature(Tokens $tokens, int $constructorParameterIndex, array $tokensToInsert, bool $makeTypeNullable): void + { + $prevElementIndex = $tokens->getPrevTokenOfKind($constructorParameterIndex, ['(', ',', [CT::T_ATTRIBUTE_CLOSE]]); + \assert(\is_int($prevElementIndex)); + + $propertyStartIndex = $tokens->getNextMeaningfulToken($prevElementIndex); + \assert(\is_int($propertyStartIndex)); + + foreach ($tokensToInsert as $index => $token) { + if ($token->isGivenKind(\T_PUBLIC)) { + $tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, $token->getContent()]); + } elseif ($token->isGivenKind(\T_PROTECTED)) { + $tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, $token->getContent()]); + } elseif ($token->isGivenKind(\T_PRIVATE)) { + $tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, $token->getContent()]); + } + } + $tokensToInsert[] = new Token([\T_WHITESPACE, ' ']); + + if ($makeTypeNullable && !$tokens[$propertyStartIndex]->isGivenKind(CT::T_NULLABLE_TYPE)) { + $tokensToInsert[] = new Token([CT::T_NULLABLE_TYPE, '?']); + } + + $this->tokensToInsert[$propertyStartIndex] = $tokensToInsert; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ReadonlyPromotedPropertiesFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ReadonlyPromotedPropertiesFixer.php new file mode 100644 index 00000000000..9b3ba7fac75 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/ReadonlyPromotedPropertiesFixer.php @@ -0,0 +1,191 @@ + */ + private array $promotedPropertyVisibilityKinds; + + public function __construct() + { + $this->promotedPropertyVisibilityKinds = [ + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, + CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, + ]; + if (\defined('T_PUBLIC_SET')) { + $this->promotedPropertyVisibilityKinds[] = \T_PUBLIC_SET; + $this->promotedPropertyVisibilityKinds[] = \T_PROTECTED_SET; + $this->promotedPropertyVisibilityKinds[] = \T_PRIVATE_SET; + } + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Promoted properties must be declared as read-only.', + [ + new VersionSpecificCodeSample( + 'isAnyTokenKindsFound($this->promotedPropertyVisibilityKinds); + } + + public function isRisky(): bool + { + return true; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $constructorAnalyzer = new ConstructorAnalyzer(); + + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CLASS)) { + continue; + } + + if ($this->isClassReadonly($tokens, $index)) { + continue; + } + + $constructorAnalysis = $constructorAnalyzer->findNonAbstractConstructor($tokens, $index); + if ($constructorAnalysis === null) { + continue; + } + + $constructorNameIndex = $tokens->getNextMeaningfulToken($constructorAnalysis->getConstructorIndex()); + + $classOpenBraceIndex = $tokens->getNextTokenOfKind($index, ['{']); + \assert(\is_int($classOpenBraceIndex)); + $classCloseBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classOpenBraceIndex); + + $constructorOpenParenthesisIndex = $tokens->getNextTokenOfKind($constructorAnalysis->getConstructorIndex(), ['(']); + \assert(\is_int($constructorOpenParenthesisIndex)); + $constructorCloseParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $constructorOpenParenthesisIndex); + + $this->fixParameters( + $tokens, + $classOpenBraceIndex, + $classCloseBraceIndex, + $constructorOpenParenthesisIndex, + $constructorCloseParenthesisIndex, + ); + } + } + + private function isClassReadonly(Tokens $tokens, int $index): bool + { + do { + $index = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($index)); + } while ($tokens[$index]->isGivenKind([\T_ABSTRACT, \T_FINAL])); + + return $tokens[$index]->isGivenKind(\T_READONLY); + } + + private function fixParameters( + Tokens $tokens, + int $classOpenBraceIndex, + int $classCloseBraceIndex, + int $constructorOpenParenthesisIndex, + int $constructorCloseParenthesisIndex + ): void { + for ($index = $constructorCloseParenthesisIndex; $index > $constructorOpenParenthesisIndex; $index--) { + if (!$tokens[$index]->isGivenKind(\T_VARIABLE)) { + continue; + } + + $insertIndex = $this->getInsertIndex($tokens, $index); + if ($insertIndex === null) { + continue; + } + + $propertyAssignment = $tokens->findSequence( + [ + [\T_VARIABLE, '$this'], + [\T_OBJECT_OPERATOR], + [\T_STRING, \substr($tokens[$index]->getContent(), 1)], + ], + $classOpenBraceIndex, + $classCloseBraceIndex, + ); + if ($propertyAssignment !== null) { + continue; + } + + $tokens->insertAt( + $insertIndex + 1, + [ + new Token([\T_WHITESPACE, ' ']), + new Token([\T_READONLY, 'readonly']), + ], + ); + } + } + + private function getInsertIndex(Tokens $tokens, int $index): ?int + { + $insertIndex = null; + + $index = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($index)); + while (!$tokens[$index]->equalsAny([',', '('])) { + $index = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($index)); + if ($tokens[$index]->isGivenKind(\T_READONLY)) { + return null; + } + if ($insertIndex === null && $tokens[$index]->isGivenKind($this->promotedPropertyVisibilityKinds)) { + $insertIndex = $index; + } + } + + return $insertIndex; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/SingleSpaceAfterStatementFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/SingleSpaceAfterStatementFixer.php new file mode 100644 index 00000000000..0a420b815ed --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/SingleSpaceAfterStatementFixer.php @@ -0,0 +1,162 @@ + + * + * @phpstan-type _InputConfig array{allow_linebreak?: bool} + * @phpstan-type _Config array{allow_linebreak: bool} + * + * @no-named-arguments + */ +final class SingleSpaceAfterStatementFixer extends AbstractFixer implements ConfigurableFixerInterface +{ + /** @var list */ + private array $tokens = [ + \T_ABSTRACT, + \T_AS, + \T_BREAK, + \T_CASE, + \T_CATCH, + \T_CLASS, + \T_CLONE, + \T_CONST, + \T_CONTINUE, + \T_DO, + \T_ECHO, + \T_ELSE, + \T_ELSEIF, + \T_EXTENDS, + \T_FINAL, + \T_FINALLY, + \T_FOR, + \T_FOREACH, + \T_FUNCTION, + \T_GLOBAL, + \T_GOTO, + \T_IF, + \T_IMPLEMENTS, + \T_INCLUDE, + \T_INCLUDE_ONCE, + \T_INSTANCEOF, + \T_INSTEADOF, + \T_INTERFACE, + \T_NAMESPACE, + \T_NEW, + \T_PRINT, + \T_PRIVATE, + \T_PROTECTED, + \T_PUBLIC, + \T_REQUIRE, + \T_REQUIRE_ONCE, + \T_RETURN, + \T_SWITCH, + \T_THROW, + \T_TRAIT, + \T_TRY, + \T_USE, + \T_VAR, + \T_WHILE, + \T_YIELD, + \T_YIELD_FROM, + CT::T_CONST_IMPORT, + CT::T_FUNCTION_IMPORT, + CT::T_USE_TRAIT, + CT::T_USE_LAMBDA, + ]; + + private bool $allowLinebreak = false; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Statements not followed by a semicolon must be followed by a single space.', + [new CodeSample("bar();\n")], + '', + ); + } + + public function getConfigurationDefinition(): FixerConfigurationResolverInterface + { + return new FixerConfigurationResolver([ + (new FixerOptionBuilder('allow_linebreak', 'whether to allow statement followed by linebreak')) + ->setAllowedTypes(['bool']) + ->setDefault($this->allowLinebreak) + ->getOption(), + ]); + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + if (\array_key_exists('allow_linebreak', $configuration)) { + $this->allowLinebreak = $configuration['allow_linebreak']; + } + } + + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAnyTokenKindsFound($this->tokens); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind($this->tokens)) { + continue; + } + + if (!$this->canAddSpaceAfter($tokens, $index)) { + continue; + } + + $tokens->ensureWhitespaceAtIndex($index + 1, 0, ' '); + } + } + + private function canAddSpaceAfter(Tokens $tokens, int $index): bool + { + if ($tokens[$index + 1]->isGivenKind(\T_WHITESPACE)) { + return !$this->allowLinebreak || !Preg::match('/\\R/', $tokens[$index + 1]->getContent()); + } + + if ($tokens[$index]->isGivenKind(\T_CLASS) && $tokens[$index + 1]->equals('(')) { + return false; + } + + return !\in_array($tokens[$index + 1]->getContent(), [';', ':'], true); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/SingleSpaceBeforeStatementFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/SingleSpaceBeforeStatementFixer.php new file mode 100644 index 00000000000..545f4f46403 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/SingleSpaceBeforeStatementFixer.php @@ -0,0 +1,146 @@ + */ + private array $tokens = [ + \T_ABSTRACT, + \T_AS, + \T_BREAK, + \T_CASE, + \T_CATCH, + \T_CLASS, + \T_CLONE, + \T_CONST, + \T_CONTINUE, + \T_DO, + \T_ECHO, + \T_ELSE, + \T_ELSEIF, + \T_EXTENDS, + \T_FINAL, + \T_FINALLY, + \T_FOR, + \T_FOREACH, + \T_FUNCTION, + \T_GLOBAL, + \T_GOTO, + \T_IF, + \T_IMPLEMENTS, + \T_INCLUDE, + \T_INCLUDE_ONCE, + \T_INSTANCEOF, + \T_INSTEADOF, + \T_INTERFACE, + \T_NAMESPACE, + \T_NEW, + \T_PRINT, + \T_PRIVATE, + \T_PROTECTED, + \T_PUBLIC, + \T_REQUIRE, + \T_REQUIRE_ONCE, + \T_RETURN, + \T_SWITCH, + \T_THROW, + \T_TRAIT, + \T_TRY, + \T_USE, + \T_VAR, + \T_WHILE, + \T_YIELD, + \T_YIELD_FROM, + CT::T_CONST_IMPORT, + CT::T_FUNCTION_IMPORT, + CT::T_USE_TRAIT, + CT::T_USE_LAMBDA, + ]; + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition( + 'Statements not preceded by a line break must be preceded by a single space.', + [new CodeSample("isAnyTokenKindsFound($this->tokens); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind($this->tokens)) { + continue; + } + + if ($tokens[$index - 1]->isGivenKind(\T_OPEN_TAG)) { + continue; + } + + if ($tokens[$index - 2]->isGivenKind(\T_OPEN_TAG)) { + $this->fixTwoTokensAfterOpenTag($tokens, $index); + continue; + } + + $this->fixMoreThanTwoTokensAfterOpenTag($tokens, $index); + } + } + + private function fixTwoTokensAfterOpenTag(Tokens $tokens, int $index): void + { + if ($tokens[$index - 1]->isGivenKind(\T_WHITESPACE) && !Preg::match('/\\R/', $tokens[$index - 2]->getContent())) { + $tokens->clearAt($index - 1); + } + } + + private function fixMoreThanTwoTokensAfterOpenTag(Tokens $tokens, int $index): void + { + if ($tokens[$index - 1]->isGivenKind(\T_WHITESPACE)) { + if (!Preg::match('/\\R/', $tokens[$index - 1]->getContent())) { + $tokens[$index - 1] = new Token([\T_WHITESPACE, ' ']); + } + + return; + } + + if (!\in_array($tokens[$index - 1]->getContent(), ['!', '(', '@', '[', '{'], true)) { + $tokens->insertAt($index, new Token([\T_WHITESPACE, ' '])); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/StringableInterfaceFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/StringableInterfaceFixer.php new file mode 100644 index 00000000000..99942ce7e9c --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/StringableInterfaceFixer.php @@ -0,0 +1,236 @@ += 80000 && $tokens->isAllTokenKindsFound([\T_CLASS, \T_STRING]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + $namespaceStartIndex = 0; + + for ($index = 1; $index < $tokens->count(); $index++) { + if ($tokens[$index]->isGivenKind(\T_NAMESPACE)) { + $namespaceStartIndex = $index; + continue; + } + + if (!$tokens[$index]->isGivenKind(\T_CLASS)) { + continue; + } + + $classStartIndex = $tokens->getNextTokenOfKind($index, ['{']); + \assert(\is_int($classStartIndex)); + + $classEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classStartIndex); + + if (!$this->doesHaveToStringMethod($tokens, $classStartIndex, $classEndIndex)) { + continue; + } + + if ($this->doesImplementStringable($tokens, $namespaceStartIndex, $index, $classStartIndex)) { + continue; + } + + $this->addStringableInterface($tokens, $index); + } + } + + private function doesHaveToStringMethod(Tokens $tokens, int $classStartIndex, int $classEndIndex): bool + { + $index = $classStartIndex; + + while ($index < $classEndIndex) { + $index++; + + if ($tokens[$index]->equals('{')) { + $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index); + continue; + } + + if (!$tokens[$index]->isGivenKind(\T_FUNCTION)) { + continue; + } + + $functionNameIndex = $tokens->getNextMeaningfulToken($index); + + if ($tokens[$functionNameIndex]->equals([\T_STRING, '__toString'], false)) { + return true; + } + } + + return false; + } + + private function doesImplementStringable(Tokens $tokens, int $namespaceStartIndex, int $classKeywordIndex, int $classOpenBraceIndex): bool + { + $interfaces = $this->getInterfaces($tokens, $classKeywordIndex, $classOpenBraceIndex); + if ($interfaces === []) { + return false; + } + + if (\in_array('\\stringable', $interfaces, true)) { + return true; + } + + if ($namespaceStartIndex === 0 && \in_array('stringable', $interfaces, true)) { + return true; + } + + foreach ($this->getImports($tokens, $namespaceStartIndex, $classKeywordIndex) as $import) { + if (\in_array($import, $interfaces, true)) { + return true; + } + } + + return false; + } + + /** + * @return list + */ + private function getInterfaces(Tokens $tokens, int $classKeywordIndex, int $classOpenBraceIndex): array + { + $implementsIndex = $tokens->getNextTokenOfKind($classKeywordIndex, ['{', [\T_IMPLEMENTS]]); + \assert(\is_int($implementsIndex)); + + $interfaces = []; + $interface = ''; + for ( + $index = $tokens->getNextMeaningfulToken($implementsIndex); + $index < $classOpenBraceIndex; + $index = $tokens->getNextMeaningfulToken($index) + ) { + \assert(\is_int($index)); + if ($tokens[$index]->equals(',')) { + $interfaces[] = \strtolower($interface); + $interface = ''; + continue; + } + $interface .= $tokens[$index]->getContent(); + } + if ($interface !== '') { + $interfaces[] = \strtolower($interface); + } + + return $interfaces; + } + + /** + * @return iterable + */ + private function getImports(Tokens $tokens, int $namespaceStartIndex, int $classKeywordIndex): iterable + { + for ($index = $namespaceStartIndex; $index < $classKeywordIndex; $index++) { + if (!$tokens[$index]->isGivenKind(\T_USE)) { + continue; + } + $nameIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($nameIndex)); + + if ($tokens[$nameIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $nameIndex = $tokens->getNextMeaningfulToken($nameIndex); + \assert(\is_int($nameIndex)); + } + + $nextIndex = $tokens->getNextMeaningfulToken($nameIndex); + \assert(\is_int($nextIndex)); + if ($tokens[$nextIndex]->isGivenKind(\T_AS)) { + $nameIndex = $tokens->getNextMeaningfulToken($nextIndex); + \assert(\is_int($nameIndex)); + } + + yield \strtolower($tokens[$nameIndex]->getContent()); + } + } + + private function addStringableInterface(Tokens $tokens, int $classIndex): void + { + $implementsIndex = $tokens->getNextTokenOfKind($classIndex, ['{', [\T_IMPLEMENTS]]); + \assert(\is_int($implementsIndex)); + + if ($tokens[$implementsIndex]->equals('{')) { + $prevIndex = $tokens->getPrevMeaningfulToken($implementsIndex); + \assert(\is_int($prevIndex)); + + $tokens->insertAt( + $prevIndex + 1, + [ + new Token([\T_WHITESPACE, ' ']), + new Token([\T_IMPLEMENTS, 'implements']), + new Token([\T_WHITESPACE, ' ']), + new Token([\T_NS_SEPARATOR, '\\']), + new Token([\T_STRING, \Stringable::class]), + ], + ); + + return; + } + + $afterImplementsIndex = $tokens->getNextMeaningfulToken($implementsIndex); + \assert(\is_int($afterImplementsIndex)); + + $tokens->insertAt( + $afterImplementsIndex, + [ + new Token([\T_NS_SEPARATOR, '\\']), + new Token([\T_STRING, \Stringable::class]), + new Token(','), + new Token([\T_WHITESPACE, ' ']), + ], + ); + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/TrimKeyFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/TrimKeyFixer.php new file mode 100644 index 00000000000..7a5ce515f72 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/TrimKeyFixer.php @@ -0,0 +1,99 @@ + 'v1', + 'option 2 or 3' => 'v23', + ]; + + PHP)], + ); + } + + public function getPriority(): int + { + return 0; + } + + public function isCandidate(Tokens $tokens): bool + { + return $tokens->isAnyTokenKindsFound([\T_CONSTANT_ENCAPSED_STRING]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_DOUBLE_ARROW)) { + continue; + } + + $indexToFix = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($indexToFix)); + + if (!$tokens[$indexToFix]->isGivenKind([\T_CONSTANT_ENCAPSED_STRING])) { + continue; + } + + $content = $tokens[$indexToFix]->getContent(); + $stringBorderQuote = $content[0]; + $innerContent = \substr($content, 1, -1); + + $newInnerContent = Preg::replace('/\\s{2,}/', ' ', $innerContent); + + $prevIndex = $tokens->getPrevMeaningfulToken($indexToFix); + if (!$tokens[$prevIndex]->equals('.')) { + $newInnerContent = \ltrim($newInnerContent); + } + + $nextIndex = $tokens->getNextMeaningfulToken($indexToFix); + if (!$tokens[$nextIndex]->equals('.')) { + $newInnerContent = \rtrim($newInnerContent); + } + + if ($newInnerContent === '') { + continue; + } + + $newContent = $stringBorderQuote . $newInnerContent . $stringBorderQuote; + + if ($content === $newContent) { + continue; + } + + $tokens[$indexToFix] = new Token([\T_CONSTANT_ENCAPSED_STRING, $newContent]); + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/TypedClassConstantFixer.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/TypedClassConstantFixer.php new file mode 100644 index 00000000000..bda909f51d1 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixer/TypedClassConstantFixer.php @@ -0,0 +1,204 @@ +isAllTokenKindsFound([\T_CLASS, \T_CONST]); + } + + public function isRisky(): bool + { + return false; + } + + public function fix(\SplFileInfo $file, Tokens $tokens): void + { + for ($index = $tokens->count() - 1; $index > 0; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CLASS)) { + continue; + } + + $openParenthesisIndex = $tokens->getNextTokenOfKind($index, ['{']); + \assert(\is_int($openParenthesisIndex)); + + $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openParenthesisIndex); + + self::fixClass($tokens, $openParenthesisIndex, $closeParenthesisIndex); + } + } + + private static function fixClass(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): void + { + for ($index = $closeParenthesisIndex; $index > $openParenthesisIndex; $index--) { + if (!$tokens[$index]->isGivenKind(\T_CONST)) { + continue; + } + + $constantNameIndex = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($constantNameIndex)); + + $assignmentIndex = $tokens->getNextMeaningfulToken($constantNameIndex); + \assert(\is_int($assignmentIndex)); + + if (!$tokens[$assignmentIndex]->equals('=')) { + continue; + } + + $expressionStartIndex = $tokens->getNextMeaningfulToken($assignmentIndex); + \assert(\is_int($expressionStartIndex)); + + if ($tokens[$expressionStartIndex]->isGivenKind(\T_NS_SEPARATOR)) { + $expressionStartIndex = $tokens->getNextMeaningfulToken($expressionStartIndex); + \assert(\is_int($expressionStartIndex)); + } + + $type = self::getTypeOfExpression($tokens, $expressionStartIndex); + + $tokens->insertAt( + $constantNameIndex, + [ + new Token([$type === 'array' ? CT::T_ARRAY_TYPEHINT : \T_STRING, $type]), + new Token([\T_WHITESPACE, ' ']), + ], + ); + } + } + + private static function getTypeOfExpression(Tokens $tokens, int $index): string + { + $semicolonIndex = $tokens->getNextTokenOfKind($index, [';']); + \assert(\is_int($semicolonIndex)); + + $beforeSemicolonIndex = $tokens->getPrevMeaningfulToken($semicolonIndex); + \assert(\is_int($beforeSemicolonIndex)); + + $foundKinds = []; + + $questionMarkCount = 0; + do { + if ($questionMarkCount > 1) { + return 'mixed'; + } + $kind = $tokens[$index]->getId() ?? $tokens[$index]->getContent(); + if ($kind === '?') { + $questionMarkCount++; + $foundKinds = []; + continue; + } + $foundKinds[] = $kind; + + $index = $tokens->getNextMeaningfulToken($index); + \assert(\is_int($index)); + } while ($index < $semicolonIndex); + + if ($foundKinds === [\T_STRING]) { + $lowercasedContent = \strtolower($tokens[$beforeSemicolonIndex]->getContent()); + if (\in_array($lowercasedContent, ['false', 'true', 'null'], true)) { + return $lowercasedContent; + } + } + + return self::getTypeOfExpressionForTokenKinds($foundKinds); + } + + /** + * @param list $tokenKinds + */ + private static function getTypeOfExpressionForTokenKinds(array $tokenKinds): string + { + if (self::isOfTypeBasedOnKinds($tokenKinds, [], [\T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN])) { + return 'array'; + } + + if (self::isOfTypeBasedOnKinds($tokenKinds, self::INTEGER_KINDS, [])) { + return 'int'; + } + + if (self::isOfTypeBasedOnKinds($tokenKinds, self::FLOAT_KINDS, [])) { + return 'float'; + } + + if (self::isOfTypeBasedOnKinds($tokenKinds, self::STRING_KINDS, ['.', CT::T_CLASS_CONSTANT])) { + return 'string'; + } + + return 'mixed'; + } + + /** + * @param list $expressionTokenKinds + * @param list $expectedKinds + * @param list $instantWinners + */ + private static function isOfTypeBasedOnKinds( + array $expressionTokenKinds, + array $expectedKinds, + array $instantWinners + ): bool { + foreach ($expressionTokenKinds as $index => $expressionTokenKind) { + if (\in_array($expressionTokenKind, $instantWinners, true)) { + return true; + } + if (\in_array($expressionTokenKind, $expectedKinds, true)) { + unset($expressionTokenKinds[$index]); + } + } + + return $expressionTokenKinds === []; + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixers.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixers.php new file mode 100644 index 00000000000..7669dfd2539 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/Fixers.php @@ -0,0 +1,46 @@ + + * + * @no-named-arguments + */ +final class Fixers implements \IteratorAggregate +{ + /** + * @return \Generator + */ + public function getIterator(): \Generator + { + $classNames = []; + foreach (new \DirectoryIterator(__DIR__ . '/Fixer') as $fileInfo) { + $fileName = $fileInfo->getBasename('.php'); + if (\in_array($fileName, ['.', '..', 'AbstractFixer', 'AbstractTypesFixer'], true)) { + continue; + } + $classNames[] = __NAMESPACE__ . '\\Fixer\\' . $fileName; + } + + \sort($classNames); + + foreach ($classNames as $className) { + $fixer = new $className(); + \assert($fixer instanceof FixerInterface); + + yield $fixer; + } + } +} diff --git a/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/TokenRemover.php b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/TokenRemover.php new file mode 100644 index 00000000000..64601e8ae2e --- /dev/null +++ b/tools/.php-cs-fixer/vendor/kubawerlos/php-cs-fixer-custom-fixers/src/TokenRemover.php @@ -0,0 +1,105 @@ +getNonEmptySibling($index, -1); + \assert(\is_int($prevIndex)); + + $wasNewlineRemoved = self::handleWhitespaceBefore($tokens, $prevIndex); + + $nextIndex = $tokens->getNonEmptySibling($index, 1); + if ($nextIndex !== null) { + self::handleWhitespaceAfter($tokens, $nextIndex, $wasNewlineRemoved); + } + } + + $tokens->clearTokenAndMergeSurroundingWhitespace($index); + } + + private static function isTokenOnlyMeaningfulInLine(Tokens $tokens, int $index): bool + { + return !self::hasMeaningTokenInLineBefore($tokens, $index) && !self::hasMeaningTokenInLineAfter($tokens, $index); + } + + private static function hasMeaningTokenInLineBefore(Tokens $tokens, int $index): bool + { + $prevIndex = $tokens->getNonEmptySibling($index, -1); + \assert(\is_int($prevIndex)); + + if (!$tokens[$prevIndex]->isGivenKind([\T_OPEN_TAG, \T_WHITESPACE])) { + return true; + } + + if ($tokens[$prevIndex]->isGivenKind(\T_OPEN_TAG) && !Preg::match('/\\R$/', $tokens[$prevIndex]->getContent())) { + return true; + } + + if (!Preg::match('/\\R/', $tokens[$prevIndex]->getContent())) { + $prevPrevIndex = $tokens->getNonEmptySibling($prevIndex, -1); + \assert(\is_int($prevPrevIndex)); + + if (!$tokens[$prevPrevIndex]->isGivenKind(\T_OPEN_TAG) || !Preg::match('/\\R$/', $tokens[$prevPrevIndex]->getContent())) { + return true; + } + } + + return false; + } + + private static function hasMeaningTokenInLineAfter(Tokens $tokens, int $index): bool + { + $nextIndex = $tokens->getNonEmptySibling($index, 1); + if ($nextIndex === null) { + return false; + } + + if (!$tokens[$nextIndex]->isGivenKind(\T_WHITESPACE)) { + return true; + } + + return !Preg::match('/\\R/', $tokens[$nextIndex]->getContent()); + } + + private static function handleWhitespaceBefore(Tokens $tokens, int $index): bool + { + if (!$tokens[$index]->isGivenKind(\T_WHITESPACE)) { + return false; + } + $contentWithoutTrailingSpaces = Preg::replace('/\\h+$/', '', $tokens[$index]->getContent()); + + $contentWithoutTrailingSpacesAndNewline = Preg::replace('/\\R$/', '', $contentWithoutTrailingSpaces, 1); + + $tokens->ensureWhitespaceAtIndex($index, 0, $contentWithoutTrailingSpacesAndNewline); + + return $contentWithoutTrailingSpaces !== $contentWithoutTrailingSpacesAndNewline; + } + + private static function handleWhitespaceAfter(Tokens $tokens, int $index, bool $wasNewlineRemoved): void + { + $pattern = $wasNewlineRemoved ? '/^\\h+/' : '/^\\h*\\R/'; + + $newContent = Preg::replace($pattern, '', $tokens[$index]->getContent()); + + $tokens->ensureWhitespaceAtIndex($index, 0, $newContent); + } +} diff --git a/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/LICENSE b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/LICENSE new file mode 100644 index 00000000000..871def02eb9 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012+ Fabien Potencier, Dariusz Rumiński + +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/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/README.md b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/README.md new file mode 100644 index 00000000000..76ff83f9907 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/README.md @@ -0,0 +1,11 @@ +

+ + PHP CS Fixer logo + +

+ +PHP Coding Standards Fixer +========================== + +This is a *shim* package of [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer/). +For any related topic, please visit the [main repo](https://github.com/FriendsOfPHP/PHP-CS-Fixer/) page. diff --git a/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/composer.json b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/composer.json new file mode 100644 index 00000000000..7ece60b03f0 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/composer.json @@ -0,0 +1,35 @@ +{ + "name": "php-cs-fixer/shim", + "description": "A tool to automatically fix PHP code style", + "license": "MIT", + "type": "application", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "ext-json": "*", + "ext-tokenizer": "*" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "config": { + "sort-packages": true + } +} diff --git a/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/logo.md b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/logo.md new file mode 100644 index 00000000000..c2877f718a2 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/logo.md @@ -0,0 +1,3 @@ +The logo is © 2010+ Sensio Labs. + +Original resolution can be found at https://github.com/PHP-CS-Fixer/logo . diff --git a/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/logo.png b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/logo.png new file mode 100644 index 00000000000..0ee90a82132 Binary files /dev/null and b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/logo.png differ diff --git a/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer new file mode 100755 index 00000000000..8846422f033 --- /dev/null +++ b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer @@ -0,0 +1,18 @@ +#!/usr/bin/env php + + * Dariusz Rumiński + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +declare(strict_types=1); + +Phar::loadPhar(__DIR__ . '/php-cs-fixer.phar', 'php-cs-fixer.phar'); + +require 'phar://php-cs-fixer.phar/php-cs-fixer'; diff --git a/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer.phar b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer.phar new file mode 100755 index 00000000000..621dda34649 Binary files /dev/null and b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer.phar differ diff --git a/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer.phar.asc b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer.phar.asc new file mode 100644 index 00000000000..b61553e06ab Binary files /dev/null and b/tools/.php-cs-fixer/vendor/php-cs-fixer/shim/php-cs-fixer.phar.asc differ diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer deleted file mode 100755 index 621dda34649..00000000000 Binary files a/tools/php-cs-fixer and /dev/null differ diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer new file mode 120000 index 00000000000..0e18c6a8c4b --- /dev/null +++ b/tools/php-cs-fixer @@ -0,0 +1 @@ +./.php-cs-fixer/vendor/bin/php-cs-fixer \ No newline at end of file