/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); foreach ($tokensAnalyzer->getImportUseIndexes() as $index) { $indent = ''; // if previous line ends with comment and current line starts with whitespace, use current indent if ($tokens[$index - 1]->isWhitespace(" \t") && $tokens[$index - 2]->isGivenKind(T_COMMENT)) { $indent = $tokens[$index - 1]->getContent(); } elseif ($tokens[$index - 1]->isWhitespace()) { $indent = Utils::calculateTrailingWhitespaceIndent($tokens[$index - 1]); } $newline = "\n"; // Handle insert index for inline T_COMMENT with whitespace after semicolon $semicolonIndex = $tokens->getNextTokenOfKind($index, array(';', '{')); $insertIndex = $semicolonIndex + 1; if ($tokens[$insertIndex]->isWhitespace(" \t") && $tokens[$insertIndex + 1]->isComment()) { ++$insertIndex; } // Increment insert index for inline T_COMMENT or T_DOC_COMMENT if ($tokens[$insertIndex]->isComment()) { ++$insertIndex; } $afterSemicolon = $tokens->getNextMeaningfulToken($semicolonIndex); if (!$tokens[$afterSemicolon]->isGivenKind(T_USE)) { $newline .= "\n"; } if ($tokens[$insertIndex]->isWhitespace()) { $nextToken = $tokens[$insertIndex]; $nextToken->setContent($newline . $indent . ltrim($nextToken->getContent())); } elseif ($newline && $indent) { $tokens->insertAt($insertIndex, new Token(array(T_WHITESPACE, $newline . $indent))); } } }
/** * Replace occurrences of the name of the classy element by "self" (if possible). * * @param Tokens $tokens * @param string $name * @param int $startIndex * @param int $endIndex */ private function replaceNameOccurrences(Tokens $tokens, $name, $startIndex, $endIndex) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($i = $startIndex; $i < $endIndex; ++$i) { $token = $tokens[$i]; // skip lambda functions (PHP < 5.4 compatibility) if ($token->isGivenKind(T_FUNCTION) && $tokensAnalyzer->isLambda($i)) { $i = $tokens->getNextTokenOfKind($i, array('{')); $i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $i); continue; } if (!$token->equals(array(T_STRING, $name), false)) { continue; } $prevToken = $tokens[$tokens->getPrevMeaningfulToken($i)]; $nextToken = $tokens[$tokens->getNextMeaningfulToken($i)]; // skip tokens that are part of a fully qualified name if ($prevToken->isGivenKind(T_NS_SEPARATOR) || $nextToken->isGivenKind(T_NS_SEPARATOR)) { continue; } if ($prevToken->isGivenKind(array(T_INSTANCEOF, T_NEW)) || $nextToken->isGivenKind(T_PAAMAYIM_NEKUDOTAYIM)) { $token->setContent('self'); } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $uses = array_reverse($tokensAnalyzer->getImportUseIndexes()); foreach ($uses as $index) { $endIndex = $tokens->getNextTokenOfKind($index, array(';')); $declarationContent = $tokens->generatePartialCode($index + 1, $endIndex - 1); $declarationParts = explode(',', $declarationContent); if (1 === count($declarationParts)) { continue; } $declarationContent = array(); foreach ($declarationParts as $declarationPart) { $declarationContent[] = 'use ' . trim($declarationPart) . ';'; } $declarationContent = implode("\n" . $this->detectIndent($tokens, $index), $declarationContent); for ($i = $index; $i <= $endIndex; ++$i) { $tokens[$i]->clear(); } $declarationTokens = Tokens::fromCode('<?php ' . $declarationContent); $declarationTokens[0]->clear(); $declarationTokens->clearEmptyTokens(); $tokens->insertAt($index, $declarationTokens); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $namespacesImports = $tokensAnalyzer->getImportUseIndexes(true); $usesOrder = array(); if (!count($namespacesImports)) { return; } foreach ($namespacesImports as $uses) { $uses = array_reverse($uses); $usesOrder = array_replace($usesOrder, $this->getNewOrder($uses, $tokens)); } $usesOrder = array_reverse($usesOrder, true); $mapStartToEnd = array(); foreach ($usesOrder as $use) { $mapStartToEnd[$use[1]] = $use[2]; } // Now insert the new tokens, starting from the end foreach ($usesOrder as $index => $use) { $declarationTokens = Tokens::fromCode('<?php use ' . $use[0] . ';'); $declarationTokens->clearRange(0, 2); // clear `<?php use ` $declarationTokens[count($declarationTokens) - 1]->clear(); // clear `;` $declarationTokens->clearEmptyTokens(); $tokens->overrideRange($index, $mapStartToEnd[$index], $declarationTokens); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $namespaceDeclarations = $this->getNamespaceDeclarations($tokens); $useDeclarationsIndexes = $tokensAnalyzer->getImportUseIndexes(); $useDeclarations = $this->getNamespaceUseDeclarations($tokens, $useDeclarationsIndexes); $contentWithoutUseDeclarations = $this->generateCodeWithoutPartials($tokens, array_merge($namespaceDeclarations, $useDeclarations)); $useUsages = $this->detectUseUsages($contentWithoutUseDeclarations, $useDeclarations); $this->removeUnusedUseDeclarations($tokens, $useDeclarations, $useUsages); $this->removeUsesInSameNamespace($tokens, $useDeclarations, $namespaceDeclarations); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $namespacesImports = $tokensAnalyzer->getImportUseIndexes(true); if (!count($namespacesImports)) { return; } foreach ($namespacesImports as $uses) { $uses = array_reverse($uses); $this->fixLineBreaksPerImportGroup($tokens, $uses); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $ending = $this->whitespacesConfig->getLineEnding(); $tokensAnalyzer = new TokensAnalyzer($tokens); foreach ($tokensAnalyzer->getImportUseIndexes() as $index) { $indent = ''; // if previous line ends with comment and current line starts with whitespace, use current indent if ($tokens[$index - 1]->isWhitespace(" \t") && $tokens[$index - 2]->isGivenKind(T_COMMENT)) { $indent = $tokens[$index - 1]->getContent(); } elseif ($tokens[$index - 1]->isWhitespace()) { $indent = Utils::calculateTrailingWhitespaceIndent($tokens[$index - 1]); } $semicolonIndex = $tokens->getNextTokenOfKind($index, array(';', array(T_CLOSE_TAG))); // Handle insert index for inline T_COMMENT with whitespace after semicolon $insertIndex = $semicolonIndex; if ($tokens[$semicolonIndex]->isGivenKind(T_CLOSE_TAG)) { if ($tokens[$insertIndex - 1]->isWhitespace()) { --$insertIndex; } $tokens->insertAt($insertIndex, new Token(';')); } if ($semicolonIndex === count($tokens) - 1) { $tokens->insertAt($insertIndex + 1, new Token(array(T_WHITESPACE, $ending . $ending . $indent))); } else { $newline = $ending; $tokens[$semicolonIndex]->isGivenKind(T_CLOSE_TAG) ? --$insertIndex : ++$insertIndex; if ($tokens[$insertIndex]->isWhitespace(" \t") && $tokens[$insertIndex + 1]->isComment()) { ++$insertIndex; } // Increment insert index for inline T_COMMENT or T_DOC_COMMENT if ($tokens[$insertIndex]->isComment()) { ++$insertIndex; } $afterSemicolon = $tokens->getNextMeaningfulToken($semicolonIndex); if (null === $afterSemicolon || !$tokens[$afterSemicolon]->isGivenKind(T_USE)) { $newline .= $ending; } if ($tokens[$insertIndex]->isWhitespace()) { $nextToken = $tokens[$insertIndex]; $nextMeaningfulAfterUseIndex = $tokens->getNextMeaningfulToken($insertIndex); if (null !== $nextMeaningfulAfterUseIndex && $tokens[$nextMeaningfulAfterUseIndex]->isGivenKind(T_USE)) { if (substr_count($nextToken->getContent(), "\n") < 2) { $nextToken->setContent($newline . $indent . ltrim($nextToken->getContent())); } } else { $nextToken->setContent($newline . $indent . ltrim($nextToken->getContent())); } } else { $tokens->insertAt($insertIndex, new Token(array(T_WHITESPACE, $newline . $indent))); } } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $uses = array_reverse($tokensAnalyzer->getImportUseIndexes()); foreach ($uses as $index) { $endIndex = $tokens->getNextTokenOfKind($index, array(';', array(T_CLOSE_TAG))); $groupClose = $tokens->getPrevMeaningfulToken($endIndex); if ($tokens[$groupClose]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) { $this->fixGroupUse($tokens, $index, $endIndex); } else { $this->fixMultipleUse($tokens, $index, $endIndex); } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; $index >= 0; --$index) { if ($tokensAnalyzer->isUnarySuccessorOperator($index)) { $tokens->removeLeadingWhitespace($index); continue; } if ($tokensAnalyzer->isUnaryPredecessorOperator($index)) { $tokens->removeTrailingWhitespace($index); continue; } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; $index >= 0; --$index) { $token = $tokens[$index]; if ($tokensAnalyzer->isUnaryPredecessorOperator($index) && $token->equals('!')) { if (!$tokens[$index + 1]->isWhitespace()) { $tokens->insertAt($index + 1, new Token(array(T_WHITESPACE, ' '))); } else { $tokens[$index + 1]->setContent(' '); } } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; $index >= 0; --$index) { if (!$tokensAnalyzer->isBinaryOperator($index)) { continue; } if (!$tokens[$index + 1]->isWhitespace()) { $tokens->insertAt($index + 1, new Token(array(T_WHITESPACE, ' '))); } if (!$tokens[$index - 1]->isWhitespace()) { $tokens->insertAt($index, new Token(array(T_WHITESPACE, ' '))); } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; $index >= 0; --$index) { $token = $tokens[$index]; if (!$token->isGivenKind(T_FUNCTION)) { continue; } $startParenthesisIndex = $tokens->getNextTokenOfKind($index, array('(')); $endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex); $startBraceIndex = $tokens->getNextTokenOfKind($endParenthesisIndex, array(';', '{')); $startBraceToken = $tokens[$startBraceIndex]; if ($startBraceToken->equals('{')) { // fix single-line whitespace before { // eg: `function foo(){}` => `function foo() {}` // eg: `function foo() {}` => `function foo() {}` if (!$tokens[$startBraceIndex - 1]->isWhitespace() || $tokens[$startBraceIndex - 1]->isWhitespace($this->singleLineWhitespaceOptions)) { $tokens->ensureWhitespaceAtIndex($startBraceIndex - 1, 1, ' '); } } $afterParenthesisIndex = $tokens->getNextNonWhitespace($endParenthesisIndex); $afterParenthesisToken = $tokens[$afterParenthesisIndex]; if ($afterParenthesisToken->isGivenKind(CT_USE_LAMBDA)) { $useStartParenthesisIndex = $tokens->getNextTokenOfKind($afterParenthesisIndex, array('(')); $useEndParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $useStartParenthesisIndex); // fix whitespace after CT_USE_LAMBDA $tokens->ensureWhitespaceAtIndex($afterParenthesisIndex + 1, 0, ' '); // remove single-line edge whitespaces inside use parentheses $this->fixParenthesisInnerEdge($tokens, $useStartParenthesisIndex, $useEndParenthesisIndex); // fix whitespace before CT_USE_LAMBDA $tokens->ensureWhitespaceAtIndex($afterParenthesisIndex - 1, 1, ' '); } // remove single-line edge whitespaces inside parameters list parentheses $this->fixParenthesisInnerEdge($tokens, $startParenthesisIndex, $endParenthesisIndex); if (!$tokensAnalyzer->isLambda($index)) { // remove whitespace before ( // eg: `function foo () {}` => `function foo() {}` if ($tokens[$startParenthesisIndex - 1]->isWhitespace()) { $tokens[$startParenthesisIndex - 1]->clear(); } } // fix whitespace after T_FUNCTION // eg: `function foo() {}` => `function foo() {}` $tokens->ensureWhitespaceAtIndex($index + 1, 0, ' '); } }
/** * @param Tokens $tokens * @param TokensAnalyzer $tokensAnalyzer * @param int $classStart * @param int $classEnd */ private function fixClass(Tokens $tokens, TokensAnalyzer $tokensAnalyzer, $classStart, $classEnd) { for ($index = $classEnd; $index > $classStart; --$index) { if (!$tokens[$index]->isGivenKind(T_FUNCTION) || $tokensAnalyzer->isLambda($index)) { continue; } $attributes = $tokensAnalyzer->getMethodAttributes($index); if (true === $attributes['abstract']) { $methodEnd = $tokens->getNextTokenOfKind($index, array(';')); } else { $methodStart = $tokens->getNextTokenOfKind($index, array('{')); $methodEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $methodStart, true); } $this->fixSpaceBelowMethod($tokens, $classEnd, $methodEnd); $this->fixSpaceAboveMethod($tokens, $classStart, $index); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $classes = array_keys($tokens->findGivenKind(T_CLASS)); $numClasses = count($classes); for ($i = 0; $i < $numClasses; ++$i) { $index = $classes[$i]; // is it an an anonymous class definition? if ($tokensAnalyzer->isAnonymousClass($index)) { continue; } // is it inside a namespace? $nspIndex = $tokens->getPrevTokenOfKind($index, array(array(T_NAMESPACE, 'namespace'))); if (null !== $nspIndex) { $nspIndex = $tokens->getNextMeaningfulToken($nspIndex); // make sure it's not the global namespace, as PHP4 constructors are allowed in there if (!$tokens[$nspIndex]->equals('{')) { // unless it's the global namespace, the index currently points to the name $nspIndex = $tokens->getNextTokenOfKind($nspIndex, array(';', '{')); if ($tokens[$nspIndex]->equals(';')) { // the class is inside a (non-block) namespace, no PHP4-code should be in there break; } // the index points to the { of a block-namespace $nspEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nspIndex); if ($index < $nspEnd) { // the class is inside a block namespace, skip other classes that might be in it for ($j = $i + 1; $j < $numClasses; ++$j) { if ($classes[$j] < $nspEnd) { ++$i; } } // and continue checking the classes that might follow continue; } } } $classNameIndex = $tokens->getNextMeaningfulToken($index); $className = $tokens[$classNameIndex]->getContent(); $classStart = $tokens->getNextTokenOfKind($classNameIndex, array('{')); $classEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $classStart); $this->fixConstructor($tokens, $className, $classStart, $classEnd); $this->fixParent($tokens, $classStart, $classEnd); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); // last and first tokens cannot be an operator for ($index = $tokens->count() - 2; $index >= 0; --$index) { if (!$tokensAnalyzer->isBinaryOperator($index)) { continue; } $isDeclare = $this->isDeclareStatement($tokens, $index); if (false !== $isDeclare) { $index = $isDeclare; // skip `declare(foo ==bar)`, see `declare_equal_normalize` } else { $this->fixWhiteSpaceAroundOperator($tokens, $index); } // previous of binary operator is now never an operator / previous of declare statement cannot be an operator --$index; } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; 0 <= $index; --$index) { $token = $tokens[$index]; if (!$token->isGivenKind(array(T_INC, T_DEC)) || !$tokensAnalyzer->isUnarySuccessorOperator($index)) { continue; } $nextToken = $tokens[$tokens->getNextMeaningfulToken($index)]; if (!$nextToken->equalsAny(array(';', ')'))) { continue; } $startIndex = $this->findStart($tokens, $index); $prevToken = $tokens[$tokens->getPrevMeaningfulToken($startIndex)]; if ($prevToken->equalsAny(array(';', '{', '}', array(T_OPEN_TAG)))) { $tokens->insertAt($startIndex, clone $token); $token->clear(); } } }
private function fixArray(Tokens $tokens, $index) { $tokensAnalyzer = new TokensAnalyzer($tokens); if ($tokensAnalyzer->isArrayMultiLine($index)) { return; } $startIndex = $index; if ($tokens[$startIndex]->isGivenKind(T_ARRAY)) { $startIndex = $tokens->getNextTokenOfKind($startIndex, array('(')); $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex); } else { $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $startIndex); } $beforeEndIndex = $tokens->getPrevMeaningfulToken($endIndex); $beforeEndToken = $tokens[$beforeEndIndex]; if ($beforeEndToken->equals(',')) { $tokens->removeTrailingWhitespace($beforeEndIndex); $beforeEndToken->clear(); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $foundNamespace = $tokens->findGivenKind(T_NAMESPACE); if (empty($foundNamespace)) { return; } $tokensAnalyzer = new TokensAnalyzer($tokens); $firstNamespaceIdx = key($foundNamespace); $usesIdxs = $tokensAnalyzer->getImportUseIndexes(); foreach ($usesIdxs as $idx) { if ($idx < $firstNamespaceIdx) { continue; } $nextTokenIdx = $tokens->getNextNonWhitespace($idx); $nextToken = $tokens[$nextTokenIdx]; if ($nextToken->isGivenKind(T_NS_SEPARATOR)) { $nextToken->clear(); } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $elements = $tokensAnalyzer->getClassyElements(); foreach (array_reverse($elements, true) as $index => $element) { if ('method' === $element['type']) { $this->overrideAttribs($tokens, $index, $this->grabAttribsBeforeMethodToken($tokens, $index)); // force whitespace between function keyword and function name to be single space char $afterToken = $tokens[++$index]; if ($afterToken->isWhitespace()) { $afterToken->setContent(' '); } } elseif ('property' === $element['type']) { $prevIndex = $tokens->getPrevTokenOfKind($index, array(';', ',', '{')); if (!$prevIndex || !$tokens[$prevIndex]->equals(',')) { $this->overrideAttribs($tokens, $index, $this->grabAttribsBeforePropertyToken($tokens, $index)); } } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $elements = $tokensAnalyzer->getClassyElements(); foreach (array_reverse($elements, true) as $index => $element) { if (!in_array($element['type'], $this->configuration, true)) { continue; } switch ($element['type']) { case 'method': $this->fixMethodVisibility($tokens, $index); break; case 'property': $this->fixPropertyVisibility($tokens, $index); break; case 'const': $this->fixConstVisibility($tokens, $index); break; } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $uses = array_reverse($tokensAnalyzer->getImportUseIndexes()); foreach ($uses as $index) { $endIndex = $tokens->getNextTokenOfKind($index, array(';', array(T_CLOSE_TAG))); $previous = $tokens->getPrevMeaningfulToken($endIndex); if ($tokens[$previous]->equals('}')) { $start = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $previous, false); $declarationContent = $tokens->generatePartialCode($start + 1, $previous - 1); $prefix = ''; for ($i = $index + 1; $i < $start; ++$i) { $prefix .= $tokens[$i]->getContent(); } $prefix = ' ' . ltrim($prefix); } else { $declarationContent = $tokens->generatePartialCode($index + 1, $endIndex - 1); $prefix = ' '; } $declarationParts = explode(',', $declarationContent); if (1 === count($declarationParts)) { continue; } $declarationContent = array(); foreach ($declarationParts as $declarationPart) { $declarationContent[] = 'use' . $prefix . trim($declarationPart) . ';'; } $declarationContent = implode("\n" . $this->detectIndent($tokens, $index), $declarationContent); for ($i = $index; $i < $endIndex; ++$i) { $tokens[$i]->clear(); } if ($tokens[$endIndex]->equals(';')) { $tokens[$endIndex]->clear(); } $declarationTokens = Tokens::fromCode('<?php ' . $declarationContent); $declarationTokens[0]->clear(); $declarationTokens->clearEmptyTokens(); $tokens->insertAt($index, $declarationTokens); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; $index >= 0; --$index) { if (!$tokensAnalyzer->isBinaryOperator($index)) { continue; } // skip `declare(foo ==bar)` $prevMeaningfulIndex = $tokens->getPrevMeaningfulToken($index); if ($tokens[$prevMeaningfulIndex]->isGivenKind(T_STRING)) { $prevMeaningfulIndex = $tokens->getPrevMeaningfulToken($prevMeaningfulIndex); if ($tokens[$prevMeaningfulIndex]->equals('(')) { $prevMeaningfulIndex = $tokens->getPrevMeaningfulToken($prevMeaningfulIndex); if ($tokens[$prevMeaningfulIndex]->isGivenKind(T_DECLARE)) { continue; } } } // fix white space after operator if ($tokens[$index + 1]->isWhitespace()) { $content = $tokens[$index + 1]->getContent(); if (' ' !== $content && false === strpos($content, "\n") && !$tokens[$tokens->getNextNonWhitespace($index + 1)]->isComment()) { $tokens[$index + 1]->setContent(' '); } } else { $tokens->insertAt($index + 1, new Token(array(T_WHITESPACE, ' '))); } // fix white space before operator if ($tokens[$index - 1]->isWhitespace()) { $content = $tokens[$index - 1]->getContent(); if (' ' !== $content && false === strpos($content, "\n") && !$tokens[$tokens->getPrevNonWhitespace($index - 1)]->isComment()) { $tokens[$index - 1]->setContent(' '); } } else { $tokens->insertAt($index, new Token(array(T_WHITESPACE, ' '))); } --$index; // skip check for binary operator on the whitespace token that is fixed. } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); $namespacesImports = $tokensAnalyzer->getImportUseIndexes(true); if (0 === count($namespacesImports)) { return; } $usesOrder = array(); foreach ($namespacesImports as $uses) { $usesOrder = array_replace($usesOrder, $this->getNewOrder(array_reverse($uses), $tokens)); } $usesOrder = array_reverse($usesOrder, true); $mapStartToEnd = array(); foreach ($usesOrder as $use) { $mapStartToEnd[$use['startIndex']] = $use['endIndex']; } // Now insert the new tokens, starting from the end foreach ($usesOrder as $index => $use) { $declarationTokens = Tokens::fromCode('<?php use ' . $use['namespace'] . ';'); $declarationTokens->clearRange(0, 2); // clear `<?php use ` $declarationTokens[count($declarationTokens) - 1]->clear(); // clear `;` $declarationTokens->clearEmptyTokens(); $tokens->overrideRange($index, $mapStartToEnd[$index], $declarationTokens); if ($use['group']) { // a group import must start with `use` and cannot be part of comma separated import list $prev = $tokens->getPrevMeaningfulToken($index); if ($tokens[$prev]->equals(',')) { $tokens[$prev]->setContent(';'); $tokens->insertAt($prev + 1, new Token(array(T_USE, 'use'))); if (!$tokens[$prev + 2]->isWhitespace()) { $tokens->insertAt($prev + 2, new Token(array(T_WHITESPACE, ' '))); } } } } }
/** * @param Tokens $tokens */ private function storeImports(Tokens $tokens, $startIndex, $endIndex) { $tokensAnalyzer = new TokensAnalyzer($tokens); $this->imports = array(); foreach ($tokensAnalyzer->getImportUseIndexes() as $index) { if ($index < $startIndex || $index > $endIndex) { continue; } $import = ''; while ($index = $tokens->getNextMeaningfulToken($index)) { if ($tokens[$index]->equalsAny(array(';', array(CT::T_GROUP_IMPORT_BRACE_OPEN))) || $tokens[$index]->isGivenKind(T_AS)) { break; } $import .= $tokens[$index]->getContent(); } // Imports group (PHP 7 spec) if ($tokens[$index]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) { $groupEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_GROUP_IMPORT_BRACE, $index); $groupImports = array_map('trim', explode(',', $tokens->generatePartialCode($index + 1, $groupEndIndex - 1))); foreach ($groupImports as $groupImport) { $groupImportParts = array_map('trim', explode(' as ', $groupImport)); if (2 === count($groupImportParts)) { $this->imports[$groupImportParts[1]] = $import . $groupImportParts[0]; } else { $this->imports[] = $import . $groupImport; } } } elseif ($tokens[$index]->isGivenKind(T_AS)) { $aliasIndex = $tokens->getNextMeaningfulToken($index); $alias = $tokens[$aliasIndex]->getContent(); $this->imports[$alias] = $import; } else { $this->imports[] = $import; } } }
private function fixAfterToken($index) { for ($i = $index - 1; $i > 0; --$i) { if ($this->tokens[$i]->isGivenKind(T_FUNCTION) && $this->tokensAnalyzer->isLambda($i)) { return; } if ($this->tokens[$i]->isGivenKind(T_CLASS) && $this->tokensAnalyzer->isAnonymousClass($i)) { return; } if ($this->tokens[$i]->isWhitespace() && false !== strpos($this->tokens[$i]->getContent(), "\n")) { break; } } $this->removeEmptyLinesAfterLineWithTokenAt($index); }
private function fixArray(Tokens $tokens, $index) { $tokensAnalyzer = new TokensAnalyzer($tokens); if (!$tokensAnalyzer->isArrayMultiLine($index)) { return; } $startIndex = $index; if ($tokens[$startIndex]->isGivenKind(T_ARRAY)) { $startIndex = $tokens->getNextTokenOfKind($startIndex, array('(')); $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex); } else { $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $startIndex); } $beforeEndIndex = $tokens->getPrevMeaningfulToken($endIndex); $beforeEndToken = $tokens[$beforeEndIndex]; // if there is some item between braces then add `,` after it if ($startIndex !== $beforeEndIndex && !$beforeEndToken->equalsAny(array(',', array(T_END_HEREDOC)))) { $tokens->insertAt($beforeEndIndex + 1, new Token(',')); $endToken = $tokens[$endIndex]; if (!$endToken->isComment() && !$endToken->isWhitespace()) { $tokens->ensureWhitespaceAtIndex($endIndex, 1, ' '); } } }
private function fixLambdas(Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; 0 <= $index; --$index) { $token = $tokens[$index]; if (!$token->isGivenKind(T_FUNCTION) || !$tokensAnalyzer->isLambda($index)) { continue; } $nextIndex = $tokens->getNextTokenOfKind($index, array('{')); $tokens->ensureWhitespaceAtIndex($nextIndex - 1, 1, ' '); } }
/** * @param Tokens $tokens * @param int $index */ private function fixElement(Tokens $tokens, $index) { $tokensAnalyzer = new TokensAnalyzer($tokens); $repeatIndex = $index; while (true) { $repeatIndex = $tokens->getNextMeaningfulToken($repeatIndex); $repeatToken = $tokens[$repeatIndex]; if ($tokensAnalyzer->isArray($repeatIndex)) { if ($repeatToken->isGivenKind(T_ARRAY)) { $repeatIndex = $tokens->getNextTokenOfKind($repeatIndex, array('(')); $repeatIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $repeatIndex); } else { $repeatIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $repeatIndex); } continue; } if ($repeatToken->equals(';')) { return; // no repeating found, no fixing needed } if ($repeatToken->equals(',')) { break; } } $start = $tokens->getPrevTokenOfKind($index, array(';', '{', '}')); $this->expandElement($tokens, $tokens->getNextMeaningfulToken($start), $tokens->getNextTokenOfKind($index, array(';'))); }
private function fixIndents(Tokens $tokens) { $classyTokens = Token::getClassyTokenKinds(); $classyAndFunctionTokens = array_merge(array(T_FUNCTION), $classyTokens); $controlTokens = $this->getControlTokens(); $indentTokens = array_filter(array_merge($classyAndFunctionTokens, $controlTokens), function ($item) { return T_SWITCH !== $item; }); $tokensAnalyzer = new TokensAnalyzer($tokens); for ($index = 0, $limit = count($tokens); $index < $limit; ++$index) { $token = $tokens[$index]; // if token is not a structure element - continue if (!$token->isGivenKind($indentTokens)) { continue; } // do not change indent for `while` in `do ... while ...` if ($token->isGivenKind(T_WHILE) && $tokensAnalyzer->isWhilePartOfDoWhile($index)) { continue; } // do not change import of functions if ($token->isGivenKind(T_FUNCTION) && $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_USE)) { continue; } if ($token->isGivenKind($classyAndFunctionTokens)) { $startBraceIndex = $tokens->getNextTokenOfKind($index, array(';', '{')); $startBraceToken = $tokens[$startBraceIndex]; } else { $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index); $startBraceIndex = $tokens->getNextNonWhitespace($parenthesisEndIndex); $startBraceToken = $tokens[$startBraceIndex]; } // structure without braces block - nothing to do, e.g. do { } while (true); if (!$startBraceToken->equals('{')) { continue; } $endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startBraceIndex); $indent = $this->detectIndent($tokens, $index); // fix indent near closing brace $tokens->ensureWhitespaceAtIndex($endBraceIndex - 1, 1, "\n" . $indent); // fix indent between braces $lastCommaIndex = $tokens->getPrevTokenOfKind($endBraceIndex - 1, array(';', '}')); $nestLevel = 1; for ($nestIndex = $lastCommaIndex; $nestIndex >= $startBraceIndex; --$nestIndex) { $nestToken = $tokens[$nestIndex]; if ($nestToken->equals(')')) { $nestIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nestIndex, false); continue; } if (1 === $nestLevel && $nestToken->equalsAny(array(';', '}'))) { $nextNonWhitespaceNestIndex = $tokens->getNextNonWhitespace($nestIndex); $nextNonWhitespaceNestToken = $tokens[$nextNonWhitespaceNestIndex]; if (!$nextNonWhitespaceNestToken->isComment() && !($nestToken->equals('}') && $nextNonWhitespaceNestToken->equalsAny(array(';', ',', ']', array(CT_ARRAY_SQUARE_BRACE_CLOSE)))) && !($nestToken->equals('}') && $nextNonWhitespaceNestToken->equals('(')) && !($nestToken->equals('}') && $tokens[$nestIndex - 1]->equalsAny(array('"', "'", array(T_CONSTANT_ENCAPSED_STRING))))) { if ($nextNonWhitespaceNestToken->isGivenKind($this->getControlContinuationTokens()) || $nextNonWhitespaceNestToken->isGivenKind(T_CLOSE_TAG) || $nextNonWhitespaceNestToken->isGivenKind(T_WHILE) && $tokensAnalyzer->isWhilePartOfDoWhile($nextNonWhitespaceNestIndex)) { $whitespace = ' '; } else { $nextToken = $tokens[$nestIndex + 1]; $nextWhitespace = ''; if ($nextToken->isWhitespace()) { $nextWhitespace = rtrim($nextToken->getContent(), " \t"); if (strlen($nextWhitespace) && "\n" === $nextWhitespace[strlen($nextWhitespace) - 1]) { $nextWhitespace = substr($nextWhitespace, 0, -1); } } $whitespace = $nextWhitespace . "\n" . $indent; if (!$nextNonWhitespaceNestToken->equals('}')) { $whitespace .= ' '; } } $tokens->ensureWhitespaceAtIndex($nestIndex + 1, 0, $whitespace); } } if ($nestToken->equals('}')) { ++$nestLevel; continue; } if ($nestToken->equals('{')) { --$nestLevel; continue; } } // fix indent near opening brace if (isset($tokens[$startBraceIndex + 2]) && $tokens[$startBraceIndex + 2]->equals('}')) { $tokens->ensureWhitespaceAtIndex($startBraceIndex + 1, 0, "\n" . $indent); } else { $nextToken = $tokens[$startBraceIndex + 1]; $nextNonWhitespaceToken = $tokens[$tokens->getNextNonWhitespace($startBraceIndex)]; // set indent only if it is not a case, when comment is following { in same line if (!$nextNonWhitespaceToken->isComment() || !($nextToken->isWhitespace() && $nextToken->isWhitespace(" \t")) && substr_count($nextToken->getContent(), "\n") === 1) { $tokens->ensureWhitespaceAtIndex($startBraceIndex + 1, 0, "\n" . $indent . ' '); } } if ($token->isGivenKind($classyTokens) && !$tokensAnalyzer->isAnonymousClass($index)) { $tokens->ensureWhitespaceAtIndex($startBraceIndex - 1, 1, "\n" . $indent); } elseif ($token->isGivenKind(T_FUNCTION) && !$tokensAnalyzer->isLambda($index)) { $closingParenthesisIndex = $tokens->getPrevTokenOfKind($startBraceIndex, array(')')); if (null === $closingParenthesisIndex) { continue; } $prevToken = $tokens[$closingParenthesisIndex - 1]; if ($prevToken->isWhitespace() && false !== strpos($prevToken->getContent(), "\n")) { if (!$tokens[$startBraceIndex - 2]->isComment()) { $tokens->ensureWhitespaceAtIndex($startBraceIndex - 1, 1, ' '); } } else { $tokens->ensureWhitespaceAtIndex($startBraceIndex - 1, 1, "\n" . $indent); } } else { $tokens->ensureWhitespaceAtIndex($startBraceIndex - 1, 1, ' '); } // reset loop limit due to collection change $limit = count($tokens); } }
/** * @param Tokens $tokens * @param int $classyIndex * * @return array */ private function getClassyDefinitionInfo(Tokens $tokens, $classyIndex) { $openIndex = $tokens->getNextTokenOfKind($classyIndex, array('{')); $prev = $tokens->getPrevMeaningfulToken($classyIndex); $startIndex = $tokens[$prev]->isGivenKind(array(T_FINAL, T_ABSTRACT)) ? $prev : $classyIndex; $extends = false; $implements = false; $anonymousClass = false; if (!(defined('T_TRAIT') && $tokens[$classyIndex]->isGivenKind(T_TRAIT))) { $extends = $tokens->findGivenKind(T_EXTENDS, $classyIndex, $openIndex); $extends = count($extends) ? $this->getClassyInheritanceInfo($tokens, key($extends), $openIndex, 'numberOfExtends') : false; if (!$tokens[$classyIndex]->isGivenKind(T_INTERFACE)) { $implements = $tokens->findGivenKind(T_IMPLEMENTS, $classyIndex, $openIndex); $implements = count($implements) ? $this->getClassyInheritanceInfo($tokens, key($implements), $openIndex, 'numberOfImplements') : false; $tokensAnalyzer = new TokensAnalyzer($tokens); $anonymousClass = $tokensAnalyzer->isAnonymousClass($classyIndex); } } return array('start' => $startIndex, 'classy' => $classyIndex, 'open' => $openIndex, 'extends' => $extends, 'implements' => $implements, 'anonymousClass' => $anonymousClass); }