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