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