lithium\analysis\Parser::find()

public static method

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

Returns

array

Source

	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;
	}