File "TrailingCommaInMultilineFixer.php"
Full Path: /var/www/html/back/vendor/friendsofphp/php-cs-fixer/src/Fixer/ControlStructure/TrailingCommaInMultilineFixer.php
File size: 9.06 KB
MIME-type: text/x-php
Charset: utf-8
<?php
declare(strict_types=1);
/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Fixer\ControlStructure;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\FixerDefinition\VersionSpecification;
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
use PhpCsFixer\Future;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
/**
* @phpstan-type _AutogeneratedInputConfiguration array{
* after_heredoc?: bool,
* elements?: list<'arguments'|'array_destructuring'|'arrays'|'match'|'parameters'>,
* }
* @phpstan-type _AutogeneratedComputedConfiguration array{
* after_heredoc: bool,
* elements: list<'arguments'|'array_destructuring'|'arrays'|'match'|'parameters'>,
* }
*
* @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
*
* @author Sebastiaan Stok <s.stok@rollerscapes.net>
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
* @author Kuba Werłos <werlos@gmail.com>
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise.
*/
final class TrailingCommaInMultilineFixer extends AbstractFixer implements ConfigurableFixerInterface
{
/** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
use ConfigurableFixerTrait;
/**
* @internal
*/
public const ELEMENTS_ARRAYS = 'arrays';
/**
* @internal
*/
public const ELEMENTS_ARGUMENTS = 'arguments';
/**
* @internal
*/
public const ELEMENTS_PARAMETERS = 'parameters';
private const MATCH_EXPRESSIONS = 'match';
private const ARRAY_DESTRUCTURING = 'array_destructuring';
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Arguments lists, array destructuring lists, arrays that are multi-line, `match`-lines and parameters lists must have a trailing comma.',
[
new CodeSample("<?php\narray(\n 1,\n 2\n);\n"),
new CodeSample(
<<<'SAMPLE'
<?php
$x = [
'foo',
<<<EOD
bar
EOD
];
SAMPLE,
['after_heredoc' => true],
),
new CodeSample("<?php\nfoo(\n 1,\n 2\n);\n", ['elements' => [self::ELEMENTS_ARGUMENTS]]),
new VersionSpecificCodeSample("<?php\nfunction foo(\n \$x,\n \$y\n)\n{\n}\n", new VersionSpecification(8_00_00), ['elements' => [self::ELEMENTS_PARAMETERS]]),
],
);
}
/**
* {@inheritdoc}
*
* Must run after MultilinePromotedPropertiesFixer.
*/
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAnyTokenKindsFound([\T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN, '(', CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN]);
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder('after_heredoc', 'Whether a trailing comma should also be placed after heredoc end.'))
->setAllowedTypes(['bool'])
->setDefault(Future::getV4OrV3(true, false))
->getOption(),
(new FixerOptionBuilder('elements', \sprintf('Where to fix multiline trailing comma (PHP >= 8.0 for `%s` and `%s`).', self::ELEMENTS_PARAMETERS, self::MATCH_EXPRESSIONS))) // @TODO: remove text when PHP 8.0+ is required
->setAllowedTypes(['string[]'])
->setAllowedValues([
new AllowedValueSubset([
self::ARRAY_DESTRUCTURING,
self::ELEMENTS_ARGUMENTS,
self::ELEMENTS_ARRAYS,
self::ELEMENTS_PARAMETERS,
self::MATCH_EXPRESSIONS,
]),
])
->setDefault([self::ELEMENTS_ARRAYS])
->getOption(),
]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
$configuredElements = $this->configuration['elements'];
$fixArrays = \in_array(self::ELEMENTS_ARRAYS, $configuredElements, true);
$fixArguments = \in_array(self::ELEMENTS_ARGUMENTS, $configuredElements, true);
$fixParameters = \PHP_VERSION_ID >= 8_00_00 && \in_array(self::ELEMENTS_PARAMETERS, $configuredElements, true); // @TODO: drop condition when PHP 8.0+ is required
$fixMatch = \PHP_VERSION_ID >= 8_00_00 && \in_array(self::MATCH_EXPRESSIONS, $configuredElements, true); // @TODO: drop condition when PHP 8.0+ is required
$fixDestructuring = \in_array(self::ARRAY_DESTRUCTURING, $configuredElements, true);
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
if ($tokens[$index]->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN)) {
if ($fixDestructuring) { // array destructing short syntax
$this->fixBlock($tokens, $index);
}
continue;
}
if ($tokens[$index]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
if ($fixArrays) { // array short syntax
$this->fixBlock($tokens, $index);
}
continue;
}
if (!$tokens[$index]->equals('(')) {
continue;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(\T_ARRAY)) {
if ($fixArrays) { // array long syntax
$this->fixBlock($tokens, $index);
}
continue;
}
if ($tokens[$prevIndex]->isGivenKind(\T_LIST)) {
if ($fixDestructuring || $fixArguments) { // array destructing long syntax
$this->fixBlock($tokens, $index);
}
continue;
}
if ($fixMatch && $tokens[$prevIndex]->isGivenKind(\T_MATCH)) {
$this->fixBlock($tokens, $tokens->getNextTokenOfKind($index, ['{']));
continue;
}
$prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if ($fixArguments
&& $tokens[$prevIndex]->equalsAny([']', [\T_CLASS], [\T_STRING], [\T_VARIABLE], [\T_STATIC], [\T_ISSET], [\T_UNSET], [\T_LIST]])
&& !$tokens[$prevPrevIndex]->isGivenKind(\T_FUNCTION)
) {
$this->fixBlock($tokens, $index);
continue;
}
if (
$fixParameters
&& (
$tokens[$prevIndex]->isGivenKind(\T_STRING)
&& $tokens[$prevPrevIndex]->isGivenKind(\T_FUNCTION)
|| $tokens[$prevIndex]->isGivenKind([\T_FN, \T_FUNCTION])
)
) {
$this->fixBlock($tokens, $index);
}
}
}
private function fixBlock(Tokens $tokens, int $startIndex): void
{
$tokensAnalyzer = new TokensAnalyzer($tokens);
if (!$tokensAnalyzer->isBlockMultiline($tokens, $startIndex)) {
return;
}
$blockType = Tokens::detectBlockType($tokens[$startIndex]);
$endIndex = $tokens->findBlockEnd($blockType['type'], $startIndex);
$beforeEndIndex = $tokens->getPrevMeaningfulToken($endIndex);
if (!$tokens->isPartialCodeMultiline($beforeEndIndex, $endIndex)) {
return;
}
$beforeEndToken = $tokens[$beforeEndIndex];
// if there is some item between braces then add `,` after it
if (
$startIndex !== $beforeEndIndex && !$beforeEndToken->equals(',')
&& (true === $this->configuration['after_heredoc'] || !$beforeEndToken->isGivenKind(\T_END_HEREDOC))
) {
$tokens->insertAt($beforeEndIndex + 1, new Token(','));
$endToken = $tokens[$endIndex];
if (!$endToken->isComment() && !$endToken->isWhitespace()) {
$tokens->ensureWhitespaceAtIndex($endIndex, 1, ' ');
}
}
}
}