@@ -25,6 +25,7 @@ class Parser
25
25
const FOLDED_SCALAR_PATTERN = self ::BLOCK_SCALAR_HEADER_PATTERN ;
26
26
27
27
private $ offset = 0 ;
28
+ private $ totalNumberOfLines ;
28
29
private $ lines = array ();
29
30
private $ currentLineNb = -1 ;
30
31
private $ currentLine = '' ;
@@ -33,11 +34,13 @@ class Parser
33
34
/**
34
35
* Constructor.
35
36
*
36
- * @param int $offset The offset of YAML document (used for line numbers in error messages)
37
+ * @param int $offset The offset of YAML document (used for line numbers in error messages)
38
+ * @param int|null $totalNumberOfLines The overall number of lines being parsed
37
39
*/
38
- public function __construct ($ offset = 0 )
40
+ public function __construct ($ offset = 0 , $ totalNumberOfLines = null )
39
41
{
40
42
$ this ->offset = $ offset ;
43
+ $ this ->totalNumberOfLines = $ totalNumberOfLines ;
41
44
}
42
45
43
46
/**
@@ -62,6 +65,10 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
62
65
$ value = $ this ->cleanup ($ value );
63
66
$ this ->lines = explode ("\n" , $ value );
64
67
68
+ if (null === $ this ->totalNumberOfLines ) {
69
+ $ this ->totalNumberOfLines = count ($ this ->lines );
70
+ }
71
+
65
72
if (function_exists ('mb_internal_encoding ' ) && ((int ) ini_get ('mbstring.func_overload ' )) & 2 ) {
66
73
$ mbEncoding = mb_internal_encoding ();
67
74
mb_internal_encoding ('UTF-8 ' );
@@ -83,7 +90,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
83
90
$ isRef = $ mergeNode = false ;
84
91
if (preg_match ('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u ' , $ this ->currentLine , $ values )) {
85
92
if ($ context && 'mapping ' == $ context ) {
86
- throw new ParseException ('You cannot define a sequence item when in a mapping ' );
93
+ throw new ParseException ('You cannot define a sequence item when in a mapping ' , $ this -> getRealCurrentLineNb () + 1 , $ this -> currentLine );
87
94
}
88
95
$ context = 'sequence ' ;
89
96
@@ -95,7 +102,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
95
102
// array
96
103
if (!isset ($ values ['value ' ]) || '' == trim ($ values ['value ' ], ' ' ) || 0 === strpos (ltrim ($ values ['value ' ], ' ' ), '# ' )) {
97
104
$ c = $ this ->getRealCurrentLineNb () + 1 ;
98
- $ parser = new self ($ c );
105
+ $ parser = new self ($ c, $ this -> totalNumberOfLines );
99
106
$ parser ->refs = &$ this ->refs ;
100
107
$ data [] = $ parser ->parse ($ this ->getNextEmbedBlock (null , true ), $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
101
108
} else {
@@ -104,7 +111,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
104
111
) {
105
112
// this is a compact notation element, add to next block and parse
106
113
$ c = $ this ->getRealCurrentLineNb ();
107
- $ parser = new self ($ c );
114
+ $ parser = new self ($ c, $ this -> totalNumberOfLines );
108
115
$ parser ->refs = &$ this ->refs ;
109
116
110
117
$ block = $ values ['value ' ];
@@ -122,7 +129,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
122
129
}
123
130
} elseif (preg_match ('#^(?P<key> ' .Inline::REGEX_QUOTED_STRING .'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u ' , $ this ->currentLine , $ values ) && (false === strpos ($ values ['key ' ], ' # ' ) || in_array ($ values ['key ' ][0 ], array ('" ' , "' " )))) {
124
131
if ($ context && 'sequence ' == $ context ) {
125
- throw new ParseException ('You cannot define a mapping item when in a sequence ' );
132
+ throw new ParseException ('You cannot define a mapping item when in a sequence ' , $ this -> currentLineNb + 1 , $ this -> currentLine );
126
133
}
127
134
$ context = 'mapping ' ;
128
135
@@ -169,7 +176,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
169
176
$ value = $ this ->getNextEmbedBlock ();
170
177
}
171
178
$ c = $ this ->getRealCurrentLineNb () + 1 ;
172
- $ parser = new self ($ c );
179
+ $ parser = new self ($ c, $ this -> totalNumberOfLines );
173
180
$ parser ->refs = &$ this ->refs ;
174
181
$ parsed = $ parser ->parse ($ value , $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
175
182
@@ -220,7 +227,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
220
227
}
221
228
} else {
222
229
$ c = $ this ->getRealCurrentLineNb () + 1 ;
223
- $ parser = new self ($ c );
230
+ $ parser = new self ($ c, $ this -> totalNumberOfLines );
224
231
$ parser ->refs = &$ this ->refs ;
225
232
$ value = $ parser ->parse ($ this ->getNextEmbedBlock (), $ exceptionOnInvalidType , $ objectSupport , $ objectForMap );
226
233
// Spec: Keys MUST be unique; first one wins.
@@ -247,7 +254,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
247
254
} else {
248
255
// multiple documents are not supported
249
256
if ('--- ' === $ this ->currentLine ) {
250
- throw new ParseException ('Multiple documents are not supported. ' );
257
+ throw new ParseException ('Multiple documents are not supported. ' , $ this -> currentLineNb + 1 , $ this -> currentLine );
251
258
}
252
259
253
260
// 1-liner optionally followed by newline(s)
@@ -483,7 +490,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob
483
490
}
484
491
485
492
if (!array_key_exists ($ value , $ this ->refs )) {
486
- throw new ParseException (sprintf ('Reference "%s" does not exist. ' , $ value ), $ this ->currentLine );
493
+ throw new ParseException (sprintf ('Reference "%s" does not exist. ' , $ value ), $ this ->currentLineNb + 1 , $ this -> currentLine );
487
494
}
488
495
489
496
return $ this ->refs [$ value ];
@@ -569,6 +576,8 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0)
569
576
if ($ notEOF ) {
570
577
$ blockLines [] = '' ;
571
578
$ this ->moveToPreviousLine ();
579
+ } elseif (!$ notEOF && !$ this ->isCurrentLineLastLineInDocument ()) {
580
+ $ blockLines [] = '' ;
572
581
}
573
582
574
583
// folded style
@@ -675,6 +684,11 @@ private function isCurrentLineComment()
675
684
return '' !== $ ltrimmedLine && $ ltrimmedLine [0 ] === '# ' ;
676
685
}
677
686
687
+ private function isCurrentLineLastLineInDocument ()
688
+ {
689
+ return ($ this ->offset + $ this ->currentLineNb ) >= ($ this ->totalNumberOfLines - 1 );
690
+ }
691
+
678
692
/**
679
693
* Cleanups a YAML string to be parsed.
680
694
*
@@ -752,7 +766,7 @@ private function isNextLineUnIndentedCollection()
752
766
*/
753
767
private function isStringUnIndentedCollectionItem ()
754
768
{
755
- return 0 === strpos ($ this ->currentLine , '- ' );
769
+ return ' - ' === rtrim ( $ this -> currentLine ) || 0 === strpos ($ this ->currentLine , '- ' );
756
770
}
757
771
758
772
/**
0 commit comments