cssSelectorLexer = $cssSelectorLexer ?? new CssSelectorLexer(); } /** @return array> */ public function collate( string $regex, string $string, ?callable $transform = null ):array { if($regex === Translator::CSS_REGEX) { return $this->collateCssSelector($string, $transform); } preg_match_all( $regex, $string, $matches, PREG_PATTERN_ORDER ); $set = $this->initialiseSet($matches[0]); foreach($matches as $key => $matchedGroup) { if(is_numeric($key)) { continue; } $this->collateGroup($set, $key, $matchedGroup, $transform); } return $set; } /** @return array> */ private function collateCssSelector( string $selector, ?callable $transform ):array { return $this->cssSelectorLexer->lex($selector, $transform); } /** * @param array $matches * @return array|null> */ private function initialiseSet(array $matches):array { $set = []; foreach($matches as $index => $value) { if($value !== "") { $set[$index] = null; } } return $set; } /** * @param array|null> $set * @param array $matchedGroup */ private function collateGroup( array &$set, string $groupKey, array $matchedGroup, ?callable $transform ):void { foreach($matchedGroup as $index => $match) { if($match === "") { continue; } $toSet = $this->buildMatchPayload($groupKey, $match, $transform); $this->appendMatch($set, $index, $toSet); } } /** @return array */ private function buildMatchPayload( string $groupKey, string $match, ?callable $transform ):array { if($transform) { return $transform($groupKey, $match); } return ["type" => $groupKey, "content" => $match]; } /** * @param array|null> $set * @param array $toSet */ private function appendMatch(array &$set, int $index, array $toSet):void { if(!isset($set[$index])) { $set[$index] = $toSet; return; } if(!isset($set[$index]["detail"])) { $set[$index]["detail"] = []; } $set[$index]["detail"][] = $toSet; } }