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