10000 'Add map support' · polyfractal/Yaml@b0a08f3 · GitHub
[go: up one dir, main page]

Skip to content

Commit b0a08f3

Browse files
committed
'Add map support'
1 parent 6194137 commit b0a08f3

File tree

3 files changed

+152
-24
lines changed

3 files changed

+152
-24
lines changed

Inline.php

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ class Inline
3232
* @param string $value A YAML string
3333
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
3434
* @param Boolean $objectSupport true if object support is enabled, false otherwise
35+
* @param Boolean $objectForMap true if maps should return a stdClass instead of array()
3536
*
3637
* @return array A PHP array representing the YAML string
3738
*
3839
* @throws ParseException
3940
*/
40-
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
41+
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false)
4142
{
4243
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
4344
self::$objectSupport = $objectSupport;
@@ -56,11 +57,11 @@ public static function parse($value, $exceptionOnInvalidType = false, $objectSup
5657
$i = 0;
5758
switch ($value[0]) {
5859
case '[':
59-
$result = self::parseSequence($value, $i);
60+
$result = self::parseSequence($value, $i, $objectForMap);
6061
++$i;
6162
break;
6263
case '{':
63-
$result = self::parseMapping($value, $i);
64+
$result = self::parseMapping($value, $i, $objectForMap);
6465
++$i;
6566
break;
6667
default:
@@ -261,14 +262,14 @@ private static function parseQuotedScalar($scalar, &$i)
261262
/**
262263
* Parses a sequence to a YAML string.
263264
*
264-
* @param string $sequence
265+
* @param string $sequence
265266
* @param integer &$i
267+
* @param Boolean $objectForMap true if maps should return a stdClass instead of array()
266268
*
269+
* @return array
267270
* @return string A YAML string
268-
*
269-
* @throws ParseException When malformed inline YAML string is parsed
270271
*/
271-
private static function parseSequence($sequence, &$i = 0)
272+
private static function parseSequence($sequence, &$i = 0, $objectForMap = false)
272273
{
273274
$output = array();
274275
$len = strlen($sequence);
@@ -279,11 +280,11 @@ private static function parseSequence($sequence, &$i = 0)
279280
switch ($sequence[$i]) {
280281
case '[':
281282
// nested sequence
282-
$output[] = self::parseSequence($sequence, $i);
283+
$output[] = self::parseSequence($sequence, $i, $objectForMap);
283284
break;
284285
case '{':
285286
// nested mapping
286-
$output[] = self::parseMapping($sequence, $i);
287+
$output[] = self::parseMapping($sequence, $i, $objectForMap);
287288
break;
288289
case ']':
289290
return $output;
@@ -297,7 +298,7 @@ private static function parseSequence($sequence, &$i = 0)
297298
if (!$isQuoted && false !== strpos($value, ': ')) {
298299
// embedded mapping?
299300
try {
300-
$value = self::parseMapping('{'.$value.'}');
301+
$value = self::parseMapping('{'.$value.'}', $j = 0, $objectForMap);
301302
} catch (\InvalidArgumentException $e) {
302303
// no, it's not
303304
}
@@ -319,12 +320,13 @@ private static function parseSequence($sequence, &$i = 0)
319320
*
320321
* @param string $mapping
321322
* @param integer &$i
323+
* @param Boolean $objectForMap true if maps should return a stdClass instead of array()
322324
*
323325
* @return string A YAML string
324326
*
325327
* @throws ParseException When malformed inline YAML string is parsed
326328
*/
327-
private static function parseMapping($mapping, &$i = 0)
329+
private static function parseMapping($mapping, &$i = 0, $objectForMap = false)
328330
{
329331
$output = array();
330332
$len = strlen($mapping);
@@ -338,6 +340,10 @@ private static function parseMapping($mapping, &$i = 0)
338340
++$i;
339341
continue 2;
340342
case '}':
343+
if (true === $objectForMap) {
344+
return (object) $output;
345+
}
346+
341347
return $output;
342348
}
343349

@@ -350,12 +356,12 @@ private static function parseMapping($mapping, &$i = 0)
350356
switch ($mapping[$i]) {
351357
case '[':
352358
// nested sequence
353-
$output[$key] = self::parseSequence($mapping, $i);
359+
$output[$key] = self::parseSequence($mapping, $i, $objectForMap);
354360
$done = true;
355361
break;
356362
case '{':
357363
// nested mapping
358-
$output[$key] = self::parseMapping($mapping, $i);
364+
$output[$key] = self::parseMapping($mapping, $i, $objectForMap);
359365
$done = true;
360366
break;
361367
case ':':

Parser.php

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@ public function __construct($offset = 0)
4444
* @param string $value A YAML string
4545
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
4646
* @param Boolean $objectSupport true if object support is enabled, false otherwise
47+
* @param Boolean $objectForMap true if maps should return a stdClass instead of array()
4748
*
4849
* @return mixed A PHP value
4950
*
5051
* @throws ParseException If the YAML is not valid
5152
*/
52-
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
53+
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false)
5354
{
5455
$this->currentLineNb = -1;
5556
$this->currentLine = '';
@@ -93,7 +94,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
9394
$c = $this->getRealCurrentLineNb() + 1;
9495
$parser = new Parser($c);
9596
$parser->refs =& $this->refs;
96-
$data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
97+
$data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);
9798
} else {
9899
if (isset($values['leadspaces'])
99100
&& ' ' == $values['leadspaces']
@@ -109,9 +110,9 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
109110
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2);
110111
}
111112

112-
$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport);
113+
$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport, $objectForMap);
113114
} else {
114-
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
115+
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap);
115116
}
116117
}
117118
} elseif (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values) && false === strpos($values['key'],' #')) {
@@ -121,7 +122,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
121122
$context = 'mapping';
122123

123124
// force correct settings
124-
Inline::parse(null, $exceptionOnInvalidType, $objectSupport);
125+
Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $objectForMap);
125126
try {
126127
$key = Inline::parseScalar($values['key']);
127128
} catch (ParseException $e) {
@@ -146,7 +147,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
146147
$c = $this->getRealCurrentLineNb() + 1;
147148
$parser = new Parser($c);
148149
$parser->refs =& $this->refs;
149-
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport);
150+
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap);
150151

151152
$merged = array();
152153
if (!is_array($parsed)) {
@@ -183,21 +184,21 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
183184
$c = $this->getRealCurrentLineNb() + 1;
184185
$parser = new Parser($c);
185186
$parser->refs =& $this->refs;
186-
$data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
187+
$data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);
187188
}
188189
} else {
189190
if ($isInPlace) {
190191
$data = $this->refs[$isInPlace];
191192
} else {
192-
$data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
193+
$data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap);
193194
}
194195
}
195196
} else {
196197
// 1-liner optionally followed by newline
197198
$lineCount = count($this->lines);
198199
if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) {
199200
try {
200-
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport);
201+
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap);
201202
} catch (ParseException $e) {
202203
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
203204
$e->setSnippet($this->currentLine);
@@ -376,12 +377,13 @@ private function moveToPreviousLine()
376377
* @param string $value A YAML value
377378
* @param Boolean $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise
378379
* @param Boolean $objectSupport True if object support is enabled, false otherwise
380+
* @param Boolean $objectForMap true if maps should return a stdClass instead of array()
379381
*
380382
* @return mixed A PHP value
381383
*
382384
* @throws ParseException When reference does not exist
383385
*/
384-
private function parseValue($value, $exceptionOnInvalidType, $objectSupport)
386+
private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $objectForMap = false)
385387
{
386388
if (0 === strpos($value, '*')) {
387389
if (false !== $pos = strpos($value, '#')) {
@@ -404,7 +406,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport)
404406
}
405407

406408
try {
407-
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport);
409+
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap);
408410
} catch (ParseException $e) {
409411
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
410412
$e->setSnippet($this->currentLine);

Tests/InlineTest.php

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,23 @@ public function testParse()
2222
}
2323
}
2424

25+
public function testParseWithMapObjects()
26+
{
27+
foreach ($this->getTestsForMapObjectParse() as $yaml => $value) {
28+
$actual = Inline::parse($yaml, false, false, true);
29+
if (true === is_object($value)) {
30+
$this->assertInstanceOf(get_class($value), $actual);
31+
$this->assertEquals(get_object_vars($value), get_object_vars($actual));
32+
} elseif (true === is_array($value)) {
33+
$this->assertEquals($value, $actual);
34+
$this->assertMixedArraysSame($value, $actual);
35+
} else {
36+
$this->assertSame($value, $actual);
37+
}
38+
}
39+
40+
}
41+
2542
public function testDump()
2643
{
2744
$testsForDump = $this->getTestsForDump();
@@ -182,6 +199,85 @@ protected function getTestsForParse()
182199
);
183200
}
184201

202+
protected function getTestsForMapObjectParse()
203+
{
204+
return array(
205+
'' => '',
206+
'null' => null,
207+
'false' => false,
208+
'true' => true,
209+
'12' => 12,
210+
'-12' => -12,
211+
'"quoted string"' => 'quoted string',
212+
"'quoted string'" => 'quoted string',
213+
'12.30e+02' => 12.30e+02,
214+
'0x4D2' => 0x4D2,
215+
'02333' => 02333,
216+
'.Inf' => -log(0),
217+
'-.Inf' => log(0),
218+
"'686e444'" => '686e444',
219+
'686e444' => 646e444,
220+
'123456789123456789123456789123456789' => '123456789123456789123456789123456789',
221+
'"foo\r\nbar"' => "foo\r\nbar",
222+
"'foo#bar'" => 'foo#bar',
223+
"'foo # bar'" => 'foo # bar',
224+
"'#cfcfcf'" => '#cfcfcf',
225+
'::form_base.html.twig' => '::form_base.html.twig',
226+
227+
'2007-10-30' => mktime(0, 0, 0, 10, 30, 2007),
228+
'2007-10-30T02:59:43Z' => gmmktime(2, 59, 43, 10, 30, 2007),
229+
'2007-10-30 02:59:43 Z' => gmmktime(2, 59, 43, 10, 30, 2007),
230+
'1960-10-30 02:59:43 Z' => gmmktime(2, 59, 43, 10, 30, 1960),
231+
'1730-10-30T02:59:43Z' => gmmktime(2, 59, 43, 10, 30, 1730),
232+
233+
'"a \\"string\\" with \'quoted strings inside\'"' => 'a "string" with \'quoted strings inside\'',
234+
"'a \"string\" with ''quoted strings inside'''" => 'a "string" with \'quoted strings inside\'',
235+
236+
// sequences
237+
// urls are no key value mapping. see #3609. Valid yaml "key: value" mappings require a space after the colon
238+
'[foo, http://urls.are/no/mappings, false, null, 12]' => array('foo', 'http://urls.are/no/mappings', false, null, 12),
239+
'[ foo , bar , false , null , 12 ]' => array('foo', 'bar', false, null, 12),
240+
'[\'foo,bar\', \'foo bar\']' => array('foo,bar', 'foo bar'),
241+
242+
// mappings
243+
'{foo:bar,bar:foo,false:false,null:null,integer:12}' => (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12),
244+
'{ foo : bar, bar : foo, false : false, null : null, integer : 12 }' => (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12),
245+
'{foo: \'bar\', bar: \'foo: bar\'}' => (object) array('foo' => 'bar', 'bar' => 'foo: bar'),
246+
'{\'foo\': \'bar\', "bar": \'foo: bar\'}' => (object) array('foo' => 'bar', 'bar' => 'foo: bar'),
247+
'{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}' => (object) array('foo\'' => 'bar', "bar\"" => 'foo: bar'),
248+
'{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}' => (object) array('foo: ' => 'bar', "bar: " => 'foo: bar'),
249+
250+
// nested sequences and mappings
251+
'[foo, [bar, foo]]' => array('foo', array('bar', 'foo')),
252+
'[foo, {bar: foo}]' => array('foo', (object) array('bar' => 'foo')),
253+
'{ foo: {bar: foo} }' => (object) array('foo' => (object) array('bar' => 'foo')),
254+
'{ foo: [bar, foo] }' => (object) array('foo' => array('bar', 'foo')),
255+
256+
'[ foo, [ bar, foo ] ]' => array('foo', array('bar', 'foo')),
257+
258+
'[{ foo: {bar: foo} }]' => array((object) array('foo' => (object) array('bar' => 'foo'))),
259+
260+
'[foo, [bar, [foo, [bar, foo]], foo]]' => array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo')),
261+
262+
'[foo, {bar: foo, foo: [foo, {bar: foo}]}, [foo, {bar: foo}]]' => array('foo', (object) array('bar' => 'foo', 'foo' => array('foo', (object) array('bar' => 'foo'))), array('foo', (object) array('bar' => 'foo'))),
263+
264+
'[foo, bar: { foo: bar }]' => array('foo', '1' => (object) array('bar' => (object) array('foo' => 'bar'))),
265+
'[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']' => array('foo', '@foo.baz', (object) array('%foo%' => 'foo is %foo%', 'bar' => '%foo%',), true, '@service_container',),
266+
267+
'{}' => new \stdClass(),
268+
'{ foo : bar, bar : {} }' => (object) array('foo' => 'bar', 'bar' => new \stdClass()),
269+
'{ foo : [], bar : {} }' => (object) array('foo' => array(), 'bar' => new \stdClass()),
270+
'{foo: \'bar\', bar: {} }' => (object) array('foo' => 'bar', 'bar' => new \stdClass()),
271+
'{\'foo\': \'bar\', "bar": {}}' => (object) array('foo' => 'bar', 'bar' => new \stdClass()),
272+
'{\'foo\': \'bar\', "bar": \'{}\'}' => (object) array('foo' => 'bar', 'bar' => '{}'),
273+
274+
'[foo, [{}, {}]]' => array('foo', array(new \stdClass(), new \stdClass())),
275+
'[foo, [[], {}]]' => array('foo', array(array(), new \stdClass())),
276+
'[foo, [[{}, {}], {}]]' => array('foo', array(array(new \stdClass(), new \stdClass()), new \stdClass())),
277+
'[foo, {bar: {}}]' => array('foo', '1' => (object) array('bar' => new \stdClass())),
278+
);
279+
}
280+
185281
protected function getTestsForDump()
186282
{
187283
return array(
@@ -196,6 +292,7 @@ protected function getTestsForDump()
196292
'.Inf' => -log(0),
197293
'-.Inf' => log(0),
198294
"'686e444'" => '686e444',
295+
'.Inf' => 646e444,
199296
'"foo\r\nbar"' => "foo\r\nbar",
200297
"'foo#bar'" => 'foo#bar',
201298
"'foo # bar'" => 'foo # bar',
@@ -228,4 +325,27 @@ protected function getTestsForDump()
228325
'[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']' => array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%',), true, '@service_container',),
229326
);
230327
}
328+
329+
protected function assertMixedArraysSame($a, $b)
330+
{
331+
332+
foreach ($a as $key => $value) {
333+
if (array_key_exists($key, $b)) {
334+
if (is_array($value)) {
335+
$this->assertMixedArraysSame($value, $b[$key]);
336+
} else {
337+
if (true === is_object($value)) {
338+
$this->assertEquals($value, $b[$key]);
339+
$this->assertInstanceOf(get_class($value), $b[$key]);
340+
70BB $this->assertEquals(get_object_vars($value), get_object_vars($b[$key]));
341+
} else {
342+
$this->assertSame($value, $b[$key]);
343+
}
344+
}
345+
} else {
346+
$this->assertFail();
347+
}
348+
}
349+
350+
}
231351
}

0 commit comments

Comments
 (0)
0