_status = $status; } /** * Scan a string for tokens * * @param array &$target token target * @param string $string content string * @param integer $offset start offset * @throws UnexpectedValueException * @return integer new offset */ public function scan(array &$target, string $string, int $offset = 0): int { $this->_buffer = $string; $this->_offset = $offset; while ($token = $this->_next()) { $target[] = $token; // switch back to previous scanner if ($this->_status->isEndToken($token)) { return $this->_offset; } // check for status switch if ($newStatus = $this->_status->getNewStatus($token)) { // delegate to subscanner $this->_offset = $this->_delegate($target, $newStatus); } } if ($this->_offset < strlen($this->_buffer)) { /** * @todo a some substring logic for large strings */ throw new Exception\InvalidCharacterException( $this->_buffer, $this->_offset, $this->_status ); } return $this->_offset; } /** * Get next token * * @return Scanner\Token|NULL */ private function _next(): ?Scanner\Token { if (($token = $this->_status->getToken($this->_buffer, $this->_offset)) && $token->length > 0) { $this->_offset += $token->length; return $token; } return NULL; } /** * Got new status, delegate to subscanner. * * If the status returns a new status object, a new scanner is created to handle it. * * @param Scanner\Token[] $target * @param Scanner\Status $status * @return int */ private function _delegate(array &$target, Scanner\Status $status): int { $scanner = new self($status); return $scanner->scan($target, $this->_buffer, $this->_offset); } public function getStatus(): Scanner\Status { return $this->_status; } } }