Find a sequence of meaningful tokens and returns the array of their locations.
public findSequence ( array $sequence, integer $start, integer $end = null, boolean | array $caseSensitive = true ) : array | null | ||
$sequence | array | an array of tokens (same format used by getNextTokenOfKind) |
$start | integer | start index, defaulting to the start of the file |
$end | integer | end index, defaulting to the end of the file |
$caseSensitive | boolean | array | global case sensitiveness or an array of booleans, whose keys should match the ones used in $others. If any is missing, the default case-sensitive comparison is used |
Résultat | array | null | an array containing the tokens matching the sequence elements, indexed by their position |
/** * Looks up Tokens sequence for suitable candidates and delivers boundaries information, * which can be supplied by other methods in this abstract class. * * @param string $functionNameToSearch * @param Tokens $tokens * @param int $start * @param int|null $end * * @return int[]|null returns $functionName, $openParenthesis, $closeParenthesis packed into array */ protected function find($functionNameToSearch, Tokens $tokens, $start = 0, $end = null) { // make interface consistent with findSequence $end = null === $end ? $tokens->count() : $end; // find raw sequence which we can analyse for context $candidateSequence = array(array(T_STRING, $functionNameToSearch), '('); $matches = $tokens->findSequence($candidateSequence, $start, $end, false); if (null === $matches) { // not found, simply return without further attempts return; } // translate results for humans list($functionName, $openParenthesis) = array_keys($matches); // first criteria check: shall look like function call $functionNamePrefix = $tokens->getPrevMeaningfulToken($functionName); $functionNamePrecedingToken = $tokens[$functionNamePrefix]; if ($functionNamePrecedingToken->isGivenKind(array(T_DOUBLE_COLON, T_NEW, T_OBJECT_OPERATOR, T_FUNCTION))) { // this expression is differs from expected, resume return $this->find($functionNameToSearch, $tokens, $openParenthesis, $end); } // second criteria check: ensure namespace is the root one if ($functionNamePrecedingToken->isGivenKind(T_NS_SEPARATOR)) { $namespaceCandidate = $tokens->getPrevMeaningfulToken($functionNamePrefix); $namespaceCandidateToken = $tokens[$namespaceCandidate]; if ($namespaceCandidateToken->isGivenKind(array(T_NEW, T_STRING, CT::T_NAMESPACE_OPERATOR))) { // here can be added complete namespace scan // this expression is differs from expected, resume return $this->find($functionNameToSearch, $tokens, $openParenthesis, $end); } } // final step: find closing parenthesis $closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis); return array($functionName, $openParenthesis, $closeParenthesis); }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { foreach ($this->configuration as $methodBefore => $methodAfter) { for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) { $sequence = $tokens->findSequence(array(array(T_VARIABLE, '$this'), array(T_OBJECT_OPERATOR, '->'), array(T_STRING, $methodBefore), '('), $index); if (null === $sequence) { break; } $sequenceIndexes = array_keys($sequence); $tokens[$sequenceIndexes[2]]->setContent($methodAfter); $index = $sequenceIndexes[3]; } } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { static $searchSequence = array(array(T_VARIABLE, '$this'), array(T_OBJECT_OPERATOR, '->'), array(T_STRING)); $index = 1; $candidate = $tokens->findSequence($searchSequence, $index); while (null !== $candidate) { end($candidate); $index = $this->getAssertCandidate($tokens, key($candidate)); if (is_array($index)) { $index = $this->fixAssert($tokens, $index); } ++$index; $candidate = $tokens->findSequence($searchSequence, $index); } }
/** * {@inheritdoc} */ public function fix(\SplFileInfo $file, Tokens $tokens) { $end = $tokens->count() - 1; foreach (self::$functions as $map) { // the sequence is the function name, followed by "(" and a quoted string $seq = array(array(T_STRING, $map[0]), '(', array(T_CONSTANT_ENCAPSED_STRING)); $currIndex = 0; while (null !== $currIndex) { $match = $tokens->findSequence($seq, $currIndex, $end, false); // did we find a match? if (null === $match) { break; } // findSequence also returns the tokens, but we're only interested in the indexes, i.e.: // 0 => function name, // 1 => bracket "(" // 2 => quoted string passed as 1st parameter $match = array_keys($match); // advance tokenizer cursor $currIndex = $match[2]; // ensure it's a function call (not a method / static call) $prev = $tokens->getPrevMeaningfulToken($match[0]); if (null === $prev || $tokens[$prev]->isGivenKind(array(T_OBJECT_OPERATOR, T_DOUBLE_COLON))) { continue; } // ensure the first parameter is just a string (e.g. has nothing appended) $next = $tokens->getNextMeaningfulToken($match[2]); if (null === $next || !$tokens[$next]->equalsAny(array(',', ')'))) { continue; } // convert to PCRE $string = substr($tokens[$match[2]]->getContent(), 1, -1); $quote = substr($tokens[$match[2]]->getContent(), 0, 1); $delim = $this->getBestDelimiter($string); $preg = $delim . addcslashes($string, $delim) . $delim . 'D' . $map[2]; // check if the preg is valid if (!$this->checkPreg($preg)) { continue; } // modify function and argument $tokens[$match[2]]->setContent($quote . $preg . $quote); $tokens[$match[0]]->setContent($map[1]); } } }
/** * Find a function or method matching a given name within certain bounds. * * @param Tokens $tokens the Tokens instance * @param string $name the function/Method name * @param int $startIndex the search start index * @param int $endIndex the search end index * * @return array|null An associative array, if a match is found: * * - nameIndex (int): The index of the function/method name. * - startIndex (int): The index of the function/method start. * - endIndex (int): The index of the function/method end. * - bodyIndex (int): The index of the function/method body. * - modifiers (array): The modifiers as array keys and their index as * the values, e.g. array(T_PUBLIC => 10). */ private function findFunction(Tokens $tokens, $name, $startIndex, $endIndex) { $function = $tokens->findSequence(array(array(T_FUNCTION), array(T_STRING, $name), '('), $startIndex, $endIndex, false); if (null === $function) { return; } // keep only the indexes $function = array_keys($function); // find previous block, saving method modifiers for later use $possibleModifiers = array(T_PUBLIC, T_PROTECTED, T_PRIVATE, T_STATIC, T_ABSTRACT, T_FINAL); $modifiers = array(); $prevBlock = $tokens->getPrevMeaningfulToken($function[0]); while (null !== $prevBlock && $tokens[$prevBlock]->isGivenKind($possibleModifiers)) { $modifiers[$tokens[$prevBlock]->getId()] = $prevBlock; $prevBlock = $tokens->getPrevMeaningfulToken($prevBlock); } if (isset($modifiers[T_ABSTRACT])) { // abstract methods have no body $bodyStart = null; $funcEnd = $tokens->getNextTokenOfKind($function[2], array(';')); } else { // find method body start and the end of the function definition $bodyStart = $tokens->getNextTokenOfKind($function[2], array('{')); $funcEnd = $bodyStart !== null ? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $bodyStart) : null; } return array('nameIndex' => $function[1], 'startIndex' => $prevBlock + 1, 'endIndex' => $funcEnd, 'bodyIndex' => $bodyStart, 'modifiers' => $modifiers); }
/** * @param array<string, string> $map * @param Tokens $tokens * @param int $index * @param string $method * * @return int|null */ private function fixAssert(array $map, Tokens $tokens, $index, $method) { $sequence = $tokens->findSequence(array(array(T_VARIABLE, '$this'), array(T_OBJECT_OPERATOR, '->'), array(T_STRING, $method), '('), $index); if (null === $sequence) { return; } $sequenceIndexes = array_keys($sequence); $sequenceIndexes[4] = $tokens->getNextMeaningfulToken($sequenceIndexes[3]); $firstParameterToken = $tokens[$sequenceIndexes[4]]; if (!$firstParameterToken->isNativeConstant()) { return; } $sequenceIndexes[5] = $tokens->getNextMeaningfulToken($sequenceIndexes[4]); // return if first method argument is an expression, not value if (!$tokens[$sequenceIndexes[5]]->equals(',')) { return; } $tokens[$sequenceIndexes[2]]->setContent($map[$firstParameterToken->getContent()]); $tokens->clearRange($sequenceIndexes[4], $tokens->getNextNonWhitespace($sequenceIndexes[5]) - 1); return $sequenceIndexes[5]; }