1*04fd306cSNickeau<?php 2*04fd306cSNickeau/** 3*04fd306cSNickeau* Scans a string and creates list of tokens. 4*04fd306cSNickeau* 5*04fd306cSNickeau* @license http://www.opensource.org/licenses/mit-license.php The MIT License 6*04fd306cSNickeau* @copyright Copyright 2010-2014 PhpCss Team 7*04fd306cSNickeau*/ 8*04fd306cSNickeaunamespace PhpCss { 9*04fd306cSNickeau 10*04fd306cSNickeau use UnexpectedValueException; 11*04fd306cSNickeau 12*04fd306cSNickeau /** 13*04fd306cSNickeau * Scans a string and creates list of tokens. 14*04fd306cSNickeau * 15*04fd306cSNickeau * The actual result depends on the status, the status 16*04fd306cSNickeau * class does the actual token matching and generation, the scanner handles, to loops and 17*04fd306cSNickeau * delegations. 18*04fd306cSNickeau */ 19*04fd306cSNickeau class Scanner { 20*04fd306cSNickeau 21*04fd306cSNickeau /** 22*04fd306cSNickeau * Scanner status object 23*04fd306cSNickeau * @var Scanner\Status 24*04fd306cSNickeau */ 25*04fd306cSNickeau private $_status; 26*04fd306cSNickeau /** 27*04fd306cSNickeau * string to parse 28*04fd306cSNickeau * @var string 29*04fd306cSNickeau */ 30*04fd306cSNickeau private $_buffer = ''; 31*04fd306cSNickeau /** 32*04fd306cSNickeau * current offset 33*04fd306cSNickeau * @var integer 34*04fd306cSNickeau */ 35*04fd306cSNickeau private $_offset = 0; 36*04fd306cSNickeau 37*04fd306cSNickeau /** 38*04fd306cSNickeau * Constructor, set status object 39*04fd306cSNickeau * 40*04fd306cSNickeau * @param Scanner\Status $status 41*04fd306cSNickeau */ 42*04fd306cSNickeau public function __construct(Scanner\Status $status) { 43*04fd306cSNickeau $this->_status = $status; 44*04fd306cSNickeau } 45*04fd306cSNickeau 46*04fd306cSNickeau /** 47*04fd306cSNickeau * Scan a string for tokens 48*04fd306cSNickeau * 49*04fd306cSNickeau * @param array &$target token target 50*04fd306cSNickeau * @param string $string content string 51*04fd306cSNickeau * @param integer $offset start offset 52*04fd306cSNickeau * @throws UnexpectedValueException 53*04fd306cSNickeau * @return integer new offset 54*04fd306cSNickeau */ 55*04fd306cSNickeau public function scan(array &$target, string $string, int $offset = 0): int { 56*04fd306cSNickeau $this->_buffer = $string; 57*04fd306cSNickeau $this->_offset = $offset; 58*04fd306cSNickeau while ($token = $this->_next()) { 59*04fd306cSNickeau $target[] = $token; 60*04fd306cSNickeau // switch back to previous scanner 61*04fd306cSNickeau if ($this->_status->isEndToken($token)) { 62*04fd306cSNickeau return $this->_offset; 63*04fd306cSNickeau } 64*04fd306cSNickeau // check for status switch 65*04fd306cSNickeau if ($newStatus = $this->_status->getNewStatus($token)) { 66*04fd306cSNickeau // delegate to subscanner 67*04fd306cSNickeau $this->_offset = $this->_delegate($target, $newStatus); 68*04fd306cSNickeau } 69*04fd306cSNickeau } 70*04fd306cSNickeau if ($this->_offset < strlen($this->_buffer)) { 71*04fd306cSNickeau /** 72*04fd306cSNickeau * @todo a some substring logic for large strings 73*04fd306cSNickeau */ 74*04fd306cSNickeau throw new Exception\InvalidCharacterException( 75*04fd306cSNickeau $this->_buffer, 76*04fd306cSNickeau $this->_offset, 77*04fd306cSNickeau $this->_status 78*04fd306cSNickeau ); 79*04fd306cSNickeau } 80*04fd306cSNickeau return $this->_offset; 81*04fd306cSNickeau } 82*04fd306cSNickeau 83*04fd306cSNickeau /** 84*04fd306cSNickeau * Get next token 85*04fd306cSNickeau * 86*04fd306cSNickeau * @return Scanner\Token|NULL 87*04fd306cSNickeau */ 88*04fd306cSNickeau private function _next(): ?Scanner\Token { 89*04fd306cSNickeau if (($token = $this->_status->getToken($this->_buffer, $this->_offset)) && 90*04fd306cSNickeau $token->length > 0) { 91*04fd306cSNickeau $this->_offset += $token->length; 92*04fd306cSNickeau return $token; 93*04fd306cSNickeau } 94*04fd306cSNickeau return NULL; 95*04fd306cSNickeau } 96*04fd306cSNickeau 97*04fd306cSNickeau /** 98*04fd306cSNickeau * Got new status, delegate to subscanner. 99*04fd306cSNickeau * 100*04fd306cSNickeau * If the status returns a new status object, a new scanner is created to handle it. 101*04fd306cSNickeau * 102*04fd306cSNickeau * @param Scanner\Token[] $target 103*04fd306cSNickeau * @param Scanner\Status $status 104*04fd306cSNickeau * @return int 105*04fd306cSNickeau */ 106*04fd306cSNickeau private function _delegate(array &$target, Scanner\Status $status): int { 107*04fd306cSNickeau $scanner = new self($status); 108*04fd306cSNickeau return $scanner->scan($target, $this->_buffer, $this->_offset); 109*04fd306cSNickeau } 110*04fd306cSNickeau 111*04fd306cSNickeau public function getStatus(): Scanner\Status { 112*04fd306cSNickeau return $this->_status; 113*04fd306cSNickeau } 114*04fd306cSNickeau } 115*04fd306cSNickeau} 116