@@ -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 , $ context );
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 ])) {
@@ -323,14 +314,42 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
323
314
return empty ($ data ) ? null : $ data ;
324
315
}
325
316
317
+ private function parseBlock ($ offset , $ yaml , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap )
318
+ {
319
+ $ skippedLineNumbers = $ this ->skippedLineNumbers ;
320
+
321
+ foreach ($ this ->locallySkippedLineNumbers as $ lineNumber ) {
322
+ if ($ lineNumber < $ offset ) {
323
+ continue ;
324
+ }
325
+
326
+ $ skippedLineNumbers [] = $ lineNumber ;
327
+ }
328
+
329
+ $ parser = new self ($ offset , $ this ->totalNumberOfLines , $ skippedLineNumbers );
330
+ $ parser ->refs = &$ this ->refs ;
331
+
332
+ return $ parser ->parse ($ yaml , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
333
+ }
334
+
326
335
/**
327
336
* Returns the current line number (takes the offset into account).
328
337
*
329
338
* @return int The current line number
330
339
*/
331
340
private function getRealCurrentLineNb ()
332
341
{
333
- return $ this ->currentLineNb + $ this ->offset ;
342
+ $ realCurrentLineNumber = $ this ->currentLineNb + $ this ->offset ;
343
+
344
+ foreach ($ this ->skippedLineNumbers as $ skippedLineNumber ) {
345
+ if ($ skippedLineNumber > $ realCurrentLineNumber ) {
346
+ break ;
347
+ }
348
+
349
+ ++$ realCurrentLineNumber ;
350
+ }
351
+
352
+ return $ realCurrentLineNumber ;
334
353
}
335
354
336
355
/**
@@ -432,7 +451,15 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false)
432
451
}
433
452
434
453
// we ignore "comment" lines only when we are not inside a scalar block
435
- if (empty ($ blockScalarIndentations ) && $ this ->isCurrentLineComment () && false === $ this ->checkIfPreviousNonCommentLineIsCollectionItem ()) {
454
+ if (empty ($ blockScalarIndentations ) && $ this ->isCurrentLineComment ()) {
455
+ // remember ignored comment lines (they are used later in nested
456
+ // parser calls to determine real line numbers)
457
+ //
458
+ // CAUTION: beware to not populate the global property here as it
459
+ // will otherwise influence the getRealCurrentLineNb() call here
460
+ // for consecutive comment lines and subsequent embedded blocks
461
+ $ this ->locallySkippedLineNumbers [] = $ this ->getRealCurrentLineNb ();
462
+
436
463
continue ;
437
464
}
438
465
@@ -802,44 +829,4 @@ private function isBlockScalarHeader()
802
829
{
803
830
return (bool ) preg_match ('~ ' .self ::BLOCK_SCALAR_HEADER_PATTERN .'$~ ' , $ this ->currentLine );
804
831
}
805
-
806
- /**
807
- * Returns true if the current line is a collection item.
808
- *
809
- * @return bool
810
- */
811
- private function isCurrentLineCollectionItem ()
812
- {
813
- $ ltrimmedLine = ltrim ($ this ->currentLine , ' ' );
814
-
815
- return '' !== $ ltrimmedLine && '- ' === $ ltrimmedLine [0 ];
816
- }
817
-
818
- /**
819
- * Tests whether the current comment line is in a collection.
820
- *
821
- * @return bool
822
- */
823
- private function checkIfPreviousNonCommentLineIsCollectionItem ()
824
- {
825
- $ isCollectionItem = false ;
826
- $ moves = 0 ;
827
- while ($ this ->moveToPreviousLine ()) {
828
- ++$ moves ;
829
- // If previous line is a comment, move back again.
830
- if ($ this ->isCurrentLineComment ()) {
831
- continue ;
832
- }
833
- $ isCollectionItem = $ this ->isCurrentLineCollectionItem ();
834
- break ;
835
- }
836
-
837
- // Move parser back to previous line.
838
- while ($ moves > 0 ) {
839
- $ this ->moveToNextLine ();
840
- --$ moves ;
841
- }
842
-
843
- return $ isCollectionItem ;
844
- }
845
832
}
0 commit comments