8000 Add tags support · symfony/symfony@1a00dc3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1a00dc3

Browse files
committed
Add tags support
1 parent e6bd47e commit 1a00dc3

File tree

6 files changed

+211
-4
lines changed

6 files changed

+211
-4
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Yaml\Exception;
13+
14+
/**
15+
* @author Guilhem N. <egetick@gmail.com>
16+
*
17+
* @internal
18+
*/
19+
class UnsupportedTagException extends ParseException
20+
{
21+
}

src/Symfony/Component/Yaml/Inline.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Yaml;
1313

1414
use Symfony\Component\Yaml\Exception\ParseException;
15+
use Symfony\Component\Yaml\Exception\UnsupportedTagException;
1516
use Symfony\Component\Yaml\Exception\DumpException;
1617

1718
/**
@@ -26,6 +27,7 @@ class Inline
2627
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
2728

2829
public static $parsedLineNumber;
30+
public static $tagResolver;
2931

3032
private static $exceptionOnInvalidType = false;
3133
private static $objectSupport = false;
@@ -659,9 +661,22 @@ private static function evaluateScalar($scalar, $flags, $references = array())
659661

660662
return $time;
661663
}
662-
default:
663-
return (string) $scalar;
664664
}
665+
666+
if (null !== self::$tagResolver && '!' === $scalar[0]) {
667+
$tagLength = strcspn($scalar, " \t", 1);
668+
$tag = substr($scalar, 1, $tagLength);
669+
$i = 0;
670+
$scalar = self::parseScalar(ltrim(substr($scalar, $tagLength)), $flags, null, array('"', "'"), $i, false, $references);
671+
672+
try {
673+
return self::$tagResolver->resolve($scalar, $tag);
674+
} catch (UnsupportedTagException $e) {
675+
// Ignored for bc
676+
}
677+
}
678+
679+
return (string) $scalar;
665680
}
666681

667682
/**

src/Symfony/Component/Yaml/Parser.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
namespace Symfony\Component\Yaml;
1313

1414
use Symfony\Component\Yaml\Exception\ParseException;
15+
use Symfony\Component\Yaml\Exception\UnsupportedTagException;
16+
use Symfony\Component\Yaml\Tag\TagInterface;
17+
use Symfony\Component\Yaml\Tag\TagResolver;
1518

1619
/**
1720
* Parser parses YAML strings to convert them to PHP arrays.
@@ -31,6 +34,7 @@ class Parser
3134
private $refs = array();
3235
private $skippedLineNumbers = array();
3336
private $locallySkippedLineNumbers = array();
37+
private $tagResolver;
3438

3539
/**
3640
* Constructor.
@@ -44,6 +48,16 @@ public function __construct($offset = 0, $totalNumberOfLines = null, array $skip
4448
$this->offset = $offset;
4549
$this->totalNumberOfLines = $totalNumberOfLines;
4650
$this->skippedLineNumbers = $skippedLineNumbers;
51+
$this->tagResolver = new TagResolver();
52+
}
53+
54+
/**
55+
* @param TagInterface $tag
56+
* @param int $priority
57+
*/
58+
public function addTag(TagInterface $tag, $priority = 0)
59+
{
60+
$this->tagResolver->addTag($tag, $priority);
4761
}
4862

4963
/**
@@ -101,6 +115,16 @@ public function parse($value, $flags = 0)
101115
mb_internal_encoding('UTF-8');
102116
}
103117

118+
Inline::$tagResolver = $this->tagResolver;
119+
try {
120+
return $this->doParse($value, $flags);
121+
} finally {
122+
Inline::$tagResolver = null;
123+
}
124+
}
125+
126+
private function doParse($value, $flags)
127+
{
104128
$data = array();
105129
$context = null;
106130
$allowOverwrite = false;
@@ -386,6 +410,7 @@ private function parseBlock($offset, $yaml, $flags)
386410

387411
$parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers);
388412
$parser->refs = &$this->refs;
413+
$parser->tagResolver = $this->tagResolver;
389414

390415
return $parser->parse($yaml, $flags);
391416
}
@@ -599,8 +624,18 @@ private function parseValue($value, $flags, $context)
599624

600625
$data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers));
601626

602-
if (isset($matches['tag']) && '!!binary' === $matches['tag']) {
603-
return Inline::evaluateBinaryScalar($data);
627+
if (isset($matches['tag'])) {
628+
if ('!!binary' === $matches['tag']) {
629+
return Inline::evaluateBinaryScalar($data);
630+
}
631+
632+
$tag = substr($matches['tag'], 1);
633+
634+
try {
635+
return $this->tagResolver->resolve($data, $tag);
636+
} catch (UnsupportedTagException $e) {
637+
// ignored for bc
638+
}
604639
}
605640

606641
return $data;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Yaml\Tag;
13+
14+
/**
15+
* @author Guilhem N. <egetick@gmail.com>
16+
*/
17+
interface TagInterface
18+
{
19+
/**
20+
* @param mixed $value
21+
* @param string $tag
22+
*
23+
* @return mixed
24+
*/
25+
public function construct($value, $tag);
26+
27+
/**
28+
* @param mixed $value
29+
* @param string $tag
30+
*
31+
* @return bool
32+
*/
33+
public function supports($value, $tag);
34+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Yaml\Tag;
13+
14+
use Symfony\Component\Yaml\Yaml;
15+
use Symfony\Component\Yaml\Exception\UnsupportedTagException;
16+
17+
/**
18+
* @author Guilhem N. <egetick@gmail.com>
19+
*
20+
* @internal
21+
*/
22+
final class TagResolver
23+
{
24+
private $tags = array();
25+
26+
/**
27+
* @param TagInterface $tag
28+
* @param int $priority
29+
*/
30+
public function addTag(TagInterface $tag, $priority = 0)
31+
{
32+
if (!isset($this->tags[$priority])) {
33+
$this->tags[$priority] = array();
34+
krsort($this->tags);
35+
}
36+
37+
$this->tags[$priority][] = $tag;
38+
}
39+
40+
/**
41+
* @param mixed $value
42+
* @param string $tag
43+
*
44+
* @return mixed
45+
*
46+
* @throws UnsupportedTagException when the tag is not supported
47+
*/
48+
public function resolve($value, $tag)
49+
{
50+
$tag = $this->prefixTag($tag);
51+
foreach ($this->tags as $registeredTags) {
52+
foreach ($registeredTags as $registeredTag) {
53+
if ($registeredTag->supports($value, $tag)) {
54+
return $registeredTag->construct($value, $tag);
55+
}
56+
}
57+
}
58+
59+
throw new UnsupportedTagException(sprintf('Tag "%s" used with value "%s" is not supported', $tag, $value));
60+
}
61+
62+
/**
63+
* @param string $tag
64+
*
65+
* @return string
66+
*/
67+
private function prefixTag($tag)
68+
{
69+
if ('' !== $tag && '!' === $tag[0]) {
70+
$tag = 'tag:yaml.org,2002:'.substr($tag, 1);
71+
}
72+
73+
return $tag;
74+
}
75+
}

src/Symfony/Component/Yaml/Tests/ParserTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Yaml\Yaml;
1515
use Symfony\Component\Yaml\Parser;
16+
use Symfony\Component\Yaml\Tag\TagInterface;
1617

1718
class ParserTest extends \PHPUnit_Framework_TestCase
1819
{
@@ -1491,9 +1492,35 @@ public function testParseMultiLineMappingValue()
14911492

14921493
$this->assertEquals($expected, $this->parser->parse($yaml));
14931494
}
1495+
1496+
public function testCustomTagSupport()
1497+
{
1498+
$this->parser->addTag(new FooTag());
1499+
1500+
$this->assertSame('bar', $this->parser->parse('!foo'));
1501+
}
14941502
}
14951503

14961504
class B
14971505
{
14981506
public $b = 'foo';
14991507
}
1508+
1509+
class FooTag implements TagInterface
1510+
{
1511+
/**
1512+
* {@inheritdoc}
1513+
*/
1514+
public function construct($value, $tag)
1515+
{
1516+
return 'bar';
1517+
}
1518+
1519+
/**
1520+
* {@inheritdoc}
1521+
*/
1522+
public function supports($value, $tag)
1523+
{
1524+
return 'foo' === $tag;
1525+
}
1526+
}

0 commit comments

Comments
 (0)
0