@@ -30,17 +30,21 @@ class Parser
3030 private $ currentLineNb = -1 ;
3131 private $ currentLine = '' ;
3232 private $ refs = array ();
33+ private $ skippedLineNumbers = array ();
34+ private $ locallySkippedLineNumbers = array ();
3335
3436 /**
3537 * Constructor.
3638 *
3739 * @param int $offset The offset of YAML document (used for line numbers in error messages)
3840 * @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
3942 */
40- public function __construct ($ offset = 0 , $ totalNumberOfLines = null )
43+ public function __construct ($ offset = 0 , $ totalNumberOfLines = null , array $ skippedLineNumbers = array () )
4144 {
4245 $ this ->offset = $ offset ;
4346 $ this ->totalNumberOfLines = $ totalNumberOfLines ;
47+ $ this ->skippedLineNumbers = $ skippedLineNumbers ;
4448 }
4549
4650 /**
@@ -101,25 +105,18 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
101105
102106 // array
103107 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 );
108109 } else {
109110 if (isset ($ values ['leadspaces ' ])
110111 && preg_match ('#^(?P<key> ' .Inline::REGEX_QUOTED_STRING .'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u ' , $ values ['value ' ], $ matches )
111112 ) {
112113 // 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-
117114 $ block = $ values ['value ' ];
118115 if ($ this ->isNextLineIndented ()) {
119116 $ block .= "\n" .$ this ->getNextEmbedBlock ($ this ->getCurrentLineIndentation () + strlen ($ values ['leadspaces ' ]) + 1 );
120117 }
121118
122- $ data [] = $ parser -> parse ( $ block , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
119+ $ data [] = $ this -> parseBlock ( $ this -> getRealCurrentLineNb (), $ block , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
123120 } else {
124121 $ data [] = $ this ->parseValue ($ values ['value ' ], $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
125122 }
@@ -175,10 +172,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
175172 } else {
176173 $ value = $ this ->getNextEmbedBlock ();
177174 }
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 );
182176
183177 if (!is_array ($ parsed )) {
184178 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 =
226220 $ data [$ key ] = null ;
227221 }
228222 } 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 );
233224 // Spec: Keys MUST be unique; first one wins.
234225 // But overwriting is allowed when a merge node is used in current block.
235226 if ($ allowOverwrite || !isset ($ data [$ key ])) {
@@ -317,14 +308,42 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
317308 return empty ($ data ) ? null : $ data ;
318309 }
319310
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+
320329 /**
321330 * Returns the current line number (takes the offset into account).
322331 *
323332 * @return int The current line number
324333 */
325334 private function getRealCurrentLineNb ()
326335 {
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 ;
328347 }
329348
330349 /**
@@ -426,7 +445,15 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false)
426445 }
427446
428447 // 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+
430457 continue ;
431458 }
432459
@@ -786,44 +813,4 @@ private function isBlockScalarHeader()
786813 {
787814 return (bool ) preg_match ('~ ' .self ::BLOCK_SCALAR_HEADER_PATTERN .'$~ ' , $ this ->currentLine );
788815 }
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- }
829816}
0 commit comments