Merge branch '2.3' into 2.7

* 2.3:
  [Yaml] fix exception contexts
  People - person singularization
  [Yaml] properly handle unindented collections
  chomp newlines only at the end of YAML documents
This commit is contained in:
Fabien Potencier 2016-05-24 11:57:18 +02:00
commit eac5868d20
4 changed files with 61 additions and 13 deletions

View File

@ -124,6 +124,9 @@ class StringUtil
// chateaux (chateau) // chateaux (chateau)
array('xuae', 4, false, true, 'eau'), array('xuae', 4, false, true, 'eau'),
// people (person)
array('elpoep', 6, true, true, 'person'),
); );
/** /**

View File

@ -107,6 +107,8 @@ class StringUtilTest extends \PHPUnit_Framework_TestCase
array('objectives', 'objective'), array('objectives', 'objective'),
array('oxen', 'ox'), array('oxen', 'ox'),
array('parties', 'party'), array('parties', 'party'),
array('people', 'person'),
array('persons', 'person'),
array('phenomena', array('phenomenon', 'phenomenum')), array('phenomena', array('phenomenon', 'phenomenum')),
array('photos', 'photo'), array('photos', 'photo'),
array('pianos', 'piano'), array('pianos', 'piano'),

View File

@ -25,6 +25,7 @@ class Parser
const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN; const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN;
private $offset = 0; private $offset = 0;
private $totalNumberOfLines;
private $lines = array(); private $lines = array();
private $currentLineNb = -1; private $currentLineNb = -1;
private $currentLine = ''; private $currentLine = '';
@ -34,10 +35,12 @@ class Parser
* Constructor. * Constructor.
* *
* @param int $offset The offset of YAML document (used for line numbers in error messages) * @param int $offset The offset of YAML document (used for line numbers in error messages)
* @param int|null $totalNumberOfLines The overall number of lines being parsed
*/ */
public function __construct($offset = 0) public function __construct($offset = 0, $totalNumberOfLines = null)
{ {
$this->offset = $offset; $this->offset = $offset;
$this->totalNumberOfLines = $totalNumberOfLines;
} }
/** /**
@ -62,6 +65,10 @@ class Parser
$value = $this->cleanup($value); $value = $this->cleanup($value);
$this->lines = explode("\n", $value); $this->lines = explode("\n", $value);
if (null === $this->totalNumberOfLines) {
$this->totalNumberOfLines = count($this->lines);
}
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
$mbEncoding = mb_internal_encoding(); $mbEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8'); mb_internal_encoding('UTF-8');
@ -83,7 +90,7 @@ class Parser
$isRef = $mergeNode = false; $isRef = $mergeNode = false;
if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) { if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
if ($context && 'mapping' == $context) { if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a sequence item when in a mapping'); throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
} }
$context = 'sequence'; $context = 'sequence';
@ -95,7 +102,7 @@ class Parser
// array // array
if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
$c = $this->getRealCurrentLineNb() + 1; $c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c); $parser = new self($c, $this->totalNumberOfLines);
$parser->refs = &$this->refs; $parser->refs = &$this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap); $data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
} else { } else {
@ -104,7 +111,7 @@ class Parser
) { ) {
// this is a compact notation element, add to next block and parse // this is a compact notation element, add to next block and parse
$c = $this->getRealCurrentLineNb(); $c = $this->getRealCurrentLineNb();
$parser = new self($c); $parser = new self($c, $this->totalNumberOfLines);
$parser->refs = &$this->refs; $parser->refs = &$this->refs;
$block = $values['value']; $block = $values['value'];
@ -122,7 +129,7 @@ class Parser
} }
} 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('"', "'")))) { } 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('"', "'")))) {
if ($context && 'sequence' == $context) { if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a mapping item when in a sequence'); throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
} }
$context = 'mapping'; $context = 'mapping';
@ -169,7 +176,7 @@ class Parser
$value = $this->getNextEmbedBlock(); $value = $this->getNextEmbedBlock();
} }
$c = $this->getRealCurrentLineNb() + 1; $c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c); $parser = new self($c, $this->totalNumberOfLines);
$parser->refs = &$this->refs; $parser->refs = &$this->refs;
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap); $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap);
@ -220,7 +227,7 @@ class Parser
} }
} else { } else {
$c = $this->getRealCurrentLineNb() + 1; $c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c); $parser = new self($c, $this->totalNumberOfLines);
$parser->refs = &$this->refs; $parser->refs = &$this->refs;
$value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap); $value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);
// Spec: Keys MUST be unique; first one wins. // Spec: Keys MUST be unique; first one wins.
@ -247,7 +254,7 @@ class Parser
} else { } else {
// multiple documents are not supported // multiple documents are not supported
if ('---' === $this->currentLine) { if ('---' === $this->currentLine) {
throw new ParseException('Multiple documents are not supported.'); throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine);
} }
// 1-liner optionally followed by newline(s) // 1-liner optionally followed by newline(s)
@ -483,7 +490,7 @@ class Parser
} }
if (!array_key_exists($value, $this->refs)) { if (!array_key_exists($value, $this->refs)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine); throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine);
} }
return $this->refs[$value]; return $this->refs[$value];
@ -569,6 +576,8 @@ class Parser
if ($notEOF) { if ($notEOF) {
$blockLines[] = ''; $blockLines[] = '';
$this->moveToPreviousLine(); $this->moveToPreviousLine();
} elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) {
$blockLines[] = '';
} }
// folded style // folded style
@ -675,6 +684,11 @@ class Parser
return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#'; return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#';
} }
private function isCurrentLineLastLineInDocument()
{
return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1);
}
/** /**
* Cleanups a YAML string to be parsed. * Cleanups a YAML string to be parsed.
* *
@ -752,7 +766,7 @@ class Parser
*/ */
private function isStringUnIndentedCollectionItem() private function isStringUnIndentedCollectionItem()
{ {
return 0 === strpos($this->currentLine, '- '); return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- ');
} }
/** /**

View File

@ -547,7 +547,7 @@ EOF;
/** /**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException * @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage Multiple documents are not supported. * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/
*/ */
public function testMultipleDocumentsNotSupportedException() public function testMultipleDocumentsNotSupportedException()
{ {
@ -579,6 +579,34 @@ EOF
); );
} }
public function testSequenceInMappingStartedBySingleDashLine()
{
$yaml = <<<EOT
a:
-
b:
-
bar: baz
- foo
d: e
EOT;
$expected = array(
'a' => array(
array(
'b' => array(
array(
'bar' => 'baz',
),
),
),
'foo',
),
'd' => 'e',
);
$this->assertSame($expected, $this->parser->parse($yaml));
}
/** /**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException * @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/ */
@ -905,6 +933,7 @@ EOT
foo foo
# bar # bar
baz baz
EOT EOT
, ,
), ),
@ -933,7 +962,7 @@ EOT;
$expected = array( $expected = array(
'foo' => array( 'foo' => array(
'bar' => array( 'bar' => array(
'scalar-block' => 'line1 line2>', 'scalar-block' => "line1 line2>\n",
), ),
'baz' => array( 'baz' => array(
'foobar' => null, 'foobar' => null,