/** * Finds a pattern in a block of code. * * @param string $code * @param string $pattern * @param 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 * `array('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 * @return array */ public static function find($code, $pattern, array $options = array()) { $defaults = array('all' => true, 'capture' => array(), 'ignore' => array('T_WHITESPACE'), 'return' => true, 'lineBreaks' => false, 'startOfLine' => false); $options += $defaults; $results = array(); $matches = array(); $patternMatch = array(); $ret = $options['return']; $tokens = new CollectionObject(static::tokenize($code, $options)); $pattern = new CollectionObject(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 (is_null($token)) { $matches = $patternMatch = array(); return false; } if (empty($patternMatch)) { $prev = $tokens->prev(); $tokens->next(); if ($options['startOfLine'] && $token['line'] == $prev['line']) { $patternMatch = $matches = array(); return false; } } $patternMatch[] = $token; if (empty($options['capture']) || !in_array($token['name'], $options['capture'])) { return true; } if (!$breaks($token)) { $matches = array(); return true; } $matches[] = $token; return true; }; $executors = array('*' => 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; }
/** * Gets the static and dynamic dependencies for a class or group of classes. * * @param mixed $classes Either a string specifying a class, or a numerically indexed array * of classes * @param array $options * @return array An array of the static and dynamic class dependencies * @todo Document valid options */ public static function dependencies($classes, array $options = array()) { $defaults = array('type' => null); $options += $defaults; $static = $dynamic = array(); $trim = function ($c) { return trim(trim($c, '\\')); }; $join = function ($i) { return join('', $i); }; foreach ((array) $classes as $class) { $data = explode("\n", file_get_contents(\Radical\Core\Libraries::path($class))); $data = "<?php \n" . join("\n", preg_grep('/^\\s*use /', $data)) . "\n ?>"; $classes = array_map($join, Parser::find($data, 'use *;', array('return' => 'content', 'lineBreaks' => true, 'startOfLine' => true, 'capture' => array('T_STRING', 'T_NS_SEPARATOR')))); if ($classes) { $static = array_unique(array_merge($static, array_map($trim, $classes))); } $classes = static::info($class . '::$_classes', array('value')); if (isset($classes['value'])) { $dynamic = array_merge($dynamic, array_map($trim, array_values($classes['value']))); } } if (empty($options['type'])) { return array_unique(array_merge($static, $dynamic)); } $type = $options['type']; return isset(${$type}) ? ${$type} : null; }