xref: /plugin/combo/vendor/carica/phpcss/src/PhpCss/Scanner.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
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