/** * 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, $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 Collection(array('items' => static::tokenize($code, $options))); $pattern = new Collection(array('items' => 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; }
/** * Provides short-hand convenience syntax for filter chaining. * * @see lithium\core\Object::applyFilter() * @see lithium\core\Object::_filter() * @param object $self The object instance that owns the filtered method. * @param array $params An associative array containing the parameters passed to the filtered * method. * @param array $chain The Filters object instance containing this chain of filters. * @return mixed Returns the return value of the next filter in the chain. */ public function next($self, $params, $chain) { if (empty($self) || empty($chain)) { return parent::next(); } $next = parent::next(); return $next($self, $params, $chain); }
/** * Tests the `ArrayAccess` interface implementation for traversing values. * * @return void */ public function testArrayAccessTraversalMethods() { $collection = new Collection(array('data' => array('foo', 'bar', 'baz' => 'dib'))); $this->assertEqual('foo', $collection->current()); $this->assertEqual('bar', $collection->next()); $this->assertEqual('foo', $collection->prev()); $this->assertEqual('bar', $collection->next()); $this->assertEqual('dib', $collection->next()); $this->assertEqual('baz', $collection->key()); $this->assertTrue($collection->valid()); $this->assertFalse($collection->next()); $this->assertFalse($collection->valid()); $this->assertEqual('foo', $collection->rewind()); $this->assertTrue($collection->valid()); $this->assertEqual('dib', $collection->prev()); $this->assertTrue($collection->valid()); $this->assertEqual('bar', $collection->prev()); $this->assertTrue($collection->valid()); $this->assertEqual('dib', $collection->end()); $this->assertTrue($collection->valid()); }
/** * Provides short-hand convenience syntax for filter chaining. * * @param object $self The object instance that owns the filtered method. * @param array $params An associative array containing the parameters passed to the filtered * method. * @param array $chain The Filters object instance containing this chain of filters. * @return mixed Returns the return value of the next filter in the chain. * @see lithium\core\Object::applyFilter() * @see lithium\core\Object::_filter() * @todo Implement checks allowing params to be null, to traverse filter chain */ public function next($self, $params, $chain) { if (empty($self) || empty($chain)) { return parent::next(); } return parent::next()->__invoke($self, $params, $chain); }