private function fixArray(Tokens $tokens, &$index) { $tokens[$index]->clear(); $bracesLevel = 0; ++$index; for ($c = $tokens->count(); $index < $c; ++$index) { $token = $tokens[$index]; if ('(' === $token->content) { if (0 === $bracesLevel) { $tokens[$index]->content = '['; } ++$bracesLevel; continue; } if ($token->isGivenKind(T_ARRAY) && '(' === $tokens->getNextNonWhitespace($index)->content) { $this->fixArray($tokens, $index); continue; } if (')' === $token->content) { --$bracesLevel; if (0 === $bracesLevel) { $tokens[$index]->content = ']'; break; } } } }
private function getNamespaceUseDeclarations(Tokens $tokens, array $useIndexes) { $uses = array(); foreach ($useIndexes as $index) { $declarationEndIndex = null; $tokens->getNextTokenOfKind($index, array(';'), $declarationEndIndex); $declarationContent = $tokens->generatePartialCode($index + 1, $declarationEndIndex - 1); // ignore multiple use statements like: `use BarB, BarC as C, BarD;` // that should be split into few separate statements if (false !== strpos($declarationContent, ',')) { continue; } $declarationParts = preg_split('/\\s+as\\s+/i', $declarationContent); if (1 === count($declarationParts)) { $fullName = $declarationContent; $declarationParts = explode('\\', $fullName); $shortName = end($declarationParts); $aliased = false; } else { $fullName = $declarationParts[0]; $shortName = $declarationParts[1]; $declarationParts = explode('\\', $fullName); $aliased = $shortName !== end($declarationParts); } $shortName = trim($shortName); $uses[$shortName] = array('shortName' => $shortName, 'fullName' => trim($fullName), 'aliased' => $aliased, 'declarationStart' => $index, 'declarationEnd' => $declarationEndIndex); } return $uses; }
private function fixArray(Tokens $tokens, &$index) { $bracesLevel = 0; // Skip only when its an array, for short arrays we need the brace for correct // level counting if ($tokens[$index]->isGivenKind(T_ARRAY)) { ++$index; } $multiline = $tokens->isArrayMultiLine($index); for ($c = $tokens->count(); $index < $c; ++$index) { $token = $tokens[$index]; if ('(' === $token->content || '[' === $token->content) { ++$bracesLevel; continue; } if ($token->isGivenKind(T_ARRAY) || $tokens->isShortArray($index)) { $this->fixArray($tokens, $index); continue; } if (')' === $token->content || ']' === $token->content) { --$bracesLevel; $foundIndex = null; if (!$multiline && 0 === $bracesLevel && ',' === $tokens->getPrevNonWhitespace($index, array(), $foundIndex)->content) { $tokens->removeTrailingWhitespace($foundIndex); $tokens[$foundIndex]->clear(); } if (0 === $bracesLevel) { break; } } } }
private function ensureWhitespaceExistance(Tokens $tokens, $index, $after) { $indexChange = $after ? 0 : 1; $token = $tokens[$index]; if ($token->isWhitespace()) { return; } $tokens->insertAt($index + $indexChange, new Token(array(T_WHITESPACE, ' ', $token->line))); }
private function clearIncludies(Tokens $tokens, array $includies) { foreach (array_reverse($includies) as $includy) { if ($includy['end']) { $tokens->removeLeadingWhitespace($includy['end']); } $braces = $includy['braces']; if ($braces) { $nextToken = $tokens->getNextNonWhitespace($includy['braces']['close']); if (!$nextToken->isArray() && ';' === $nextToken->content) { $tokens->removeLeadingWhitespace($braces['open']); $tokens->removeTrailingWhitespace($braces['open']); $tokens->removeLeadingWhitespace($braces['close']); $tokens->removeTrailingWhitespace($braces['close']); $tokens[$braces['open']] = new Token(array(T_WHITESPACE, ' ')); $tokens[$braces['close']]->clear(); } } $nextIndex = $includy['begin'] + 1; $nextToken = $tokens[$nextIndex]; while ($nextToken->isEmpty()) { $nextToken = $tokens[++$nextIndex]; } if ($nextToken->isWhitespace()) { $nextToken->content = ' '; } elseif ($braces) { $tokens->insertAt($includy['begin'] + 1, new Token(array(T_WHITESPACE, ' '))); } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); for ($index = $tokens->count() - 1; $index >= 0; --$index) { $token = $tokens[$index]; if (T_NEW !== $token->id) { continue; } $nextIndex = null; $nextToken = $tokens->getNextTokenOfKind($index, array(';', ',', '(', ')', '[', ']'), $nextIndex); // no correct end of code - break if (null === $nextToken) { break; } // entrance into array index syntax - need to look for exit if (!$nextToken->isArray() && '[' === $nextToken->content) { $braceLevel = 1; while (0 < $braceLevel) { $nextToken = $tokens->getNextTokenOfKind($nextIndex, array('[', ']'), $nextIndex); $braceLevel += '[' === $nextToken->content ? 1 : -1; } $nextToken = $tokens[++$nextIndex]; } // new statement with () - nothing to do if (!$nextToken->isArray() && '(' === $nextToken->content) { continue; } $meaningBeforeNextIndex = null; $tokens->getPrevNonWhitespace($nextIndex, array(), $meaningBeforeNextIndex); $tokens->insertAt($meaningBeforeNextIndex + 1, array(new Token('('), new Token(')'))); } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); foreach ($tokens as $token) { if (!$token->isGivenKind(T_WHITESPACE)) { continue; } $content = ''; $count = 0; $parts = explode("\n", $token->content); for ($i = 0, $last = count($parts) - 1; $i <= $last; ++$i) { if ('' === $parts[$i]) { // if part is empty then we between two \n ++$count; } else { $count = 0; $content .= $parts[$i]; } if ($i !== $last && $count < 3) { $content .= "\n"; } } $token->content = $content; } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); $uses = array_reverse($tokens->getNamespaceUseIndexes()); foreach ($uses as $index) { $endIndex = null; $tokens->getNextTokenOfKind($index, array(';'), $endIndex); $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(); $tokens->insertAt($index, $declarationTokens); } return $tokens->generateCode(); }
/** * Replace all `else if` (T_ELSE T_IF) with `elseif` (T_ELSEIF) * * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); foreach ($tokens as $index => $token) { // if token is not T_ELSE - continue searching, this is not right token to fix if (!$token->isGivenKind(T_ELSE)) { continue; } $nextIndex = null; $nextToken = $tokens->getNextNonWhitespace($index, array(), $nextIndex); // if next meaning token is not T_IF - continue searching, this is not the case for fixing if (!$nextToken->isGivenKind(T_IF)) { continue; } // now we have T_ELSE following by T_IF so we could fix this // 1. clear whitespaces between T_ELSE and T_IF $tokens[$index + 1]->clear(); // 2. change token from T_ELSE into T_ELSEIF $token->content = 'elseif'; $token->id = T_ELSEIF; // 3. clear succeeding T_IF $nextToken->clear(); } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) { $token = $tokens[$index]; if (!$token->isGivenKind(T_RETURN)) { continue; } $prevNonWhitespaceToken = $tokens->getPrevNonWhitespace($index); if (!$prevNonWhitespaceToken->equalsAny(array(';', '}'))) { continue; } $prevToken = $tokens[$index - 1]; if ($prevToken->isWhitespace()) { $parts = explode("\n", $prevToken->content); $countParts = count($parts); if (1 === $countParts) { $prevToken->content = rtrim($prevToken->content, " \t") . "\n\n"; } elseif (count($parts) <= 2) { $prevToken->content = "\n" . $prevToken->content; } } else { $tokens->insertAt($index, new Token(array(T_WHITESPACE, "\n\n"))); ++$index; ++$limit; } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); foreach ($tokens as $index => $token) { if ($token->isGivenKind(T_IS_NOT_EQUAL)) { $tokens[$index]->content = '!='; } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); foreach ($tokens as $token) { if ($token->isKeyword()) { $token->content = strtolower($token->content); } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); foreach ($tokens as $index => $token) { if (!$token->isWhitespace()) { continue; } $tokens[$index]->content = preg_replace('/(?:(?<! ) {1,3})?\\t/', ' ', $token->content); } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); foreach ($tokens as $index => $token) { if (!$token->isGivenKind(T_DOC_COMMENT)) { continue; } $tokens[$index]->content = $this->fixDocBlock($token->content); } return $tokens->generateCode(); }
public function testReadFromCacheAfterClearing() { $code = '<?php echo 1;'; $tokens = Tokens::fromCode($code); $countBefore = $tokens->count(); for ($i = 0; $i < $countBefore; ++$i) { $tokens[$i]->clear(); } $tokens = Tokens::fromCode($code); $this->assertSame($countBefore, $tokens->count()); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); $whitespaces = array('whitespaces' => " \t"); foreach ($tokens as $index => $token) { if (!$token->isArray() && '.' === $token->content) { $tokens->removeLeadingWhitespace($index, $whitespaces); $tokens->removeTrailingWhitespace($index, $whitespaces); } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); foreach ($tokens as $index => $token) { if ($token->isNativeConstant()) { if ($tokens->getPrevNonWhitespace($index, array('whitespaces' => " \t\n"))->isArray() || $tokens->getNextNonWhitespace($index, array('whitespaces' => " \t\n"))->isArray()) { continue; } $token->content = strtolower($token->content); } } return $tokens->generateCode(); }
private function getNewOrder(array $uses, Tokens $tokens) { $uses = array_reverse($uses); $indexes = array(); $originalIndexes = array(); foreach ($uses as $index) { $tokens->getNextTokenOfKind($index, array(';'), $endIndex); $tokens->getTokenNotOfKindSibling($index + 1, 1, array(array(T_WHITESPACE)), $startIndex); $namespace = ''; $index = $startIndex; while ($index <= $endIndex) { $token = $tokens[$index]; /** @var Token $token */ if (',' === $token->content || $index === $endIndex) { $indexes[$startIndex] = array($namespace, $startIndex, $index - 1); $originalIndexes[] = $startIndex; $namespace = ''; $nextPartIndex = $endIndex; $tokens->getTokenNotOfKindSibling($index, 1, array(array(','), array(T_WHITESPACE)), $nextPartIndex); $startIndex = $nextPartIndex; $index = $nextPartIndex; continue; } $namespace .= $token->content; ++$index; } } uasort($indexes, 'self::sortingCallBack'); $i = -1; $usesOrder = array(); // Loop trough the index but use original index order foreach ($indexes as $v) { $usesOrder[$originalIndexes[++$i]] = $v; } return $usesOrder; }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); for ($index = $tokens->count() - 1; $index >= 0; --$index) { $token = $tokens[$index]; if (!$token->isArray() && '.' === $token->content) { 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, ' '))); } } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { // replace all <? with <?php to replace all short open tags even without short_open_tag option enabled $newContent = preg_replace('/<\\?(\\s|$)/', '<?php$1', $content); /* the following code is magic to revert previous replacements which should NOT be replaced, for example incorrectly replacing * > echo '<? '; * with * > echo '<?php '; */ $tokens = Tokens::fromCode($newContent); $tokensOldContent = ''; $tokensOldContentLength = 0; foreach ($tokens as $token) { if ($token->isGivenKind(T_OPEN_TAG)) { $tokenContent = $token->content; if ('<?php' !== substr($content, $tokensOldContentLength, 5)) { $tokenContent = '<? '; } $tokensOldContent .= $tokenContent; $tokensOldContentLength += strlen($tokenContent); continue; } if ($token->isGivenKind(array(T_COMMENT, T_DOC_COMMENT, T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE, T_STRING))) { $tokenContent = ''; $tokenContentLength = 0; $parts = explode('<?php ', $token->content); $iLast = count($parts) - 1; foreach ($parts as $i => $part) { $tokenContent .= $part; $tokenContentLength += strlen($part); if ($i !== $iLast) { if ('<?php' === substr($content, $tokensOldContentLength + $tokenContentLength, 5)) { $tokenContent .= '<?php '; $tokenContentLength += 6; } else { $tokenContent .= '<? '; $tokenContentLength += 3; } } } $token->content = $tokenContent; } $tokensOldContent .= $token->content; $tokensOldContentLength += strlen($token->content); } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); for ($index = $tokens->count() - 1; $index >= 0; --$index) { $token = $tokens[$index]; if (!$token->isGivenKind(T_FUNCTION)) { continue; } $startParenthesisIndex = null; $tokens->getNextTokenOfKind($index, array('('), $startParenthesisIndex); $endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex); $startBraceIndex = null; $startBraceToken = $tokens->getNextTokenOfKind($endParenthesisIndex, array(';', '{'), $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 = null; $afterParenthesisToken = $tokens->getNextNonWhitespace($endParenthesisIndex, array(), $afterParenthesisIndex); if ($afterParenthesisToken->isGivenKind(T_USE)) { $useStartParenthesisIndex = null; $tokens->getNextTokenOfKind($afterParenthesisIndex, array('('), $useStartParenthesisIndex); $useEndParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $useStartParenthesisIndex); // fix whitespace after T_USE $tokens->ensureWhitespaceAtIndex($afterParenthesisIndex + 1, 0, ' '); // remove single-line edge whitespaces inside use parentheses $this->fixParenthesisInnerEdge($tokens, $useStartParenthesisIndex, $useEndParenthesisIndex); // fix whitespace before T_USE $tokens->ensureWhitespaceAtIndex($afterParenthesisIndex - 1, 1, ' '); } // remove single-line edge whitespaces inside parameters list parentheses $this->fixParenthesisInnerEdge($tokens, $startParenthesisIndex, $endParenthesisIndex); // 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, ' '); } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); $elements = $tokens->getClassyElements(); foreach (array_reverse($elements, true) as $index => $element) { if ('method' === $element['type']) { $tokens->applyAttribs($index, $tokens->grabAttribsBeforeMethodToken($index)); // force whitespace between function keyword and function name to be single space char $tokens[++$index]->content = ' '; } elseif ('property' === $element['type']) { $prevToken = $tokens->getPrevTokenOfKind($index, array(';', ',')); $nextToken = $tokens->getNextTokenOfKind($index, array(';', ',')); if ((!$prevToken || ',' !== $prevToken->content) && (!$nextToken || ',' !== $nextToken->content)) { $tokens->applyAttribs($index, $tokens->grabAttribsBeforePropertyToken($index)); } } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { static $insideCastSpaceReplaceMap = array(' ' => '', "\t" => '', "\n" => ''); $tokens = Tokens::fromCode($content); foreach ($tokens as $index => $token) { if ($token->isCast()) { $token->content = strtr($token->content, $insideCastSpaceReplaceMap); // force single whitespace after cast token: if ($tokens[$index + 1]->isWhitespace(array('whitespaces' => " \t"))) { // - if next token is whitespaces that contains only spaces and tabs - override next token with single space $tokens[$index + 1]->content = ' '; } elseif (!$tokens[$index + 1]->isWhitespace()) { // - if next token is not whitespaces that contains spaces, tabs and new lines - append single space to current token $tokens->insertAt($index + 1, new Token(' ')); } } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { // [Structure] there should not be space before or after T_OBJECT_OPERATOR $tokens = Tokens::fromCode($content); foreach ($tokens as $index => $token) { // skip if $token is not -> if (!$token->isGivenKind(T_OBJECT_OPERATOR)) { continue; } // clear whitespace before -> if ($tokens[$index - 1]->isWhitespace(array('whitespaces' => " \t")) && !$tokens[$index - 2]->isComment()) { $tokens[$index - 1]->clear(); } // clear whitespace after -> if ($tokens[$index + 1]->isWhitespace(array('whitespaces' => " \t")) && !$tokens[$index + 2]->isComment()) { $tokens[$index + 1]->clear(); } } return $tokens->generateCode(); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); for ($index = $tokens->count() - 1; $index >= 0; --$index) { $token = $tokens[$index]; if (T_NAMESPACE === $token->id) { $semicolonIndex = null; $semicolonToken = $tokens->getNextTokenOfKind($index, array(';', '{'), $semicolonIndex); if (!$semicolonToken || ';' !== $semicolonToken->content || !isset($tokens[$semicolonIndex + 1])) { continue; } $nextToken = $tokens[$semicolonIndex + 1]; if (!$nextToken->isWhitespace()) { $tokens->insertAt($semicolonIndex + 1, new Token(array(T_WHITESPACE, "\n\n"))); } else { $nextToken->content = "\n\n" . ltrim($nextToken->content); } } } return $tokens->generateCode(); }
private function fixArray(Tokens $tokens, $index) { $bracesLevel = 0; $startIndex = $index; if ($tokens[$index]->isGivenKind(T_ARRAY)) { $tokens->getNextTokenOfKind($index, array('(', '['), $startIndex); } if (!$tokens->isArrayMultiLine($index)) { return; } if ($tokens[$startIndex]->equals('(')) { $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startIndex); } else { $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_SQUARE_BRACE, $startIndex); } $beforeEndIndex = null; $beforeEndToken = $tokens->getTokenNotOfKindSibling($endIndex, -1, array(array(T_WHITESPACE), array(T_COMMENT), array(T_DOC_COMMENT)), $beforeEndIndex); // if there is some item between braces then add `,` after it if ($startIndex !== $beforeEndIndex && !$beforeEndToken->equals(',')) { $tokens->insertAt($beforeEndIndex + 1, new Token(',')); } }
public function fixFile(\SplFileInfo $file, array $fixers, $dryRun, $diff) { if ($this->stopwatch) { $this->stopwatch->start($this->getFileRelativePathname($file)); } $new = $old = file_get_contents($file->getRealpath()); $appliedFixers = array(); // we do not need Tokens to still caching previously fixed file - so clear the cache Tokens::clearCache(); foreach ($fixers as $fixer) { if (!$fixer->supports($file)) { continue; } $newest = $fixer->fix($file, $new); if ($newest !== $new) { $appliedFixers[] = $fixer->getName(); } $new = $newest; } $fixInfo = null; if ($new !== $old) { if (!$dryRun) { file_put_contents($file->getRealpath(), $new); } $fixInfo = array('appliedFixers' => $appliedFixers); if ($diff) { $fixInfo['diff'] = $this->stringDiff($old, $new); } } if ($this->stopwatch) { $this->stopwatch->stop($this->getFileRelativePathname($file)); } return $fixInfo; }
private function findStatementEnd(Tokens $tokens, $parenthesisEndIndex) { $nextIndex = null; $nextToken = $tokens->getNextNonWhitespace($parenthesisEndIndex, array(), $nextIndex); if (!$nextToken) { return $parenthesisEndIndex; } if ($nextToken->equals('{')) { return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex); } if ($nextToken->isGivenKind($this->getControlTokens())) { $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex); $endIndex = $this->findStatementEnd($tokens, $parenthesisEndIndex); if ($nextToken->isGivenKind(T_IF)) { $nextIndex = null; $nextToken = $tokens->getNextNonWhitespace($endIndex, array(), $nextIndex); if ($nextToken && $nextToken->isGivenKind($this->getControlContinuationTokens())) { $parenthesisEndIndex = $this->findParenthesisEnd($tokens, $nextIndex); return $this->findStatementEnd($tokens, $parenthesisEndIndex); } } return $endIndex; } $index = $parenthesisEndIndex; while (true) { $token = $tokens[++$index]; if (';' === $token->content) { break; } } return $index; }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, $content) { $tokens = Tokens::fromCode($content); $this->fixComparisons($tokens); return $tokens->generateCode(); }