File "OrderedInterfacesFixer.php"
Full Path: /var/www/html/back/vendor/friendsofphp/php-cs-fixer/src/Fixer/ClassNotation/OrderedInterfacesFixer.php
File size: 9.57 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\ClassNotation;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
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\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @phpstan-type _AutogeneratedInputConfiguration array{
* case_sensitive?: bool,
* direction?: 'ascend'|'descend',
* order?: 'alpha'|'length',
* }
* @phpstan-type _AutogeneratedComputedConfiguration array{
* case_sensitive: bool,
* direction: 'ascend'|'descend',
* order: 'alpha'|'length',
* }
*
* @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
*
* @author Dave van der Brugge <dmvdbrugge@gmail.com>
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise.
*/
final class OrderedInterfacesFixer extends AbstractFixer implements ConfigurableFixerInterface
{
/** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
use ConfigurableFixerTrait;
/** @internal */
public const OPTION_DIRECTION = 'direction';
/** @internal */
public const OPTION_ORDER = 'order';
/** @internal */
public const DIRECTION_ASCEND = 'ascend';
/** @internal */
public const DIRECTION_DESCEND = 'descend';
/** @internal */
public const ORDER_ALPHA = 'alpha';
/** @internal */
public const ORDER_LENGTH = 'length';
/**
* Array of supported directions in configuration.
*
* @var non-empty-list<string>
*/
private const SUPPORTED_DIRECTION_OPTIONS = [
self::DIRECTION_ASCEND,
self::DIRECTION_DESCEND,
];
/**
* Array of supported orders in configuration.
*
* @var non-empty-list<string>
*/
private const SUPPORTED_ORDER_OPTIONS = [
self::ORDER_ALPHA,
self::ORDER_LENGTH,
];
public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
'Orders the interfaces in an `implements` or `interface extends` clause.',
[
new CodeSample(
"<?php\n\nfinal class ExampleA implements Gamma, Alpha, Beta {}\n\ninterface ExampleB extends Gamma, Alpha, Beta {}\n",
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements Gamma, Alpha, Beta {}\n\ninterface ExampleB extends Gamma, Alpha, Beta {}\n",
[self::OPTION_DIRECTION => self::DIRECTION_DESCEND],
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements MuchLonger, Short, Longer {}\n\ninterface ExampleB extends MuchLonger, Short, Longer {}\n",
[self::OPTION_ORDER => self::ORDER_LENGTH],
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements MuchLonger, Short, Longer {}\n\ninterface ExampleB extends MuchLonger, Short, Longer {}\n",
[
self::OPTION_ORDER => self::ORDER_LENGTH,
self::OPTION_DIRECTION => self::DIRECTION_DESCEND,
],
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements IgnorecaseB, IgNoReCaSeA, IgnoreCaseC {}\n\ninterface ExampleB extends IgnorecaseB, IgNoReCaSeA, IgnoreCaseC {}\n",
[
self::OPTION_ORDER => self::ORDER_ALPHA,
],
),
new CodeSample(
"<?php\n\nfinal class ExampleA implements Casesensitivea, CaseSensitiveA, CasesensitiveA {}\n\ninterface ExampleB extends Casesensitivea, CaseSensitiveA, CasesensitiveA {}\n",
[
self::OPTION_ORDER => self::ORDER_ALPHA,
'case_sensitive' => true,
],
),
],
);
}
/**
* {@inheritdoc}
*
* Must run after FullyQualifiedStrictTypesFixer, StringableForToStringFixer.
*/
public function getPriority(): int
{
return 0;
}
public function isCandidate(Tokens $tokens): bool
{
return $tokens->isTokenKindFound(\T_IMPLEMENTS)
|| $tokens->isAllTokenKindsFound([\T_INTERFACE, \T_EXTENDS]);
}
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
foreach ($tokens as $index => $token) {
if (!$token->isGivenKind(\T_IMPLEMENTS)) {
if (!$token->isGivenKind(\T_EXTENDS)) {
continue;
}
$nameTokenIndex = $tokens->getPrevMeaningfulToken($index);
$interfaceTokenIndex = $tokens->getPrevMeaningfulToken($nameTokenIndex);
$interfaceToken = $tokens[$interfaceTokenIndex];
if (!$interfaceToken->isGivenKind(\T_INTERFACE)) {
continue;
}
}
$implementsStart = $index + 1;
$implementsEnd = $tokens->getPrevMeaningfulToken($tokens->getNextTokenOfKind($implementsStart, ['{']));
$interfacesTokens = $this->getInterfaces($tokens, $implementsStart, $implementsEnd);
if (1 === \count($interfacesTokens)) {
continue;
}
$interfaces = [];
foreach ($interfacesTokens as $interfaceIndex => $interface) {
$interfaceTokens = Tokens::fromArray($interface);
$normalized = '';
$actualInterfaceIndex = $interfaceTokens->getNextMeaningfulToken(-1);
while ($interfaceTokens->offsetExists($actualInterfaceIndex)) {
$token = $interfaceTokens[$actualInterfaceIndex];
if ($token->isComment() || $token->isWhitespace()) {
break;
}
$normalized .= str_replace('\\', ' ', $token->getContent());
++$actualInterfaceIndex;
}
$interfaces[$interfaceIndex] = [
'tokens' => $interface,
'normalized' => $normalized,
'originalIndex' => $interfaceIndex,
];
}
usort($interfaces, function (array $first, array $second): int {
$score = self::ORDER_LENGTH === $this->configuration[self::OPTION_ORDER]
? \strlen($first['normalized']) - \strlen($second['normalized'])
: (
true === $this->configuration['case_sensitive']
? $first['normalized'] <=> $second['normalized']
: strcasecmp($first['normalized'], $second['normalized'])
);
if (self::DIRECTION_DESCEND === $this->configuration[self::OPTION_DIRECTION]) {
$score *= -1;
}
return $score;
});
$changed = false;
foreach ($interfaces as $interfaceIndex => $interface) {
if ($interface['originalIndex'] !== $interfaceIndex) {
$changed = true;
break;
}
}
if (!$changed) {
continue;
}
$newTokens = array_shift($interfaces)['tokens'];
foreach ($interfaces as $interface) {
array_push($newTokens, new Token(','), ...$interface['tokens']);
}
$tokens->overrideRange($implementsStart, $implementsEnd, $newTokens);
}
}
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
{
return new FixerConfigurationResolver([
(new FixerOptionBuilder(self::OPTION_ORDER, 'How the interfaces should be ordered.'))
->setAllowedValues(self::SUPPORTED_ORDER_OPTIONS)
->setDefault(self::ORDER_ALPHA)
->getOption(),
(new FixerOptionBuilder(self::OPTION_DIRECTION, 'Which direction the interfaces should be ordered.'))
->setAllowedValues(self::SUPPORTED_DIRECTION_OPTIONS)
->setDefault(self::DIRECTION_ASCEND)
->getOption(),
(new FixerOptionBuilder('case_sensitive', 'Whether the sorting should be case sensitive.'))
->setAllowedTypes(['bool'])
->setDefault(false)
->getOption(),
]);
}
/**
* @return array<int, list<Token>>
*/
private function getInterfaces(Tokens $tokens, int $implementsStart, int $implementsEnd): array
{
$interfaces = [];
$interfaceIndex = 0;
for ($i = $implementsStart; $i <= $implementsEnd; ++$i) {
if ($tokens[$i]->equals(',')) {
++$interfaceIndex;
$interfaces[$interfaceIndex] = [];
continue;
}
$interfaces[$interfaceIndex][] = $tokens[$i];
}
return $interfaces;
}
}