%PDF- %PDF-
Direktori : /home2/vacivi36/code/vendor/friendsofphp/php-cs-fixer/src/Fixer/Import/ |
Current File : //home2/vacivi36/code/vendor/friendsofphp/php-cs-fixer/src/Fixer/Import/GroupImportFixer.php |
<?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\Import; use PhpCsFixer\AbstractFixer; use PhpCsFixer\FixerDefinition\CodeSample; use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis; use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer; use PhpCsFixer\Tokenizer\CT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; /** * @author Volodymyr Kupriienko <vldmr.kuprienko@gmail.com> */ final class GroupImportFixer extends AbstractFixer { /** * {@inheritdoc} */ public function getDefinition(): FixerDefinitionInterface { return new FixerDefinition( 'There MUST be group use for the same namespaces.', [ new CodeSample( "<?php\nuse Foo\\Bar;\nuse Foo\\Baz;\n" ), ] ); } /** * {@inheritdoc} */ public function isCandidate(Tokens $tokens): bool { return $tokens->isTokenKindFound(T_USE); } /** * {@inheritdoc} */ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void { $useWithSameNamespaces = $this->getSameNamespaces($tokens); if ([] === $useWithSameNamespaces) { return; } $this->removeSingleUseStatements($useWithSameNamespaces, $tokens); $this->addGroupUseStatements($useWithSameNamespaces, $tokens); } /** * Gets namespace use analyzers with same namespaces. * * @return NamespaceUseAnalysis[] */ private function getSameNamespaces(Tokens $tokens): array { $useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens); if (0 === \count($useDeclarations)) { return []; } $allNamespaceAndType = array_map( function (NamespaceUseAnalysis $useDeclaration): string { return $this->getNamespaceNameWithSlash($useDeclaration).$useDeclaration->getType(); }, $useDeclarations ); $sameNamespaces = array_filter(array_count_values($allNamespaceAndType), static function (int $count): bool { return $count > 1; }); $sameNamespaces = array_keys($sameNamespaces); $sameNamespaceAnalysis = array_filter($useDeclarations, function (NamespaceUseAnalysis $useDeclaration) use ($sameNamespaces): bool { $namespaceNameAndType = $this->getNamespaceNameWithSlash($useDeclaration).$useDeclaration->getType(); return \in_array($namespaceNameAndType, $sameNamespaces, true); }); usort($sameNamespaceAnalysis, function (NamespaceUseAnalysis $a, NamespaceUseAnalysis $b): int { $namespaceA = $this->getNamespaceNameWithSlash($a); $namespaceB = $this->getNamespaceNameWithSlash($b); return \strlen($namespaceA) - \strlen($namespaceB) ?: strcmp($a->getFullName(), $b->getFullName()); }); return $sameNamespaceAnalysis; } /** * @param NamespaceUseAnalysis[] $statements */ private function removeSingleUseStatements(array $statements, Tokens $tokens): void { foreach ($statements as $useDeclaration) { $index = $useDeclaration->getStartIndex(); $endIndex = $useDeclaration->getEndIndex(); $useStatementTokens = [T_USE, T_WHITESPACE, T_STRING, T_NS_SEPARATOR, T_AS, CT::T_CONST_IMPORT, CT::T_FUNCTION_IMPORT]; while ($index !== $endIndex) { if ($tokens[$index]->isGivenKind($useStatementTokens)) { $tokens->clearAt($index); } ++$index; } if (isset($tokens[$index]) && $tokens[$index]->equals(';')) { $tokens->clearAt($index); } ++$index; if (isset($tokens[$index]) && $tokens[$index]->isGivenKind(T_WHITESPACE)) { $tokens->clearAt($index); } } } /** * @param NamespaceUseAnalysis[] $statements */ private function addGroupUseStatements(array $statements, Tokens $tokens): void { $currentUseDeclaration = null; $insertIndex = \array_slice($statements, -1)[0]->getEndIndex() + 1; foreach ($statements as $index => $useDeclaration) { if ($this->areDeclarationsDifferent($currentUseDeclaration, $useDeclaration)) { $currentUseDeclaration = $useDeclaration; $insertIndex += $this->createNewGroup( $tokens, $insertIndex, $useDeclaration, $this->getNamespaceNameWithSlash($currentUseDeclaration) ); } else { $newTokens = [ new Token(','), new Token([T_WHITESPACE, ' ']), ]; if ($useDeclaration->isAliased()) { $tokens->insertAt($insertIndex, $newTokens); $insertIndex += \count($newTokens); $newTokens = []; $insertIndex += $this->insertToGroupUseWithAlias($tokens, $insertIndex, $useDeclaration); } $newTokens[] = new Token([T_STRING, $useDeclaration->getShortName()]); if (!isset($statements[$index + 1]) || $this->areDeclarationsDifferent($currentUseDeclaration, $statements[$index + 1])) { $newTokens[] = new Token([CT::T_GROUP_IMPORT_BRACE_CLOSE, '}']); $newTokens[] = new Token(';'); $newTokens[] = new Token([T_WHITESPACE, "\n"]); } $tokens->insertAt($insertIndex, $newTokens); $insertIndex += \count($newTokens); } } } private function getNamespaceNameWithSlash(NamespaceUseAnalysis $useDeclaration): string { $position = strrpos($useDeclaration->getFullName(), '\\'); if (false === $position || 0 === $position) { return $useDeclaration->getFullName(); } return substr($useDeclaration->getFullName(), 0, $position + 1); } /** * Insert use with alias to the group. */ private function insertToGroupUseWithAlias(Tokens $tokens, int $insertIndex, NamespaceUseAnalysis $useDeclaration): int { $newTokens = [ new Token([T_STRING, substr($useDeclaration->getFullName(), strripos($useDeclaration->getFullName(), '\\') + 1)]), new Token([T_WHITESPACE, ' ']), new Token([T_AS, 'as']), new Token([T_WHITESPACE, ' ']), ]; $tokens->insertAt($insertIndex, $newTokens); return \count($newTokens) + 1; } /** * Creates new use statement group. */ private function createNewGroup(Tokens $tokens, int $insertIndex, NamespaceUseAnalysis $useDeclaration, string $currentNamespace): int { $insertedTokens = 0; if (\count($tokens) === $insertIndex) { $tokens->setSize($insertIndex + 1); } $newTokens = [ new Token([T_USE, 'use']), new Token([T_WHITESPACE, ' ']), ]; if ($useDeclaration->isFunction() || $useDeclaration->isConstant()) { $importStatementParams = $useDeclaration->isFunction() ? [CT::T_FUNCTION_IMPORT, 'function'] : [CT::T_CONST_IMPORT, 'const']; $newTokens[] = new Token($importStatementParams); $newTokens[] = new Token([T_WHITESPACE, ' ']); } $namespaceParts = array_filter(explode('\\', $currentNamespace)); foreach ($namespaceParts as $part) { $newTokens[] = new Token([T_STRING, $part]); $newTokens[] = new Token([T_NS_SEPARATOR, '\\']); } $newTokens[] = new Token([CT::T_GROUP_IMPORT_BRACE_OPEN, '{']); $newTokensCount = \count($newTokens); $tokens->insertAt($insertIndex, $newTokens); $insertedTokens += $newTokensCount; $insertIndex += $newTokensCount; if ($useDeclaration->isAliased()) { $inserted = $this->insertToGroupUseWithAlias($tokens, $insertIndex + 1, $useDeclaration); $insertedTokens += $inserted; $insertIndex += $inserted; } $tokens->insertAt($insertIndex, new Token([T_STRING, $useDeclaration->getShortName()])); ++$insertedTokens; return $insertedTokens; } /** * Check if namespace use analyses are different. */ private function areDeclarationsDifferent(?NamespaceUseAnalysis $analysis1, ?NamespaceUseAnalysis $analysis2): bool { if (null === $analysis1 || null === $analysis2) { return true; } $namespaceName1 = $this->getNamespaceNameWithSlash($analysis1); $namespaceName2 = $this->getNamespaceNameWithSlash($analysis2); return $namespaceName1 !== $namespaceName2 || $analysis1->getType() !== $analysis2->getType(); } }