lithium\analysis\Parser::find()
Finds a pattern in a block of code.
Parameters
-
string
$code
-
string
$pattern
-
array
$options
The list of options to be used when parsing / matching
$code
:- 'ignore': An array of token names to ignore while parsing, defaults to
['T_WHITESPACE']
- 'lineBreaks': If true, all tokens in a single pattern match must appear on the same line of code, defaults to false
- 'startOfLine': If true, the pattern must match starting with the beginning of the line of code to be matched, defaults to false
- 'ignore': An array of token names to ignore while parsing, defaults to
Returns
arraySource
public static function find($code, $pattern, array $options = []) {
$defaults = [
'all' => true, 'capture' => [], 'ignore' => ['T_WHITESPACE'],
'return' => true, 'lineBreaks' => false, 'startOfLine' => false
];
$options += $defaults;
$results = [];
$matches = [];
$patternMatch = [];
$ret = $options['return'];
$tokens = new Collection(['data' => static::tokenize($code, $options)]);
$pattern = new Collection(['data' => static::tokenize($pattern, $options)]);
$breaks = function($token) use (&$tokens, &$matches, &$patternMatch, $options) {
if (!$options['lineBreaks']) {
return true;
}
if (empty($patternMatch) && !$options['startOfLine']) {
return true;
}
if (empty($patternMatch)) {
$prev = $tokens->prev();
$tokens->next();
} else {
$prev = reset($patternMatch);
}
if (empty($patternMatch) && $options['startOfLine']) {
return ($token['line'] > $prev['line']);
}
return ($token['line'] === $prev['line']);
};
$capture = function($token) use (&$matches, &$patternMatch, $tokens, $breaks, $options) {
if ($token === null) {
$matches = $patternMatch = [];
return false;
}
if (empty($patternMatch)) {
$prev = $tokens->prev();
$tokens->next();
if ($options['startOfLine'] && $token['line'] === $prev['line']) {
$patternMatch = $matches = [];
return false;
}
}
$patternMatch[] = $token;
if (empty($options['capture']) || !in_array($token['name'], $options['capture'])) {
return true;
}
if (!$breaks($token)) {
$matches = [];
return true;
}
$matches[] = $token;
return true;
};
$executors = [
'*' => function(&$tokens, &$pattern) use ($options, $capture) {
$closing = $pattern->next();
$tokens->prev();
while (($t = $tokens->next()) && !Parser::matchToken($closing, $t)) {
$capture($t);
}
$pattern->next();
}
];
$tokens->rewind();
$pattern->rewind();
while ($tokens->valid()) {
if (!$pattern->valid()) {
$pattern->rewind();
if (!empty($matches)) {
$results[] = array_map(
function($i) use ($ret) { return isset($i[$ret]) ? $i[$ret] : $i; },
$matches
);
}
$capture(null);
}
$p = $pattern->current();
$t = $tokens->current();
switch (true) {
case (static::matchToken($p, $t)):
$capture($t) ? $pattern->next() : $pattern->rewind();
break;
case (isset($executors[$p['name']])):
$exec = $executors[$p['name']];
$exec($tokens, $pattern);
break;
default:
$capture(null);
$pattern->rewind();
break;
}
$tokens->next();
}
return $results;
}