matchers = $matchers; $this->all = (bool)$all; } /** * Implements "&&": All of the options, in any order * @param Matcher[] $matchers * @return static */ public static function allOf( array $matchers ) { return new static( $matchers, true ); } /** * Implements "||": One or more of the options, in any order * @param Matcher[] $matchers * @return static */ public static function someOf( array $matchers ) { return new static( $matchers, false ); } protected function generateMatches( ComponentValueList $values, $start, array $options ) { $used = []; // As each Matcher is used, push it onto the stack along with the set // of remaining matchers. $stack = [ [ new Match( $values, $start, 0 ), $this->matchers, new \ArrayIterator( $this->matchers ), null, new \EmptyIterator ] ]; do { /** @var $lastMatch Match */ /** @var $matchers Matcher[] */ /** @var $matcherIter \Iterator */ /** @var $curMatcher Matcher|null */ /** @var $iter \Iterator */ list( $lastMatch, $matchers, $matcherIter, $curMatcher, $iter ) = $stack[count( $stack ) - 1]; // If the top of the stack has more matches, process the next one. if ( $iter->valid() ) { $match = $iter->current(); $iter->next(); // If we have unused matchers to try after this one, do so. // Otherwise yield and continue with the current one. if ( $matchers ) { $stack[] = [ $match, $matchers, new \ArrayIterator( $matchers ), null, new \EmptyIterator ]; } else { $newMatch = $this->makeMatch( $values, $start, $match->getNext(), $match, $stack ); $mid = $newMatch->getUniqueID(); if ( !isset( $used[$mid] ) ) { $used[$mid] = 1; yield $newMatch; } } continue; } // We ran out of matches for the current top of the stack. Pop it, // and put $curMatcher back into $matchers so it can be tried again // at a later position. array_pop( $stack ); if ( $curMatcher ) { $matchers[$matcherIter->key()] = $curMatcher; $matcherIter->next(); } $fromPos = $lastMatch->getNext(); // If there are more matchers to try, pull the next one out of // $matchers and try it at the current position. Otherwise, maybe // yield the current position and backtrack. if ( $matcherIter->valid() ) { $curMatcher = $matcherIter->current(); unset( $matchers[$matcherIter->key()] ); $iter = $curMatcher->generateMatches( $values, $fromPos, $options ); $stack[] = [ $lastMatch, $matchers, $matcherIter, $curMatcher, $iter ]; } else { if ( $stack && !$this->all ) { $newMatch = $this->makeMatch( $values, $start, $fromPos, $lastMatch, $stack ); $mid = $newMatch->getUniqueID(); if ( !isset( $used[$mid] ) ) { $used[$mid] = 1; yield $newMatch; } } } } while ( $stack ); } }