Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
peripherad
/
back
/
vendor
/
friendsofphp
/
php-cs-fixer
/
src
/
Fixer
/
ClassNotation
:
OrderedInterfacesFixer.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?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; } }