@@ -30,17 +30,21 @@ class Parser
30
30
private $ currentLineNb = -1 ;
31
31
private $ currentLine = '' ;
32
32
private $ refs = array ();
33
+ private $ skippedLineNumbers = array ();
34
+ private $ locallySkippedLineNumbers = array ();
33
35
34
36
/**
35
37
* Constructor.
36
38
*
37
39
* @param int $offset The offset of YAML document (used for line numbers in error messages)
38
40
* @param int|null $totalNumberOfLines The overall number of lines being parsed
41
+ * @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser
39
42
*/
40
- public function __construct ($ offset = 0 , $ totalNumberOfLines = null )
43
+ public function __construct ($ offset = 0 , $ totalNumberOfLines = null , array $ skippedLineNumbers = array () )
41
44
{
42
45
$ this ->offset = $ offset ;
43
46
$ this ->totalNumberOfLines = $ totalNumberOfLines ;
47
+ $ this ->skippedLineNumbers = $ skippedLineNumbers ;
44
48
}
45
49
46
50
/**
@@ -101,25 +105,18 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
101
105
102
106
// array
103
107
if (!isset ($ values ['value ' ]) || '' == trim ($ values ['value ' ], ' ' ) || 0 === strpos (ltrim ($ values ['value ' ], ' ' ), '# ' )) {
104
- $ c = $ this ->getRealCurrentLineNb () + 1 ;
105
- $ parser = new self ($ c , $ this ->totalNumberOfLines );
106
- $ parser ->refs = &$ this ->refs ;
107
- $ data [] = $ parser ->parse ($ this ->getNextEmbedBlock (null , true ), $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
108
+ $ data [] = $ this ->parseBlock ($ this ->getRealCurrentLineNb () + 1 , $ this ->getNextEmbedBlock (null , true ), $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
108
109
} else {
109
110
if (isset ($ values ['leadspaces ' ])
110
111
&& preg_match ('#^(?P<key> ' .Inline::REGEX_QUOTED_STRING .'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u ' , $ values ['value ' ], $ matches )
111
112
) {
112
113
// this is a compact notation element, add to next block and parse
113
- $ c = $ this ->getRealCurrentLineNb ();
114
- $ parser = new self ($ c , $ this ->totalNumberOfLines );
115
- $ parser ->refs = &$ this ->refs ;
116
-
117
114
$ block = $ values ['value ' ];
118
115
if ($ this ->isNextLineIndented ()) {
119
116
$ block .= "\n" .$ this ->getNextEmbedBlock ($ this ->getCurrentLineIndentation () + strlen ($ values ['leadspaces ' ]) + 1 );
120
117
}
121
118
122
- $ data [] = $ parser -> parse ( $ block , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
119
+ $ data [] = $ this -> parseBlock ( $ this -> getRealCurrentLineNb (), $ block , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
123
120
} else {
124
121
$ data [] = $ this ->parseValue ($ values ['value ' ], $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
125
122
}
@@ -175,10 +172,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
175
172
} else {
176
173
$ value = $ this ->getNextEmbedBlock ();
177
174
}
178
- $ c = $ this ->getRealCurrentLineNb () + 1 ;
179
- $ parser = new self ($ c , $ this ->totalNumberOfLines );
180
- $ parser ->refs = &$ this ->refs ;
181
- $ parsed = $ parser ->parse ($ value , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
175
+ $ parsed = $ this ->parseBlock ($ this ->getRealCurrentLineNb () + 1 , $ value , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
182
176
183
177
if (!is_array ($ parsed )) {
184
178
throw new ParseException ('YAML merge keys used with a scalar value instead of an array. ' , $ this ->getRealCurrentLineNb () + 1 , $ this ->currentLine );
@@ -226,10 +220,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
226
220
$ data [$ key ] = null ;
227
221
}
228
222
} else {
229
- $ c = $ this ->getRealCurrentLineNb () + 1 ;
230
- $ parser = new self ($ c , $ this ->totalNumberOfLines );
231
- $ parser ->refs = &$ this ->refs ;
232
- $ value = $ parser ->parse ($ this ->getNextEmbedBlock (), $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
223
+ $ value = $ this ->parseBlock ($ this ->getRealCurrentLineNb () + 1 , $ this ->getNextEmbedBlock (), $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
233
224
// Spec: Keys MUST be unique; first one wins.
234
225
// But overwriting is allowed when a merge node is used in current block.
235
226
if ($ allowOverwrite || !isset ($ data [$ key ])) {
@@ -317,14 +308,42 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
317
308
return empty ($ data ) ? null : $ data ;
318
309
}
319
310
311
+ private function parseBlock ($ offset , $ yaml , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap )
312
+ {
313
+ $ skippedLineNumbers = $ this ->skippedLineNumbers ;
314
+
315
+ foreach ($ this ->locallySkippedLineNumbers as $ lineNumber ) {
316
+ if ($ lineNumber < $ offset ) {
317
+ continue ;
318
+ }
319
+
320
+ $ skippedLineNumbers [] = $ lineNumber ;
321
+ }
322
+
323
+ $ parser = new self ($ offset , $ this ->totalNumberOfLines , $ skippedLineNumbers );
324
+ $ parser ->refs = &$ this ->refs ;
325
+
326
+ return $ parser ->parse ($ yaml , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
327
+ }
328
+
320
329
/**
321
330
* Returns the current line number (takes the offset into account).
322
331
*
323
332
* @return int The current line number
324
333
*/
325
334
private function getRealCurrentLineNb ()
326
335
{
327
- return $ this ->currentLineNb + $ this ->offset ;
336
+ $ realCurrentLineNumber = $ this ->currentLineNb + $ this ->offset ;
337
+
338
+ foreach ($ this ->skippedLineNumbers as $ skippedLineNumber ) {
339
+ if ($ skippedLineNumber > $ realCurrentLineNumber ) {
340
+ break ;
341
+ }
342
+
343
+ ++$ realCurrentLineNumber ;
344
+ }
345
+
346
+ return $ realCurrentLineNumber ;
328
347
}
329
348
330
349
/**
@@ -426,7 +445,15 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false)
426
445
}
427
446
428
447
// we ignore "comment" lines only when we are not inside a scalar block
429
- if (empty ($ blockScalarIndentations ) && $ this ->isCurrentLineComment () && false === $ this ->checkIfPreviousNonCommentLineIsCollectionItem ()) {
448
+ if (empty ($ blockScalarIndentations ) && $ this ->isCurrentLineComment ()) {
449
+ // remember ignored comment lines (they are used later in nested
450
+ // parser calls to determine real line numbers)
451
+ //
452
+ // CAUTION: beware to not populate the global property here as it
453
+ // will otherwise influence the getRealCurrentLineNb() call here
454
+ // for consecutive comment lines and subsequent embedded blocks
455
+ $ this ->locallySkippedLineNumbers [] = $ this ->getRealCurrentLineNb ();
456
+
430
457
continue ;
431
458
}
432
459
@@ -786,44 +813,4 @@ private function isBlockScalarHeader()
786
813
{
787
814
return (bool ) preg_match ('~ ' .self ::BLOCK_SCALAR_HEADER_PATTERN .'$~ ' , $ this ->currentLine );
788
815
}
789
-
790
- /**
791
- * Returns true if the current line is a collection item.
792
- *
793
- * @return bool
794
- */
795
- private function isCurrentLineCollectionItem ()
796
- {
797
- $ ltrimmedLine = ltrim ($ this ->currentLine , ' ' );
798
-
799
- return '' !== $ ltrimmedLine && '- ' === $ ltrimmedLine [0 ];
800
- }
801
-
802
- /**
803
- * Tests whether the current comment line is in a collection.
804
- *
805
- * @return bool
806
- */
807
- private function checkIfPreviousNonCommentLineIsCollectionItem ()
808
- {
809
- $ isCollectionItem = false ;
810
- $ moves = 0 ;
811
- while ($ this ->moveToPreviousLine ()) {
812
- ++$ moves ;
813
- // If previous line is a comment, move back again.
814
- if ($ this ->isCurrentLineComment ()) {
815
- continue ;
816
- }
817
- $ isCollectionItem = $ this ->isCurrentLineCollectionItem ();
818
- break ;
819
- }
820
-
821
- // Move parser back to previous line.
822
- while ($ moves > 0 ) {
823
- $ this ->moveToNextLine ();
824
- --$ moves ;
825
- }
826
-
827
- return $ isCollectionItem ;
828
- }
829
816
}
0 commit comments