/** * {@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); 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))); } } }
/** * {@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); } }
/** * 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); $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 check(\SplFileInfo $file, Tokens $tokens) { $analyzer = new TokensAnalyzer($tokens); $constructorIndex = null; $constructorArgs = []; $properties = []; // Find the class $classIndex = $tokens->getNextTokenOfKind(0, [[T_CLASS]]); if ($classIndex === null) { return; } $classAttributes = TokensHelper::getClassAttributes($tokens, $classIndex); // Event must be final or abstract if (!$classAttributes['abstract'] && !$classAttributes['final']) { $tokens->reportAt($classIndex, new Message(E_ERROR, 'check_class_must_be_final_or_abstract', ['class' => $classAttributes['name']])); } $elements = $analyzer->getClassyElements(); foreach ($elements as $index => $element) { /** @var Token $token */ if ($element['type'] === 'method') { $methodAttributes = TokensHelper::getMethodAttributes($tokens, $index, $analyzer); if ($methodInfo = $this->checkMethod($tokens, $index, $methodAttributes)) { $constructorIndex = $index; $constructorArgs = $methodInfo['arguments']; } } elseif ($element['type'] === 'property' && ($propertyInfo = $this->checkProperty($tokens, $index))) { $properties[$index] = $propertyInfo; } } // A event without data is not a error if ($constructorIndex === null && count($properties) === 0) { return; } // Event with properties must have a constructor if ($constructorIndex === null) { $tokens->reportAt($classIndex, new Message(E_ERROR, 'check_class_must_have_constructor', ['class' => $classAttributes['name']])); } else { $expectedProperties = array_map(function ($info) { return $info['token']->getContent(); }, $constructorArgs); $unknownConstructorArgs = array_diff($expectedProperties, $properties); if (count($unknownConstructorArgs) > 0) { $tokens->reportAt($constructorIndex, new Message(E_ERROR, 'check_class_constructor_has_arguments_without_related_property', ['arguments' => implode(', ', $unknownConstructorArgs)])); } $unassignedProperties = array_diff($properties, $expectedProperties); if (count($unassignedProperties) > 0) { foreach ($unassignedProperties as $index => $property) { $tokens->reportAt($index, new Message(E_ERROR, 'check_class_property_must_be_know_as_constructor_argument', ['property' => $property])); } } } }
/** * {@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); $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) { $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); 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(); } }
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, ' '); } } }
/** * @dataProvider provideGetFunctionProperties */ public function testGetFunctionProperties($source, $index, $expected) { $tokens = Tokens::fromCode($source); $tokensAnalyzer = new TokensAnalyzer($tokens); $attributes = $tokensAnalyzer->getMethodAttributes($index); $this->assertSame($expected, $attributes); }
/** * @expectedException \InvalidArgumentException * @dataProvider provideArrayExceptions */ public function testIsMultiLineArrayException($source, $tokenIndex) { $tokens = Tokens::fromCode($source); $tokensAnalyzer = new TokensAnalyzer($tokens); $tokensAnalyzer->isArrayMultiLine($tokenIndex); }
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, ' '); } }
/** * @dataProvider provideIsBinaryOperator70 * @requires PHP 7.0 */ public function testIsBinaryOperator70($source, array $expected) { $tokensAnalyzer = new TokensAnalyzer(Tokens::fromCode($source)); foreach ($expected as $index => $expected) { $this->assertSame($expected, $tokensAnalyzer->isBinaryOperator($index)); if ($expected) { $this->assertFalse($tokensAnalyzer->isUnarySuccessorOperator($index)); $this->assertFalse($tokensAnalyzer->isUnaryPredecessorOperator($index)); } } }