Detect type of block.
public static detectBlockType ( PhpCsFixer\Tokenizer\Token $token ) : null | array | ||
$token | PhpCsFixer\Tokenizer\Token | token |
return | null | array | array with 'type' and 'isStart' keys or null if not found |
/** * @param Tokens $tokens * @param int $index * * @return int */ private function findStart(Tokens $tokens, $index) { do { $index = $tokens->getPrevMeaningfulToken($index); $token = $tokens[$index]; $blockType = $tokens->detectBlockType($token); if (null !== $blockType && !$blockType['isStart']) { $index = $tokens->findBlockEnd($blockType['type'], $index, false); $token = $tokens[$index]; } } while (!$token->equalsAny(array('$', array(T_VARIABLE)))); $prevIndex = $tokens->getPrevMeaningfulToken($index); $prevToken = $tokens[$prevIndex]; if ($prevToken->equals('$')) { $index = $prevIndex; $prevIndex = $tokens->getPrevMeaningfulToken($index); $prevToken = $tokens[$prevIndex]; } if ($prevToken->isGivenKind(T_OBJECT_OPERATOR)) { return $this->findStart($tokens, $prevIndex); } if ($prevToken->isGivenKind(T_PAAMAYIM_NEKUDOTAYIM)) { $prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex); if (!$tokens[$prevPrevIndex]->isGivenKind(T_STRING)) { return $this->findStart($tokens, $prevIndex); } $index = $tokens->getTokenNotOfKindSibling($prevIndex, -1, array(array(T_NS_SEPARATOR), array(T_STRING))); $index = $tokens->getNextMeaningfulToken($index); } return $index; }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { foreach ($tokens as $index => $token) { if (!$token->isGivenKind(T_ECHO)) { continue; } $nextTokenIndex = $tokens->getNextMeaningfulToken($index); $endTokenIndex = $tokens->getNextTokenOfKind($index, array(';', array(T_CLOSE_TAG))); $canBeConverted = true; for ($i = $nextTokenIndex; $i < $endTokenIndex; ++$i) { if ($tokens[$i]->equalsAny(array('(', array(CT_ARRAY_SQUARE_BRACE_OPEN)))) { $blockType = $tokens->detectBlockType($tokens[$i]); $i = $tokens->findBlockEnd($blockType['type'], $i); } if ($tokens[$i]->equals(',')) { $canBeConverted = false; break; } } if (false === $canBeConverted) { continue; } $tokens->overrideAt($index, array(T_PRINT, 'print')); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { foreach ($tokens as $index => $token) { if (!$token->isGivenKind(T_ECHO)) { continue; } /* * HHVM parses '<?=' as T_ECHO instead of T_OPEN_TAG_WITH_ECHO * * @see https://github.com/facebook/hhvm/issues/4809 * @see https://github.com/facebook/hhvm/issues/7161 */ if (defined('HHVM_VERSION') && 0 === strpos($token->getContent(), '<?=')) { continue; } $nextTokenIndex = $tokens->getNextMeaningfulToken($index); $endTokenIndex = $tokens->getNextTokenOfKind($index, array(';', array(T_CLOSE_TAG))); $canBeConverted = true; for ($i = $nextTokenIndex; $i < $endTokenIndex; ++$i) { if ($tokens[$i]->equalsAny(array('(', array(CT_ARRAY_SQUARE_BRACE_OPEN)))) { $blockType = Tokens::detectBlockType($tokens[$i]); $i = $tokens->findBlockEnd($blockType['type'], $i); } if ($tokens[$i]->equals(',')) { $canBeConverted = false; break; } } if (false === $canBeConverted) { continue; } $tokens->overrideAt($index, array(T_PRINT, 'print')); } }
/** * Remove white line(s) after the index of a block type, * but only if the block is not on one line. * * @param int $index body start */ private function fixStructureOpenCloseIfMultiLine($index) { $blockTypeInfo = $this->tokens->detectBlockType($this->tokens[$index]); $bodyEnd = $this->tokens->findBlockEnd($blockTypeInfo['type'], $index); for ($i = $bodyEnd - 1; $i >= $index; --$i) { if (false !== strpos($this->tokens[$i]->getContent(), "\n")) { $this->removeEmptyLinesAfterLineWithTokenAt($i); $this->removeEmptyLinesAfterLineWithTokenAt($index); break; } } }
/** * @param int $expectedIndex * @param string $source * @param int $type * @param int $searchIndex * * @dataProvider provideFindBlockEndCases */ public function testFindBlockEnd($expectedIndex, $source, $type, $searchIndex) { Tokens::clearCache(); $tokens = Tokens::fromCode($source); $this->assertSame($expectedIndex, $tokens->findBlockEnd($type, $searchIndex, true)); $this->assertSame($searchIndex, $tokens->findBlockEnd($type, $expectedIndex, false)); $detectedType = Tokens::detectBlockType($tokens[$searchIndex]); $this->assertInternalType('array', $detectedType); $this->assertArrayHasKey('type', $detectedType); $this->assertArrayHasKey('isStart', $detectedType); $this->assertSame($type, $detectedType['type']); $this->assertTrue($detectedType['isStart']); $detectedType = Tokens::detectBlockType($tokens[$expectedIndex]); $this->assertInternalType('array', $detectedType); $this->assertArrayHasKey('type', $detectedType); $this->assertArrayHasKey('isStart', $detectedType); $this->assertSame($type, $detectedType['type']); $this->assertFalse($detectedType['isStart']); }
/** * Returns start and end token indexes of arguments. * * Return an array which each index being the first token af an * argument and the value the last. Including non-function tokens * such as comments and white space tokens, but without the separation * tokens like '(', ',' and ')'. * * @param Tokens $tokens * @param int $openParenthesis * @param int $closeParenthesis * * @return array<int, int> */ protected function getArguments(Tokens $tokens, $openParenthesis, $closeParenthesis) { $arguments = array(); $firstSensibleToken = $tokens->getNextMeaningfulToken($openParenthesis); if ($tokens[$firstSensibleToken]->equals(')')) { return $arguments; } $paramContentIndex = $openParenthesis + 1; $argumentsStart = $paramContentIndex; for (; $paramContentIndex < $closeParenthesis; ++$paramContentIndex) { $token = $tokens[$paramContentIndex]; // skip nested (), [], {} constructs $blockDefinitionProbe = Tokens::detectBlockType($token); if (null !== $blockDefinitionProbe && true === $blockDefinitionProbe['isStart']) { $paramContentIndex = $tokens->findBlockEnd($blockDefinitionProbe['type'], $paramContentIndex); continue; } // if comma matched, increase arguments counter if ($token->equals(',')) { $arguments[$argumentsStart] = $paramContentIndex - 1; $argumentsStart = $paramContentIndex + 1; } } $arguments[$argumentsStart] = $paramContentIndex - 1; return $arguments; }
/** * Inject into the text placeholders of candidates of vertical alignment. * * @param Tokens $tokens * @param int $startAt * @param int $endAt * * @return array($code, $context_counter) */ private function injectAlignmentPlaceholders(Tokens $tokens, $startAt, $endAt) { for ($index = $startAt; $index < $endAt; ++$index) { $token = $tokens[$index]; if ($token->isGivenKind(array(T_FOREACH, T_FOR, T_WHILE, T_IF, T_SWITCH))) { $index = $tokens->getNextMeaningfulToken($index); $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index); continue; } if ($token->isGivenKind(T_ARRAY)) { // don't use "$tokens->isArray()" here, short arrays are handled in the next case $from = $tokens->getNextMeaningfulToken($index); $until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $from); $index = $until; $this->injectArrayAlignmentPlaceholders($tokens, $from, $until); continue; } if ($token->isGivenKind(CT_ARRAY_SQUARE_BRACE_OPEN)) { $prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)]; if ($prevToken->isGivenKind(array(T_STRING, T_VARIABLE))) { continue; } $from = $index; $until = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $from); $index = $until; $this->injectArrayAlignmentPlaceholders($tokens, $from + 1, $until - 1); continue; } if ($token->isGivenKind(T_DOUBLE_ARROW)) { $tokenContent = sprintf(self::ALIGNABLE_PLACEHOLDER, $this->currentLevel) . $token->getContent(); $nextToken = $tokens[$index + 1]; if (!$nextToken->isWhitespace()) { $tokenContent .= ' '; } elseif ($nextToken->isWhitespace(" \t")) { $nextToken->setContent(' '); } $token->setContent($tokenContent); continue; } if ($token->equals(';')) { ++$this->deepestLevel; ++$this->currentLevel; continue; } if ($token->equals(',')) { for ($i = $index; $i < $endAt - 1; ++$i) { if (false !== strpos($tokens[$i - 1]->getContent(), "\n")) { break; } if ($tokens[$i + 1]->isGivenKind(array(T_ARRAY, CT_ARRAY_SQUARE_BRACE_OPEN))) { $arrayStartIndex = $tokens[$i + 1]->isGivenKind(T_ARRAY) ? $tokens->getNextMeaningfulToken($i + 1) : $i + 1; $blockType = Tokens::detectBlockType($tokens[$arrayStartIndex]); $arrayEndIndex = $tokens->findBlockEnd($blockType['type'], $arrayStartIndex); if ($tokens->isPartialCodeMultiline($arrayStartIndex, $arrayEndIndex)) { break; } } ++$index; } } } }
/** * @param Tokens $tokens * @param int $argumentStartIndex * @param int $argumentEndIndex * * @return bool */ private function isParenthesisNeeded(Tokens $tokens, $argumentStartIndex, $argumentEndIndex) { static $allowedKinds = array(T_DNUMBER, T_LNUMBER, T_VARIABLE, T_STRING, T_OBJECT_OPERATOR, T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_CAST, T_INT_CAST, T_INC, T_DEC, T_NS_SEPARATOR, T_WHITESPACE, T_DOUBLE_COLON, T_LINE, T_COMMENT, T_DOC_COMMENT, CT::T_NAMESPACE_OPERATOR); for ($i = $argumentStartIndex; $i <= $argumentEndIndex; ++$i) { if ($tokens[$i]->isGivenKind($allowedKinds) || $tokens[$i]->isEmpty()) { continue; } if (null !== ($blockType = Tokens::detectBlockType($tokens[$i]))) { $i = $tokens->findBlockEnd($blockType['type'], $i); continue; } if ($tokens[$i]->equals('$')) { $i = $tokens->getNextMeaningfulToken($i); if ($tokens[$i]->isGivenKind(CT::T_DYNAMIC_VAR_BRACE_OPEN)) { $i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE, $i); continue; } } if ($tokens[$i]->equals('+') && $tokens->getPrevMeaningfulToken($i) < $argumentStartIndex) { continue; } return true; } return false; }