Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/yaml/Parser.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | 4c8ae668cc8c |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
10 */ | 10 */ |
11 | 11 |
12 namespace Symfony\Component\Yaml; | 12 namespace Symfony\Component\Yaml; |
13 | 13 |
14 use Symfony\Component\Yaml\Exception\ParseException; | 14 use Symfony\Component\Yaml\Exception\ParseException; |
15 use Symfony\Component\Yaml\Tag\TaggedValue; | |
15 | 16 |
16 /** | 17 /** |
17 * Parser parses YAML strings to convert them to PHP arrays. | 18 * Parser parses YAML strings to convert them to PHP arrays. |
18 * | 19 * |
19 * @author Fabien Potencier <fabien@symfony.com> | 20 * @author Fabien Potencier <fabien@symfony.com> |
21 * | |
22 * @final since version 3.4 | |
20 */ | 23 */ |
21 class Parser | 24 class Parser |
22 { | 25 { |
23 const TAG_PATTERN = '((?P<tag>![\w!.\/:-]+) +)?'; | 26 const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)'; |
24 const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?'; | 27 const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?'; |
25 | 28 |
29 private $filename; | |
26 private $offset = 0; | 30 private $offset = 0; |
27 private $totalNumberOfLines; | 31 private $totalNumberOfLines; |
28 private $lines = array(); | 32 private $lines = array(); |
29 private $currentLineNb = -1; | 33 private $currentLineNb = -1; |
30 private $currentLine = ''; | 34 private $currentLine = ''; |
31 private $refs = array(); | 35 private $refs = array(); |
32 private $skippedLineNumbers = array(); | 36 private $skippedLineNumbers = array(); |
33 private $locallySkippedLineNumbers = array(); | 37 private $locallySkippedLineNumbers = array(); |
34 | 38 |
35 /** | 39 public function __construct() |
36 * Constructor. | 40 { |
37 * | 41 if (func_num_args() > 0) { |
38 * @param int $offset The offset of YAML document (used for line numbers in error messages) | 42 @trigger_error(sprintf('The constructor arguments $offset, $totalNumberOfLines, $skippedLineNumbers of %s are deprecated and will be removed in 4.0', self::class), E_USER_DEPRECATED); |
39 * @param int|null $totalNumberOfLines The overall number of lines being parsed | 43 |
40 * @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser | 44 $this->offset = func_get_arg(0); |
41 */ | 45 if (func_num_args() > 1) { |
42 public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array()) | 46 $this->totalNumberOfLines = func_get_arg(1); |
43 { | 47 } |
44 $this->offset = $offset; | 48 if (func_num_args() > 2) { |
45 $this->totalNumberOfLines = $totalNumberOfLines; | 49 $this->skippedLineNumbers = func_get_arg(2); |
46 $this->skippedLineNumbers = $skippedLineNumbers; | 50 } |
51 } | |
52 } | |
53 | |
54 /** | |
55 * Parses a YAML file into a PHP value. | |
56 * | |
57 * @param string $filename The path to the YAML file to be parsed | |
58 * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior | |
59 * | |
60 * @return mixed The YAML converted to a PHP value | |
61 * | |
62 * @throws ParseException If the file could not be read or the YAML is not valid | |
63 */ | |
64 public function parseFile($filename, $flags = 0) | |
65 { | |
66 if (!is_file($filename)) { | |
67 throw new ParseException(sprintf('File "%s" does not exist.', $filename)); | |
68 } | |
69 | |
70 if (!is_readable($filename)) { | |
71 throw new ParseException(sprintf('File "%s" cannot be read.', $filename)); | |
72 } | |
73 | |
74 $this->filename = $filename; | |
75 | |
76 try { | |
77 return $this->parse(file_get_contents($filename), $flags); | |
78 } finally { | |
79 $this->filename = null; | |
80 } | |
47 } | 81 } |
48 | 82 |
49 /** | 83 /** |
50 * Parses a YAML string to a PHP value. | 84 * Parses a YAML string to a PHP value. |
51 * | 85 * |
57 * @throws ParseException If the YAML is not valid | 91 * @throws ParseException If the YAML is not valid |
58 */ | 92 */ |
59 public function parse($value, $flags = 0) | 93 public function parse($value, $flags = 0) |
60 { | 94 { |
61 if (is_bool($flags)) { | 95 if (is_bool($flags)) { |
62 @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); | 96 @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); |
63 | 97 |
64 if ($flags) { | 98 if ($flags) { |
65 $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; | 99 $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; |
66 } else { | 100 } else { |
67 $flags = 0; | 101 $flags = 0; |
68 } | 102 } |
69 } | 103 } |
70 | 104 |
71 if (func_num_args() >= 3) { | 105 if (func_num_args() >= 3) { |
72 @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); | 106 @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); |
73 | 107 |
74 if (func_get_arg(2)) { | 108 if (func_get_arg(2)) { |
75 $flags |= Yaml::PARSE_OBJECT; | 109 $flags |= Yaml::PARSE_OBJECT; |
76 } | 110 } |
77 } | 111 } |
78 | 112 |
79 if (func_num_args() >= 4) { | 113 if (func_num_args() >= 4) { |
80 @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); | 114 @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); |
81 | 115 |
82 if (func_get_arg(3)) { | 116 if (func_get_arg(3)) { |
83 $flags |= Yaml::PARSE_OBJECT_FOR_MAP; | 117 $flags |= Yaml::PARSE_OBJECT_FOR_MAP; |
84 } | 118 } |
85 } | 119 } |
86 | 120 |
121 if (Yaml::PARSE_KEYS_AS_STRINGS & $flags) { | |
122 @trigger_error('Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.', E_USER_DEPRECATED); | |
123 } | |
124 | |
87 if (false === preg_match('//u', $value)) { | 125 if (false === preg_match('//u', $value)) { |
88 throw new ParseException('The YAML value does not appear to be valid UTF-8.'); | 126 throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename); |
89 } | 127 } |
90 | 128 |
91 $this->refs = array(); | 129 $this->refs = array(); |
92 | 130 |
93 $mbEncoding = null; | 131 $mbEncoding = null; |
132 | 170 |
133 if (null === $this->totalNumberOfLines) { | 171 if (null === $this->totalNumberOfLines) { |
134 $this->totalNumberOfLines = count($this->lines); | 172 $this->totalNumberOfLines = count($this->lines); |
135 } | 173 } |
136 | 174 |
175 if (!$this->moveToNextLine()) { | |
176 return null; | |
177 } | |
178 | |
137 $data = array(); | 179 $data = array(); |
138 $context = null; | 180 $context = null; |
139 $allowOverwrite = false; | 181 $allowOverwrite = false; |
140 | 182 |
141 while ($this->moveToNextLine()) { | 183 while ($this->isCurrentLineEmpty()) { |
184 if (!$this->moveToNextLine()) { | |
185 return null; | |
186 } | |
187 } | |
188 | |
189 // Resolves the tag and returns if end of the document | |
190 if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) { | |
191 return new TaggedValue($tag, ''); | |
192 } | |
193 | |
194 do { | |
142 if ($this->isCurrentLineEmpty()) { | 195 if ($this->isCurrentLineEmpty()) { |
143 continue; | 196 continue; |
144 } | 197 } |
145 | 198 |
146 // tab? | 199 // tab? |
147 if ("\t" === $this->currentLine[0]) { | 200 if ("\t" === $this->currentLine[0]) { |
148 throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); | 201 throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); |
149 } | 202 } |
203 | |
204 Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename); | |
150 | 205 |
151 $isRef = $mergeNode = false; | 206 $isRef = $mergeNode = false; |
152 if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) { | 207 if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) { |
153 if ($context && 'mapping' == $context) { | 208 if ($context && 'mapping' == $context) { |
154 throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); | 209 throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); |
155 } | 210 } |
156 $context = 'sequence'; | 211 $context = 'sequence'; |
157 | 212 |
158 if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { | 213 if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { |
159 $isRef = $matches['ref']; | 214 $isRef = $matches['ref']; |
160 $values['value'] = $matches['value']; | 215 $values['value'] = $matches['value']; |
161 } | 216 } |
162 | 217 |
218 if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) { | |
219 @trigger_error($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'), E_USER_DEPRECATED); | |
220 } | |
221 | |
163 // array | 222 // array |
164 if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { | 223 if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { |
165 $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags); | 224 $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags); |
225 } elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) { | |
226 $data[] = new TaggedValue( | |
227 $subTag, | |
228 $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags) | |
229 ); | |
166 } else { | 230 } else { |
167 if (isset($values['leadspaces']) | 231 if (isset($values['leadspaces']) |
168 && self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($values['value']), $matches) | 232 && self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->trimTag($values['value']), $matches) |
169 ) { | 233 ) { |
170 // this is a compact notation element, add to next block and parse | 234 // this is a compact notation element, add to next block and parse |
171 $block = $values['value']; | 235 $block = $values['value']; |
172 if ($this->isNextLineIndented()) { | 236 if ($this->isNextLineIndented()) { |
173 $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1); | 237 $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1); |
180 } | 244 } |
181 if ($isRef) { | 245 if ($isRef) { |
182 $this->refs[$isRef] = end($data); | 246 $this->refs[$isRef] = end($data); |
183 } | 247 } |
184 } elseif ( | 248 } elseif ( |
185 self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($this->currentLine), $values) | 249 self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values) |
186 && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'"))) | 250 && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'"))) |
187 ) { | 251 ) { |
188 if ($context && 'sequence' == $context) { | 252 if ($context && 'sequence' == $context) { |
189 throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); | 253 throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine, $this->filename); |
190 } | 254 } |
191 $context = 'mapping'; | 255 $context = 'mapping'; |
192 | 256 |
193 // force correct settings | |
194 Inline::parse(null, $flags, $this->refs); | |
195 try { | 257 try { |
196 Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); | 258 $i = 0; |
197 $key = Inline::parseScalar($values['key']); | 259 $evaluateKey = !(Yaml::PARSE_KEYS_AS_STRINGS & $flags); |
260 | |
261 // constants in key will be evaluated anyway | |
262 if (isset($values['key'][0]) && '!' === $values['key'][0] && Yaml::PARSE_CONSTANT & $flags) { | |
263 $evaluateKey = true; | |
264 } | |
265 | |
266 $key = Inline::parseScalar($values['key'], 0, null, $i, $evaluateKey); | |
198 } catch (ParseException $e) { | 267 } catch (ParseException $e) { |
199 $e->setParsedLine($this->getRealCurrentLineNb() + 1); | 268 $e->setParsedLine($this->getRealCurrentLineNb() + 1); |
200 $e->setSnippet($this->currentLine); | 269 $e->setSnippet($this->currentLine); |
201 | 270 |
202 throw $e; | 271 throw $e; |
203 } | 272 } |
204 | 273 |
274 if (!is_string($key) && !is_int($key)) { | |
275 $keyType = is_numeric($key) ? 'numeric key' : 'non-string key'; | |
276 @trigger_error($this->getDeprecationMessage(sprintf('Implicit casting of %s to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.', $keyType)), E_USER_DEPRECATED); | |
277 } | |
278 | |
205 // Convert float keys to strings, to avoid being converted to integers by PHP | 279 // Convert float keys to strings, to avoid being converted to integers by PHP |
206 if (is_float($key)) { | 280 if (is_float($key)) { |
207 $key = (string) $key; | 281 $key = (string) $key; |
208 } | 282 } |
209 | 283 |
210 if ('<<' === $key) { | 284 if ('<<' === $key && (!isset($values['value']) || !self::preg_match('#^&(?P<ref>[^ ]+)#u', $values['value'], $refMatches))) { |
211 $mergeNode = true; | 285 $mergeNode = true; |
212 $allowOverwrite = true; | 286 $allowOverwrite = true; |
213 if (isset($values['value']) && 0 === strpos($values['value'], '*')) { | 287 if (isset($values['value'][0]) && '*' === $values['value'][0]) { |
214 $refName = substr($values['value'], 1); | 288 $refName = substr(rtrim($values['value']), 1); |
215 if (!array_key_exists($refName, $this->refs)) { | 289 if (!array_key_exists($refName, $this->refs)) { |
216 throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine); | 290 throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); |
217 } | 291 } |
218 | 292 |
219 $refValue = $this->refs[$refName]; | 293 $refValue = $this->refs[$refName]; |
220 | 294 |
295 if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) { | |
296 $refValue = (array) $refValue; | |
297 } | |
298 | |
221 if (!is_array($refValue)) { | 299 if (!is_array($refValue)) { |
222 throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); | 300 throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); |
223 } | 301 } |
224 | 302 |
225 $data += $refValue; // array union | 303 $data += $refValue; // array union |
226 } else { | 304 } else { |
227 if (isset($values['value']) && $values['value'] !== '') { | 305 if (isset($values['value']) && '' !== $values['value']) { |
228 $value = $values['value']; | 306 $value = $values['value']; |
229 } else { | 307 } else { |
230 $value = $this->getNextEmbedBlock(); | 308 $value = $this->getNextEmbedBlock(); |
231 } | 309 } |
232 $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags); | 310 $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags); |
233 | 311 |
312 if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) { | |
313 $parsed = (array) $parsed; | |
314 } | |
315 | |
234 if (!is_array($parsed)) { | 316 if (!is_array($parsed)) { |
235 throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); | 317 throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); |
236 } | 318 } |
237 | 319 |
238 if (isset($parsed[0])) { | 320 if (isset($parsed[0])) { |
239 // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes | 321 // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes |
240 // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier | 322 // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier |
241 // in the sequence override keys specified in later mapping nodes. | 323 // in the sequence override keys specified in later mapping nodes. |
242 foreach ($parsed as $parsedItem) { | 324 foreach ($parsed as $parsedItem) { |
325 if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) { | |
326 $parsedItem = (array) $parsedItem; | |
327 } | |
328 | |
243 if (!is_array($parsedItem)) { | 329 if (!is_array($parsedItem)) { |
244 throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); | 330 throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename); |
245 } | 331 } |
246 | 332 |
247 $data += $parsedItem; // array union | 333 $data += $parsedItem; // array union |
248 } | 334 } |
249 } else { | 335 } else { |
250 // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the | 336 // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the |
251 // current mapping, unless the key already exists in it. | 337 // current mapping, unless the key already exists in it. |
252 $data += $parsed; // array union | 338 $data += $parsed; // array union |
253 } | 339 } |
254 } | 340 } |
255 } elseif (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { | 341 } elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) { |
256 $isRef = $matches['ref']; | 342 $isRef = $matches['ref']; |
257 $values['value'] = $matches['value']; | 343 $values['value'] = $matches['value']; |
258 } | 344 } |
259 | 345 |
346 $subTag = null; | |
260 if ($mergeNode) { | 347 if ($mergeNode) { |
261 // Merge keys | 348 // Merge keys |
262 } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { | 349 } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { |
263 // hash | 350 // hash |
264 // if next line is less indented or equal, then it means that the current value is null | 351 // if next line is less indented or equal, then it means that the current value is null |
265 if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { | 352 if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { |
266 // Spec: Keys MUST be unique; first one wins. | 353 // Spec: Keys MUST be unique; first one wins. |
267 // But overwriting is allowed when a merge node is used in current block. | 354 // But overwriting is allowed when a merge node is used in current block. |
268 if ($allowOverwrite || !isset($data[$key])) { | 355 if ($allowOverwrite || !isset($data[$key])) { |
269 $data[$key] = null; | 356 if (null !== $subTag) { |
357 $data[$key] = new TaggedValue($subTag, ''); | |
358 } else { | |
359 $data[$key] = null; | |
360 } | |
270 } else { | 361 } else { |
271 @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); | 362 @trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); |
272 } | 363 } |
273 } else { | 364 } else { |
274 // remember the parsed line number here in case we need it to provide some contexts in error messages below | |
275 $realCurrentLineNbKey = $this->getRealCurrentLineNb(); | |
276 $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); | 365 $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); |
277 // Spec: Keys MUST be unique; first one wins. | 366 if ('<<' === $key) { |
278 // But overwriting is allowed when a merge node is used in current block. | 367 $this->refs[$refMatches['ref']] = $value; |
279 if ($allowOverwrite || !isset($data[$key])) { | 368 |
280 $data[$key] = $value; | 369 if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) { |
370 $value = (array) $value; | |
371 } | |
372 | |
373 $data += $value; | |
374 } elseif ($allowOverwrite || !isset($data[$key])) { | |
375 // Spec: Keys MUST be unique; first one wins. | |
376 // But overwriting is allowed when a merge node is used in current block. | |
377 if (null !== $subTag) { | |
378 $data[$key] = new TaggedValue($subTag, $value); | |
379 } else { | |
380 $data[$key] = $value; | |
381 } | |
281 } else { | 382 } else { |
282 @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $realCurrentLineNbKey + 1), E_USER_DEPRECATED); | 383 @trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); |
283 } | 384 } |
284 } | 385 } |
285 } else { | 386 } else { |
286 $value = $this->parseValue($values['value'], $flags, $context); | 387 $value = $this->parseValue(rtrim($values['value']), $flags, $context); |
287 // Spec: Keys MUST be unique; first one wins. | 388 // Spec: Keys MUST be unique; first one wins. |
288 // But overwriting is allowed when a merge node is used in current block. | 389 // But overwriting is allowed when a merge node is used in current block. |
289 if ($allowOverwrite || !isset($data[$key])) { | 390 if ($allowOverwrite || !isset($data[$key])) { |
290 $data[$key] = $value; | 391 $data[$key] = $value; |
291 } else { | 392 } else { |
292 @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); | 393 @trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED); |
293 } | 394 } |
294 } | 395 } |
295 if ($isRef) { | 396 if ($isRef) { |
296 $this->refs[$isRef] = $data[$key]; | 397 $this->refs[$isRef] = $data[$key]; |
297 } | 398 } |
298 } else { | 399 } else { |
299 // multiple documents are not supported | 400 // multiple documents are not supported |
300 if ('---' === $this->currentLine) { | 401 if ('---' === $this->currentLine) { |
301 throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine); | 402 throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename); |
403 } | |
404 | |
405 if ($deprecatedUsage = (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1])) { | |
406 @trigger_error($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'), E_USER_DEPRECATED); | |
302 } | 407 } |
303 | 408 |
304 // 1-liner optionally followed by newline(s) | 409 // 1-liner optionally followed by newline(s) |
305 if (is_string($value) && $this->lines[0] === trim($value)) { | 410 if (is_string($value) && $this->lines[0] === trim($value)) { |
306 try { | 411 try { |
307 Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); | |
308 $value = Inline::parse($this->lines[0], $flags, $this->refs); | 412 $value = Inline::parse($this->lines[0], $flags, $this->refs); |
309 } catch (ParseException $e) { | 413 } catch (ParseException $e) { |
310 $e->setParsedLine($this->getRealCurrentLineNb() + 1); | 414 $e->setParsedLine($this->getRealCurrentLineNb() + 1); |
311 $e->setSnippet($this->currentLine); | 415 $e->setSnippet($this->currentLine); |
312 | 416 |
314 } | 418 } |
315 | 419 |
316 return $value; | 420 return $value; |
317 } | 421 } |
318 | 422 |
319 throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine); | 423 // try to parse the value as a multi-line string as a last resort |
320 } | 424 if (0 === $this->currentLineNb) { |
425 $previousLineWasNewline = false; | |
426 $previousLineWasTerminatedWithBackslash = false; | |
427 $value = ''; | |
428 | |
429 foreach ($this->lines as $line) { | |
430 // If the indentation is not consistent at offset 0, it is to be considered as a ParseError | |
431 if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) { | |
432 throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); | |
433 } | |
434 if ('' === trim($line)) { | |
435 $value .= "\n"; | |
436 } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { | |
437 $value .= ' '; | |
438 } | |
439 | |
440 if ('' !== trim($line) && '\\' === substr($line, -1)) { | |
441 $value .= ltrim(substr($line, 0, -1)); | |
442 } elseif ('' !== trim($line)) { | |
443 $value .= trim($line); | |
444 } | |
445 | |
446 if ('' === trim($line)) { | |
447 $previousLineWasNewline = true; | |
448 $previousLineWasTerminatedWithBackslash = false; | |
449 } elseif ('\\' === substr($line, -1)) { | |
450 $previousLineWasNewline = false; | |
451 $previousLineWasTerminatedWithBackslash = true; | |
452 } else { | |
453 $previousLineWasNewline = false; | |
454 $previousLineWasTerminatedWithBackslash = false; | |
455 } | |
456 } | |
457 | |
458 try { | |
459 return Inline::parse(trim($value)); | |
460 } catch (ParseException $e) { | |
461 // fall-through to the ParseException thrown below | |
462 } | |
463 } | |
464 | |
465 throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); | |
466 } | |
467 } while ($this->moveToNextLine()); | |
468 | |
469 if (null !== $tag) { | |
470 $data = new TaggedValue($tag, $data); | |
321 } | 471 } |
322 | 472 |
323 if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) { | 473 if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) { |
324 $object = new \stdClass(); | 474 $object = new \stdClass(); |
325 | 475 |
343 } | 493 } |
344 | 494 |
345 $skippedLineNumbers[] = $lineNumber; | 495 $skippedLineNumbers[] = $lineNumber; |
346 } | 496 } |
347 | 497 |
348 $parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers); | 498 $parser = new self(); |
499 $parser->offset = $offset; | |
500 $parser->totalNumberOfLines = $this->totalNumberOfLines; | |
501 $parser->skippedLineNumbers = $skippedLineNumbers; | |
349 $parser->refs = &$this->refs; | 502 $parser->refs = &$this->refs; |
350 | 503 |
351 return $parser->doParse($yaml, $flags); | 504 return $parser->doParse($yaml, $flags); |
352 } | 505 } |
353 | 506 |
354 /** | 507 /** |
355 * Returns the current line number (takes the offset into account). | 508 * Returns the current line number (takes the offset into account). |
356 * | 509 * |
510 * @internal | |
511 * | |
357 * @return int The current line number | 512 * @return int The current line number |
358 */ | 513 */ |
359 private function getRealCurrentLineNb() | 514 public function getRealCurrentLineNb() |
360 { | 515 { |
361 $realCurrentLineNumber = $this->currentLineNb + $this->offset; | 516 $realCurrentLineNumber = $this->currentLineNb + $this->offset; |
362 | 517 |
363 foreach ($this->skippedLineNumbers as $skippedLineNumber) { | 518 foreach ($this->skippedLineNumbers as $skippedLineNumber) { |
364 if ($skippedLineNumber > $realCurrentLineNumber) { | 519 if ($skippedLineNumber > $realCurrentLineNumber) { |
395 { | 550 { |
396 $oldLineIndentation = $this->getCurrentLineIndentation(); | 551 $oldLineIndentation = $this->getCurrentLineIndentation(); |
397 $blockScalarIndentations = array(); | 552 $blockScalarIndentations = array(); |
398 | 553 |
399 if ($this->isBlockScalarHeader()) { | 554 if ($this->isBlockScalarHeader()) { |
400 $blockScalarIndentations[] = $this->getCurrentLineIndentation(); | 555 $blockScalarIndentations[] = $oldLineIndentation; |
401 } | 556 } |
402 | 557 |
403 if (!$this->moveToNextLine()) { | 558 if (!$this->moveToNextLine()) { |
404 return; | 559 return; |
405 } | 560 } |
406 | 561 |
407 if (null === $indentation) { | 562 if (null === $indentation) { |
408 $newIndent = $this->getCurrentLineIndentation(); | 563 $newIndent = null; |
564 $movements = 0; | |
565 | |
566 do { | |
567 $EOF = false; | |
568 | |
569 // empty and comment-like lines do not influence the indentation depth | |
570 if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) { | |
571 $EOF = !$this->moveToNextLine(); | |
572 | |
573 if (!$EOF) { | |
574 ++$movements; | |
575 } | |
576 } else { | |
577 $newIndent = $this->getCurrentLineIndentation(); | |
578 } | |
579 } while (!$EOF && null === $newIndent); | |
580 | |
581 for ($i = 0; $i < $movements; ++$i) { | |
582 $this->moveToPreviousLine(); | |
583 } | |
409 | 584 |
410 $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); | 585 $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); |
411 | 586 |
412 if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { | 587 if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { |
413 throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); | 588 throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); |
414 } | 589 } |
415 } else { | 590 } else { |
416 $newIndent = $indentation; | 591 $newIndent = $indentation; |
417 } | 592 } |
418 | 593 |
419 $data = array(); | 594 $data = array(); |
420 if ($this->getCurrentLineIndentation() >= $newIndent) { | 595 if ($this->getCurrentLineIndentation() >= $newIndent) { |
421 $data[] = substr($this->currentLine, $newIndent); | 596 $data[] = substr($this->currentLine, $newIndent); |
597 } elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) { | |
598 $data[] = $this->currentLine; | |
422 } else { | 599 } else { |
423 $this->moveToPreviousLine(); | 600 $this->moveToPreviousLine(); |
424 | 601 |
425 return; | 602 return; |
426 } | 603 } |
443 | 620 |
444 while ($this->moveToNextLine()) { | 621 while ($this->moveToNextLine()) { |
445 $indent = $this->getCurrentLineIndentation(); | 622 $indent = $this->getCurrentLineIndentation(); |
446 | 623 |
447 // terminate all block scalars that are more indented than the current line | 624 // terminate all block scalars that are more indented than the current line |
448 if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && trim($this->currentLine) !== '') { | 625 if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && '' !== trim($this->currentLine)) { |
449 foreach ($blockScalarIndentations as $key => $blockScalarIndentation) { | 626 foreach ($blockScalarIndentations as $key => $blockScalarIndentation) { |
450 if ($blockScalarIndentation >= $this->getCurrentLineIndentation()) { | 627 if ($blockScalarIndentation >= $indent) { |
451 unset($blockScalarIndentations[$key]); | 628 unset($blockScalarIndentations[$key]); |
452 } | 629 } |
453 } | 630 } |
454 } | 631 } |
455 | 632 |
456 if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) { | 633 if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) { |
457 $blockScalarIndentations[] = $this->getCurrentLineIndentation(); | 634 $blockScalarIndentations[] = $indent; |
458 } | 635 } |
459 | 636 |
460 $previousLineIndentation = $indent; | 637 $previousLineIndentation = $indent; |
461 | 638 |
462 if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { | 639 if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { |
467 if ($this->isCurrentLineBlank()) { | 644 if ($this->isCurrentLineBlank()) { |
468 $data[] = substr($this->currentLine, $newIndent); | 645 $data[] = substr($this->currentLine, $newIndent); |
469 continue; | 646 continue; |
470 } | 647 } |
471 | 648 |
472 // we ignore "comment" lines only when we are not inside a scalar block | |
473 if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) { | |
474 // remember ignored comment lines (they are used later in nested | |
475 // parser calls to determine real line numbers) | |
476 // | |
477 // CAUTION: beware to not populate the global property here as it | |
478 // will otherwise influence the getRealCurrentLineNb() call here | |
479 // for consecutive comment lines and subsequent embedded blocks | |
480 $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb(); | |
481 | |
482 continue; | |
483 } | |
484 | |
485 if ($indent >= $newIndent) { | 649 if ($indent >= $newIndent) { |
486 $data[] = substr($this->currentLine, $newIndent); | 650 $data[] = substr($this->currentLine, $newIndent); |
651 } elseif ($this->isCurrentLineComment()) { | |
652 $data[] = $this->currentLine; | |
487 } elseif (0 == $indent) { | 653 } elseif (0 == $indent) { |
488 $this->moveToPreviousLine(); | 654 $this->moveToPreviousLine(); |
489 | 655 |
490 break; | 656 break; |
491 } else { | 657 } else { |
492 throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); | 658 throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); |
493 } | 659 } |
494 } | 660 } |
495 | 661 |
496 return implode("\n", $data); | 662 return implode("\n", $data); |
497 } | 663 } |
547 } else { | 713 } else { |
548 $value = substr($value, 1); | 714 $value = substr($value, 1); |
549 } | 715 } |
550 | 716 |
551 if (!array_key_exists($value, $this->refs)) { | 717 if (!array_key_exists($value, $this->refs)) { |
552 throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine); | 718 throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); |
553 } | 719 } |
554 | 720 |
555 return $this->refs[$value]; | 721 return $this->refs[$value]; |
556 } | 722 } |
557 | 723 |
558 if (self::preg_match('/^'.self::TAG_PATTERN.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { | 724 if (self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { |
559 $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; | 725 $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; |
560 | 726 |
561 $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); | 727 $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); |
562 | 728 |
563 if (isset($matches['tag']) && '!!binary' === $matches['tag']) { | 729 if ('' !== $matches['tag']) { |
564 return Inline::evaluateBinaryScalar($data); | 730 if ('!!binary' === $matches['tag']) { |
731 return Inline::evaluateBinaryScalar($data); | |
732 } elseif ('tagged' === $matches['tag']) { | |
733 return new TaggedValue(substr($matches['tag'], 1), $data); | |
734 } elseif ('!' !== $matches['tag']) { | |
735 @trigger_error($this->getDeprecationMessage(sprintf('Using the custom tag "%s" for the value "%s" is deprecated since Symfony 3.3. It will be replaced by an instance of %s in 4.0.', $matches['tag'], $data, TaggedValue::class)), E_USER_DEPRECATED); | |
736 } | |
565 } | 737 } |
566 | 738 |
567 return $data; | 739 return $data; |
568 } | 740 } |
569 | 741 |
570 try { | 742 try { |
571 $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null; | 743 $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null; |
572 | 744 |
573 // do not take following lines into account when the current line is a quoted single line value | 745 // do not take following lines into account when the current line is a quoted single line value |
574 if (null !== $quotation && preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) { | 746 if (null !== $quotation && self::preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) { |
575 return Inline::parse($value, $flags, $this->refs); | 747 return Inline::parse($value, $flags, $this->refs); |
576 } | 748 } |
749 | |
750 $lines = array(); | |
577 | 751 |
578 while ($this->moveToNextLine()) { | 752 while ($this->moveToNextLine()) { |
579 // unquoted strings end before the first unindented line | 753 // unquoted strings end before the first unindented line |
580 if (null === $quotation && $this->getCurrentLineIndentation() === 0) { | 754 if (null === $quotation && 0 === $this->getCurrentLineIndentation()) { |
581 $this->moveToPreviousLine(); | 755 $this->moveToPreviousLine(); |
582 | 756 |
583 break; | 757 break; |
584 } | 758 } |
585 | 759 |
586 $value .= ' '.trim($this->currentLine); | 760 $lines[] = trim($this->currentLine); |
587 | 761 |
588 // quoted string values end with a line that is terminated with the quotation character | 762 // quoted string values end with a line that is terminated with the quotation character |
589 if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) { | 763 if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) { |
590 break; | 764 break; |
591 } | 765 } |
592 } | 766 } |
593 | 767 |
768 for ($i = 0, $linesCount = count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { | |
769 if ('' === $lines[$i]) { | |
770 $value .= "\n"; | |
771 $previousLineBlank = true; | |
772 } elseif ($previousLineBlank) { | |
773 $value .= $lines[$i]; | |
774 $previousLineBlank = false; | |
775 } else { | |
776 $value .= ' '.$lines[$i]; | |
777 $previousLineBlank = false; | |
778 } | |
779 } | |
780 | |
594 Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); | 781 Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); |
782 | |
595 $parsedValue = Inline::parse($value, $flags, $this->refs); | 783 $parsedValue = Inline::parse($value, $flags, $this->refs); |
596 | 784 |
597 if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { | 785 if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { |
598 throw new ParseException('A colon cannot be used in an unquoted mapping value.'); | 786 throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename); |
599 } | 787 } |
600 | 788 |
601 return $parsedValue; | 789 return $parsedValue; |
602 } catch (ParseException $e) { | 790 } catch (ParseException $e) { |
603 $e->setParsedLine($this->getRealCurrentLineNb() + 1); | 791 $e->setParsedLine($this->getRealCurrentLineNb() + 1); |
724 * @return bool Returns true if the next line is indented, false otherwise | 912 * @return bool Returns true if the next line is indented, false otherwise |
725 */ | 913 */ |
726 private function isNextLineIndented() | 914 private function isNextLineIndented() |
727 { | 915 { |
728 $currentIndentation = $this->getCurrentLineIndentation(); | 916 $currentIndentation = $this->getCurrentLineIndentation(); |
729 $EOF = !$this->moveToNextLine(); | 917 $movements = 0; |
730 | 918 |
731 while (!$EOF && $this->isCurrentLineEmpty()) { | 919 do { |
732 $EOF = !$this->moveToNextLine(); | 920 $EOF = !$this->moveToNextLine(); |
733 } | 921 |
922 if (!$EOF) { | |
923 ++$movements; | |
924 } | |
925 } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment())); | |
734 | 926 |
735 if ($EOF) { | 927 if ($EOF) { |
736 return false; | 928 return false; |
737 } | 929 } |
738 | 930 |
739 $ret = $this->getCurrentLineIndentation() > $currentIndentation; | 931 $ret = $this->getCurrentLineIndentation() > $currentIndentation; |
740 | 932 |
741 $this->moveToPreviousLine(); | 933 for ($i = 0; $i < $movements; ++$i) { |
934 $this->moveToPreviousLine(); | |
935 } | |
742 | 936 |
743 return $ret; | 937 return $ret; |
744 } | 938 } |
745 | 939 |
746 /** | 940 /** |
771 private function isCurrentLineComment() | 965 private function isCurrentLineComment() |
772 { | 966 { |
773 //checking explicitly the first char of the trim is faster than loops or strpos | 967 //checking explicitly the first char of the trim is faster than loops or strpos |
774 $ltrimmedLine = ltrim($this->currentLine, ' '); | 968 $ltrimmedLine = ltrim($this->currentLine, ' '); |
775 | 969 |
776 return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#'; | 970 return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; |
777 } | 971 } |
778 | 972 |
779 private function isCurrentLineLastLineInDocument() | 973 private function isCurrentLineLastLineInDocument() |
780 { | 974 { |
781 return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); | 975 return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); |
797 $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); | 991 $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); |
798 $this->offset += $count; | 992 $this->offset += $count; |
799 | 993 |
800 // remove leading comments | 994 // remove leading comments |
801 $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); | 995 $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); |
802 if ($count == 1) { | 996 if (1 === $count) { |
803 // items have been removed, update the offset | 997 // items have been removed, update the offset |
804 $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); | 998 $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); |
805 $value = $trimmedValue; | 999 $value = $trimmedValue; |
806 } | 1000 } |
807 | 1001 |
808 // remove start of the document marker (---) | 1002 // remove start of the document marker (---) |
809 $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); | 1003 $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); |
810 if ($count == 1) { | 1004 if (1 === $count) { |
811 // items have been removed, update the offset | 1005 // items have been removed, update the offset |
812 $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); | 1006 $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); |
813 $value = $trimmedValue; | 1007 $value = $trimmedValue; |
814 | 1008 |
815 // remove end of the document marker (...) | 1009 // remove end of the document marker (...) |
825 * @return bool Returns true if the next line starts unindented collection, false otherwise | 1019 * @return bool Returns true if the next line starts unindented collection, false otherwise |
826 */ | 1020 */ |
827 private function isNextLineUnIndentedCollection() | 1021 private function isNextLineUnIndentedCollection() |
828 { | 1022 { |
829 $currentIndentation = $this->getCurrentLineIndentation(); | 1023 $currentIndentation = $this->getCurrentLineIndentation(); |
830 $notEOF = $this->moveToNextLine(); | 1024 $movements = 0; |
831 | 1025 |
832 while ($notEOF && $this->isCurrentLineEmpty()) { | 1026 do { |
833 $notEOF = $this->moveToNextLine(); | 1027 $EOF = !$this->moveToNextLine(); |
834 } | 1028 |
835 | 1029 if (!$EOF) { |
836 if (false === $notEOF) { | 1030 ++$movements; |
1031 } | |
1032 } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment())); | |
1033 | |
1034 if ($EOF) { | |
837 return false; | 1035 return false; |
838 } | 1036 } |
839 | 1037 |
840 $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); | 1038 $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); |
841 | 1039 |
842 $this->moveToPreviousLine(); | 1040 for ($i = 0; $i < $movements; ++$i) { |
1041 $this->moveToPreviousLine(); | |
1042 } | |
843 | 1043 |
844 return $ret; | 1044 return $ret; |
845 } | 1045 } |
846 | 1046 |
847 /** | 1047 /** |
903 throw new ParseException($error); | 1103 throw new ParseException($error); |
904 } | 1104 } |
905 | 1105 |
906 return $ret; | 1106 return $ret; |
907 } | 1107 } |
1108 | |
1109 /** | |
1110 * Trim the tag on top of the value. | |
1111 * | |
1112 * Prevent values such as `!foo {quz: bar}` to be considered as | |
1113 * a mapping block. | |
1114 */ | |
1115 private function trimTag($value) | |
1116 { | |
1117 if ('!' === $value[0]) { | |
1118 return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' '); | |
1119 } | |
1120 | |
1121 return $value; | |
1122 } | |
1123 | |
1124 private function getLineTag($value, $flags, $nextLineCheck = true) | |
1125 { | |
1126 if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) { | |
1127 return; | |
1128 } | |
1129 | |
1130 if ($nextLineCheck && !$this->isNextLineIndented()) { | |
1131 return; | |
1132 } | |
1133 | |
1134 $tag = substr($matches['tag'], 1); | |
1135 | |
1136 // Built-in tags | |
1137 if ($tag && '!' === $tag[0]) { | |
1138 throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename); | |
1139 } | |
1140 | |
1141 if (Yaml::PARSE_CUSTOM_TAGS & $flags) { | |
1142 return $tag; | |
1143 } | |
1144 | |
1145 throw new ParseException(sprintf('Tags support is not enabled. You must use the flag `Yaml::PARSE_CUSTOM_TAGS` to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename); | |
1146 } | |
1147 | |
1148 private function getDeprecationMessage($message) | |
1149 { | |
1150 $message = rtrim($message, '.'); | |
1151 | |
1152 if (null !== $this->filename) { | |
1153 $message .= ' in '.$this->filename; | |
1154 } | |
1155 | |
1156 $message .= ' on line '.($this->getRealCurrentLineNb() + 1); | |
1157 | |
1158 return $message.'.'; | |
1159 } | |
908 } | 1160 } |