1<?php
2
3declare(strict_types=1);
4/**
5 * SimplePie
6 *
7 * A PHP-Based RSS and Atom Feed Framework.
8 * Takes the hard work out of managing a complete RSS/Atom solution.
9 *
10 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without modification, are
14 * permitted provided that the following conditions are met:
15 *
16 * 	* Redistributions of source code must retain the above copyright notice, this list of
17 * 	  conditions and the following disclaimer.
18 *
19 * 	* Redistributions in binary form must reproduce the above copyright notice, this list
20 * 	  of conditions and the following disclaimer in the documentation and/or other materials
21 * 	  provided with the distribution.
22 *
23 * 	* Neither the name of the SimplePie Team nor the names of its contributors may be used
24 * 	  to endorse or promote products derived from this software without specific prior
25 * 	  written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
28 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
29 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
30 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 *
37 * @package SimplePie
38 * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
39 * @author Ryan Parman
40 * @author Sam Sneddon
41 * @author Ryan McCue
42 * @link http://simplepie.org/ SimplePie
43 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
44 */
45
46namespace SimplePie\XML\Declaration;
47
48/**
49 * Parses the XML Declaration
50 *
51 * @package SimplePie
52 * @subpackage Parsing
53 */
54class Parser
55{
56    /**
57     * XML Version
58     *
59     * @access public
60     * @var string
61     */
62    public $version = '1.0';
63
64    /**
65     * Encoding
66     *
67     * @access public
68     * @var string
69     */
70    public $encoding = 'UTF-8';
71
72    /**
73     * Standalone
74     *
75     * @access public
76     * @var bool
77     */
78    public $standalone = false;
79
80    private const STATE_BEFORE_VERSION_NAME = 'before_version_name';
81
82    private const STATE_VERSION_NAME = 'version_name';
83
84    private const STATE_VERSION_EQUALS = 'version_equals';
85
86    private const STATE_VERSION_VALUE = 'version_value';
87
88    private const STATE_ENCODING_NAME = 'encoding_name';
89
90    private const STATE_EMIT = 'emit';
91
92    private const STATE_ENCODING_EQUALS = 'encoding_equals';
93
94    private const STATE_STANDALONE_NAME = 'standalone_name';
95
96    private const STATE_ENCODING_VALUE = 'encoding_value';
97
98    private const STATE_STANDALONE_EQUALS = 'standalone_equals';
99
100    private const STATE_STANDALONE_VALUE = 'standalone_value';
101
102    private const STATE_ERROR = false;
103
104    /**
105     * Current state of the state machine
106     *
107     * @access private
108     * @var self::STATE_*
109     */
110    public $state = self::STATE_BEFORE_VERSION_NAME;
111
112    /**
113     * Input data
114     *
115     * @access private
116     * @var string
117     */
118    public $data = '';
119
120    /**
121     * Input data length (to avoid calling strlen() everytime this is needed)
122     *
123     * @access private
124     * @var int
125     */
126    public $data_length = 0;
127
128    /**
129     * Current position of the pointer
130     *
131     * @var int
132     * @access private
133     */
134    public $position = 0;
135
136    /**
137     * Create an instance of the class with the input data
138     *
139     * @access public
140     * @param string $data Input data
141     */
142    public function __construct($data)
143    {
144        $this->data = $data;
145        $this->data_length = strlen($this->data);
146    }
147
148    /**
149     * Parse the input data
150     *
151     * @access public
152     * @return bool true on success, false on failure
153     */
154    public function parse()
155    {
156        while ($this->state && $this->state !== self::STATE_EMIT && $this->has_data()) {
157            $state = $this->state;
158            $this->$state();
159        }
160        $this->data = '';
161        if ($this->state === self::STATE_EMIT) {
162            return true;
163        }
164
165        $this->version = '';
166        $this->encoding = '';
167        $this->standalone = '';
168        return false;
169    }
170
171    /**
172     * Check whether there is data beyond the pointer
173     *
174     * @access private
175     * @return bool true if there is further data, false if not
176     */
177    public function has_data()
178    {
179        return (bool) ($this->position < $this->data_length);
180    }
181
182    /**
183     * Advance past any whitespace
184     *
185     * @return int Number of whitespace characters passed
186     */
187    public function skip_whitespace()
188    {
189        $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
190        $this->position += $whitespace;
191        return $whitespace;
192    }
193
194    /**
195     * Read value
196     */
197    public function get_value()
198    {
199        $quote = substr($this->data, $this->position, 1);
200        if ($quote === '"' || $quote === "'") {
201            $this->position++;
202            $len = strcspn($this->data, $quote, $this->position);
203            if ($this->has_data()) {
204                $value = substr($this->data, $this->position, $len);
205                $this->position += $len + 1;
206                return $value;
207            }
208        }
209        return false;
210    }
211
212    public function before_version_name()
213    {
214        if ($this->skip_whitespace()) {
215            $this->state = self::STATE_VERSION_NAME;
216        } else {
217            $this->state = self::STATE_ERROR;
218        }
219    }
220
221    public function version_name()
222    {
223        if (substr($this->data, $this->position, 7) === 'version') {
224            $this->position += 7;
225            $this->skip_whitespace();
226            $this->state = self::STATE_VERSION_EQUALS;
227        } else {
228            $this->state = self::STATE_ERROR;
229        }
230    }
231
232    public function version_equals()
233    {
234        if (substr($this->data, $this->position, 1) === '=') {
235            $this->position++;
236            $this->skip_whitespace();
237            $this->state = self::STATE_VERSION_VALUE;
238        } else {
239            $this->state = self::STATE_ERROR;
240        }
241    }
242
243    public function version_value()
244    {
245        if ($this->version = $this->get_value()) {
246            $this->skip_whitespace();
247            if ($this->has_data()) {
248                $this->state = self::STATE_ENCODING_NAME;
249            } else {
250                $this->state = self::STATE_EMIT;
251            }
252        } else {
253            $this->state = self::STATE_ERROR;
254        }
255    }
256
257    public function encoding_name()
258    {
259        if (substr($this->data, $this->position, 8) === 'encoding') {
260            $this->position += 8;
261            $this->skip_whitespace();
262            $this->state = self::STATE_ENCODING_EQUALS;
263        } else {
264            $this->state = self::STATE_STANDALONE_NAME;
265        }
266    }
267
268    public function encoding_equals()
269    {
270        if (substr($this->data, $this->position, 1) === '=') {
271            $this->position++;
272            $this->skip_whitespace();
273            $this->state = self::STATE_ENCODING_VALUE;
274        } else {
275            $this->state = self::STATE_ERROR;
276        }
277    }
278
279    public function encoding_value()
280    {
281        if ($this->encoding = $this->get_value()) {
282            $this->skip_whitespace();
283            if ($this->has_data()) {
284                $this->state = self::STATE_STANDALONE_NAME;
285            } else {
286                $this->state = self::STATE_EMIT;
287            }
288        } else {
289            $this->state = self::STATE_ERROR;
290        }
291    }
292
293    public function standalone_name()
294    {
295        if (substr($this->data, $this->position, 10) === 'standalone') {
296            $this->position += 10;
297            $this->skip_whitespace();
298            $this->state = self::STATE_STANDALONE_EQUALS;
299        } else {
300            $this->state = self::STATE_ERROR;
301        }
302    }
303
304    public function standalone_equals()
305    {
306        if (substr($this->data, $this->position, 1) === '=') {
307            $this->position++;
308            $this->skip_whitespace();
309            $this->state = self::STATE_STANDALONE_VALUE;
310        } else {
311            $this->state = self::STATE_ERROR;
312        }
313    }
314
315    public function standalone_value()
316    {
317        if ($standalone = $this->get_value()) {
318            switch ($standalone) {
319                case 'yes':
320                    $this->standalone = true;
321                    break;
322
323                case 'no':
324                    $this->standalone = false;
325                    break;
326
327                default:
328                    $this->state = self::STATE_ERROR;
329                    return;
330            }
331
332            $this->skip_whitespace();
333            if ($this->has_data()) {
334                $this->state = self::STATE_ERROR;
335            } else {
336                $this->state = self::STATE_EMIT;
337            }
338        } else {
339            $this->state = self::STATE_ERROR;
340        }
341    }
342}
343
344class_alias('SimplePie\XML\Declaration\Parser', 'SimplePie_XML_Declaration_Parser');
345