Laravel version update

Laravel version update
This commit is contained in:
Manish Verma
2018-08-06 18:48:58 +05:30
parent d143048413
commit 126fbb0255
13678 changed files with 1031482 additions and 778530 deletions

View File

@@ -1,9 +1,91 @@
CHANGELOG
=========
3.4.0
-----
* added support for parsing YAML files using the `Yaml::parseFile()` or `Parser::parseFile()` method
* the `Dumper`, `Parser`, and `Yaml` classes are marked as final
* Deprecated the `!php/object:` tag which will be replaced by the
`!php/object` tag (without the colon) in 4.0.
* Deprecated the `!php/const:` tag which will be replaced by the
`!php/const` tag (without the colon) in 4.0.
* Support for the `!str` tag is deprecated, use the `!!str` tag instead.
* Deprecated using the non-specific tag `!` as its behavior will change in 4.0.
It will force non-evaluating your values in 4.0. Use plain integers or `!!float` instead.
3.3.0
-----
* Starting an unquoted string with a question mark followed by a space is
deprecated and will throw a `ParseException` in Symfony 4.0.
* Deprecated support for implicitly parsing non-string mapping keys as strings.
Mapping keys that are no strings will lead to a `ParseException` in Symfony
4.0. Use quotes to opt-in for keys to be parsed as strings.
Before:
```php
$yaml = <<<YAML
null: null key
true: boolean true
2.0: float key
YAML;
Yaml::parse($yaml);
```
After:
```php
$yaml = <<<YAML
"null": null key
"true": boolean true
"2.0": float key
YAML;
Yaml::parse($yaml);
```
* Omitted mapping values will be parsed as `null`.
* Omitting the key of a mapping is deprecated and will throw a `ParseException` in Symfony 4.0.
* Added support for dumping empty PHP arrays as YAML sequences:
```php
Yaml::dump([], 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
```
3.2.0
-----
* Mappings with a colon (`:`) that is not followed by a whitespace are deprecated
when the mapping key is not quoted and will lead to a `ParseException` in
Symfony 4.0 (e.g. `foo:bar` must be `foo: bar`).
* Added support for parsing PHP constants:
```php
Yaml::parse('!php/const:PHP_INT_MAX', Yaml::PARSE_CONSTANT);
```
* Support for silently ignoring duplicate mapping keys in YAML has been
deprecated and will lead to a `ParseException` in Symfony 4.0.
3.1.0
-----
* Added support to dump `stdClass` and `ArrayAccess` objects as YAML mappings
through the `Yaml::DUMP_OBJECT_AS_MAP` flag.
* Strings that are not UTF-8 encoded will be dumped as base64 encoded binary
data.

View File

@@ -0,0 +1,250 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Yaml\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Yaml;
/**
* Validates YAML files syntax and outputs encountered errors.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class LintCommand extends Command
{
protected static $defaultName = 'lint:yaml';
private $parser;
private $format;
private $displayCorrectFiles;
private $directoryIteratorProvider;
private $isReadableProvider;
public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null)
{
parent::__construct($name);
$this->directoryIteratorProvider = $directoryIteratorProvider;
$this->isReadableProvider = $isReadableProvider;
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setDescription('Lints a file and outputs encountered errors')
->addArgument('filename', null, 'A file or a directory or STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->addOption('parse-tags', null, InputOption::VALUE_NONE, 'Parse custom tags')
->setHelp(<<<EOF
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
the first encountered syntax error.
You can validates YAML contents passed from STDIN:
<info>cat filename | php %command.full_name%</info>
You can also validate the syntax of a file:
<info>php %command.full_name% filename</info>
Or of a whole directory:
<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>
EOF
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$filename = $input->getArgument('filename');
$this->format = $input->getOption('format');
$this->displayCorrectFiles = $output->isVerbose();
$flags = $input->getOption('parse-tags') ? Yaml::PARSE_CUSTOM_TAGS : 0;
if (!$filename) {
if (!$stdin = $this->getStdin()) {
throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
}
return $this->display($io, array($this->validate($stdin, $flags)));
}
if (!$this->isReadable($filename)) {
throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
}
$filesInfo = array();
foreach ($this->getFiles($filename) as $file) {
$filesInfo[] = $this->validate(file_get_contents($file), $flags, $file);
}
return $this->display($io, $filesInfo);
}
private function validate($content, $flags, $file = null)
{
$prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
if (E_USER_DEPRECATED === $level) {
throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1);
}
return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
});
try {
$this->getParser()->parse($content, Yaml::PARSE_CONSTANT | $flags);
} catch (ParseException $e) {
return array('file' => $file, 'line' => $e->getParsedLine(), 'valid' => false, 'message' => $e->getMessage());
} finally {
restore_error_handler();
}
return array('file' => $file, 'valid' => true);
}
private function display(SymfonyStyle $io, array $files)
{
switch ($this->format) {
case 'txt':
return $this->displayTxt($io, $files);
case 'json':
return $this->displayJson($io, $files);
default:
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
}
}
private function displayTxt(SymfonyStyle $io, array $filesInfo)
{
$countFiles = \count($filesInfo);
$erroredFiles = 0;
foreach ($filesInfo as $info) {
if ($info['valid'] && $this->displayCorrectFiles) {
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
} elseif (!$info['valid']) {
++$erroredFiles;
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->text(sprintf('<error> >> %s</error>', $info['message']));
}
}
if (0 === $erroredFiles) {
$io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles));
} else {
$io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
}
return min($erroredFiles, 1);
}
private function displayJson(SymfonyStyle $io, array $filesInfo)
{
$errors = 0;
array_walk($filesInfo, function (&$v) use (&$errors) {
$v['file'] = (string) $v['file'];
if (!$v['valid']) {
++$errors;
}
});
$io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return min($errors, 1);
}
private function getFiles($fileOrDirectory)
{
if (is_file($fileOrDirectory)) {
yield new \SplFileInfo($fileOrDirectory);
return;
}
foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
if (!\in_array($file->getExtension(), array('yml', 'yaml'))) {
continue;
}
yield $file;
}
}
private function getStdin()
{
if (0 !== ftell(STDIN)) {
return;
}
$inputs = '';
while (!feof(STDIN)) {
$inputs .= fread(STDIN, 1024);
}
return $inputs;
}
private function getParser()
{
if (!$this->parser) {
$this->parser = new Parser();
}
return $this->parser;
}
private function getDirectoryIterator($directory)
{
$default = function ($directory) {
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
\RecursiveIteratorIterator::LEAVES_ONLY
);
};
if (null !== $this->directoryIteratorProvider) {
return \call_user_func($this->directoryIteratorProvider, $directory, $default);
}
return $default($directory);
}
private function isReadable($fileOrDirectory)
{
$default = function ($fileOrDirectory) {
return is_readable($fileOrDirectory);
};
if (null !== $this->isReadableProvider) {
return \call_user_func($this->isReadableProvider, $fileOrDirectory, $default);
}
return $default($fileOrDirectory);
}
}

View File

@@ -15,6 +15,8 @@ namespace Symfony\Component\Yaml;
* Dumper dumps PHP variables to YAML strings.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since version 3.4
*/
class Dumper
{
@@ -41,10 +43,12 @@ class Dumper
* Sets the indentation.
*
* @param int $num The amount of spaces to use for indentation of nested nodes
*
* @deprecated since version 3.1, to be removed in 4.0. Pass the indentation to the constructor instead.
*/
public function setIndentation($num)
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 3.1 and will be removed in 4.0. Pass the indentation to the constructor instead.', E_USER_DEPRECATED);
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 3.1 and will be removed in 4.0. Pass the indentation to the constructor instead.', E_USER_DEPRECATED);
$this->indentation = (int) $num;
}
@@ -61,8 +65,8 @@ class Dumper
*/
public function dump($input, $inline = 0, $indent = 0, $flags = 0)
{
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if (\is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if ($flags) {
$flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
@@ -71,8 +75,8 @@ class Dumper
}
}
if (func_num_args() >= 5) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 5) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
if (func_get_arg(4)) {
$flags |= Yaml::DUMP_OBJECT;
@@ -81,15 +85,23 @@ class Dumper
$output = '';
$prefix = $indent ? str_repeat(' ', $indent) : '';
$dumpObjectAsInlineMap = true;
if ($inline <= 0 || !is_array($input) || empty($input)) {
if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) {
$dumpObjectAsInlineMap = empty((array) $input);
}
if ($inline <= 0 || (!\is_array($input) && $dumpObjectAsInlineMap) || empty($input)) {
$output .= $prefix.Inline::dump($input, $flags);
} else {
$isAHash = Inline::isHash($input);
$dumpAsMap = Inline::isHash($input);
foreach ($input as $key => $value) {
if ($inline > 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && is_string($value) && false !== strpos($value, "\n")) {
$output .= sprintf("%s%s%s |\n", $prefix, $isAHash ? Inline::dump($key, $flags).':' : '-', '');
if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && false !== strpos($value, "\n") && false === strpos($value, "\r\n")) {
// If the first line starts with a space character, the spec requires a blockIndicationIndicator
// http://www.yaml.org/spec/1.2/spec.html#id2793979
$blockIndentationIndicator = (' ' === substr($value, 0, 1)) ? (string) $this->indentation : '';
$output .= sprintf("%s%s%s |%s\n", $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator);
foreach (preg_split('/\n|\r\n/', $value) as $row) {
$output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row);
@@ -98,11 +110,17 @@ class Dumper
continue;
}
$willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value);
$dumpObjectAsInlineMap = true;
if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) {
$dumpObjectAsInlineMap = empty((array) $value);
}
$willBeInlined = $inline - 1 <= 0 || !\is_array($value) && $dumpObjectAsInlineMap || empty($value);
$output .= sprintf('%s%s%s%s',
$prefix,
$isAHash ? Inline::dump($key, $flags).':' : '-',
$dumpAsMap ? Inline::dump($key, $flags).':' : '-',
$willBeInlined ? ' ' : "\n",
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
).($willBeInlined ? "\n" : '');

View File

@@ -33,13 +33,15 @@ class Escaper
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9");
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
);
private static $escaped = array('\\\\', '\\"', '\\\\', '\\"',
'\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a',
'\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f',
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
'\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f',
'\\N', '\\_', '\\L', '\\P');
'\\N', '\\_', '\\L', '\\P',
);
/**
* Determines if a PHP value would require double quoting in YAML.
@@ -50,7 +52,7 @@ class Escaper
*/
public static function requiresDoubleQuoting($value)
{
return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value);
return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value);
}
/**
@@ -76,13 +78,13 @@ class Escaper
{
// Determines if a PHP value is entirely composed of a value that would
// require single quoting in YAML.
if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) {
if (\in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) {
return true;
}
// Determines if the PHP value contains any single characters that would
// cause it to require single quoting in YAML.
return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value);
return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value);
}
/**

View File

@@ -24,13 +24,11 @@ class ParseException extends RuntimeException
private $rawMessage;
/**
* Constructor.
*
* @param string $message The error message
* @param int $parsedLine The line where the error occurred
* @param int $snippet The snippet of code near the problem
* @param string $parsedFile The file name where the error occurred
* @param \Exception $previous The previous exception
* @param string $message The error message
* @param int $parsedLine The line where the error occurred
* @param string|null $snippet The snippet of code near the problem
* @param string|null $parsedFile The file name where the error occurred
* @param \Exception|null $previous The previous exception
*/
public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null)
{

View File

@@ -11,8 +11,9 @@
namespace Symfony\Component\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Tag\TaggedValue;
/**
* Inline implements a YAML parser/dumper for the YAML inline syntax.
@@ -23,27 +24,49 @@ use Symfony\Component\Yaml\Exception\DumpException;
*/
class Inline
{
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
public static $parsedLineNumber = -1;
public static $parsedFilename;
private static $exceptionOnInvalidType = false;
private static $objectSupport = false;
private static $objectForMap = false;
private static $constantSupport = false;
/**
* Converts a YAML string to a PHP array.
* @param int $flags
* @param int|null $parsedLineNumber
* @param string|null $parsedFilename
*/
public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null)
{
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);
self::$parsedFilename = $parsedFilename;
if (null !== $parsedLineNumber) {
self::$parsedLineNumber = $parsedLineNumber;
}
}
/**
* Converts a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
* @param array $references Mapping of variable names to values
*
* @return array A PHP array representing the YAML string
* @return mixed A PHP value
*
* @throws ParseException
*/
public static function parse($value, $flags = 0, $references = array())
{
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if (\is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if ($flags) {
$flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
@@ -52,31 +75,29 @@ class Inline
}
}
if (func_num_args() >= 3 && !is_array($references)) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 3 && !\is_array($references)) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
if ($references) {
$flags |= Yaml::PARSE_OBJECT;
}
if (func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
if (func_get_arg(3)) {
$flags |= Yaml::PARSE_OBJECT_FOR_MAP;
}
}
if (func_num_args() >= 5) {
if (\func_num_args() >= 5) {
$references = func_get_arg(4);
} else {
$references = array();
}
}
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
self::initialize($flags);
$value = trim($value);
@@ -90,7 +111,8 @@ class Inline
}
$i = 0;
switch ($value[0]) {
$tag = self::parseTag($value, $i, $flags);
switch ($value[$i]) {
case '[':
$result = self::parseSequence($value, $flags, $i, $references);
++$i;
@@ -100,12 +122,16 @@ class Inline
++$i;
break;
default:
$result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references);
$result = self::parseScalar($value, $flags, null, $i, null === $tag, $references);
}
if (null !== $tag) {
return new TaggedValue($tag, $result);
}
// some comments are allowed at the end
if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}
if (isset($mbEncoding)) {
@@ -121,14 +147,14 @@ class Inline
* @param mixed $value The PHP variable to convert
* @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
*
* @return string The YAML string representing the PHP array
* @return string The YAML string representing the PHP value
*
* @throws DumpException When trying to dump PHP resource
*/
public static function dump($value, $flags = 0)
{
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if (\is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if ($flags) {
$flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
@@ -137,8 +163,8 @@ class Inline
}
}
if (func_num_args() >= 3) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 3) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
if (func_get_arg(2)) {
$flags |= Yaml::DUMP_OBJECT;
@@ -146,7 +172,7 @@ class Inline
}
switch (true) {
case is_resource($value):
case \is_resource($value):
if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
}
@@ -154,13 +180,17 @@ class Inline
return 'null';
case $value instanceof \DateTimeInterface:
return $value->format('c');
case is_object($value):
case \is_object($value):
if ($value instanceof TaggedValue) {
return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags);
}
if (Yaml::DUMP_OBJECT & $flags) {
return '!php/object:'.serialize($value);
return '!php/object '.self::dump(serialize($value));
}
if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
return self::dumpArray((array) $value, $flags);
return self::dumpArray($value, $flags & ~Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
}
if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
@@ -168,7 +198,7 @@ class Inline
}
return 'null';
case is_array($value):
case \is_array($value):
return self::dumpArray($value, $flags);
case null === $value:
return 'null';
@@ -177,13 +207,13 @@ class Inline
case false === $value:
return 'false';
case ctype_digit($value):
return is_string($value) ? "'$value'" : (int) $value;
return \is_string($value) ? "'$value'" : (int) $value;
case is_numeric($value):
$locale = setlocale(LC_NUMERIC, 0);
if (false !== $locale) {
setlocale(LC_NUMERIC, 'C');
}
if (is_float($value)) {
if (\is_float($value)) {
$repr = (string) $value;
if (is_infinite($value)) {
$repr = str_ireplace('INF', '.Inf', $repr);
@@ -192,7 +222,7 @@ class Inline
$repr = '!!float '.$repr;
}
} else {
$repr = is_string($value) ? "'$value'" : (string) $value;
$repr = \is_string($value) ? "'$value'" : (string) $value;
}
if (false !== $locale) {
setlocale(LC_NUMERIC, $locale);
@@ -206,8 +236,9 @@ class Inline
case Escaper::requiresDoubleQuoting($value):
return Escaper::escapeWithDoubleQuotes($value);
case Escaper::requiresSingleQuoting($value):
case preg_match(self::getHexRegex(), $value):
case preg_match(self::getTimestampRegex(), $value):
case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value):
case Parser::preg_match(self::getHexRegex(), $value):
case Parser::preg_match(self::getTimestampRegex(), $value):
return Escaper::escapeWithSingleQuotes($value);
default:
return $value;
@@ -219,12 +250,16 @@ class Inline
*
* @internal
*
* @param array $value The PHP array to check
* @param array|\ArrayObject|\stdClass $value The PHP array or array-like object to check
*
* @return bool true if value is hash array, false otherwise
*/
public static function isHash(array $value)
public static function isHash($value)
{
if ($value instanceof \stdClass || $value instanceof \ArrayObject) {
return true;
}
$expectedKey = 0;
foreach ($value as $key => $val) {
@@ -247,7 +282,7 @@ class Inline
private static function dumpArray($value, $flags)
{
// array
if ($value && !self::isHash($value)) {
if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) {
$output = array();
foreach ($value as $val) {
$output[] = self::dump($val, $flags);
@@ -266,58 +301,60 @@ class Inline
}
/**
* Parses a scalar to a YAML string.
* Parses a YAML scalar.
*
* @param string $scalar
* @param int $flags
* @param string $delimiters
* @param array $stringDelimiters
* @param int &$i
* @param bool $evaluate
* @param array $references
* @param string $scalar
* @param int $flags
* @param string[] $delimiters
* @param int &$i
* @param bool $evaluate
* @param array $references
*
* @return string A YAML string
* @return string
*
* @throws ParseException When malformed inline YAML string is parsed
*
* @internal
*/
public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = array(), $legacyOmittedKeySupport = false)
{
if (in_array($scalar[$i], $stringDelimiters)) {
if (\in_array($scalar[$i], array('"', "'"))) {
// quoted scalar
$output = self::parseQuotedScalar($scalar, $i);
if (null !== $delimiters) {
$tmp = ltrim(substr($scalar, $i), ' ');
if (!in_array($tmp[0], $delimiters)) {
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
if ('' === $tmp) {
throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode($delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (!\in_array($tmp[0], $delimiters)) {
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
}
} else {
// "normal" string
if (!$delimiters) {
$output = substr($scalar, $i);
$i += strlen($output);
$i += \strlen($output);
// remove comments
if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
$output = substr($output, 0, $match[0][1]);
}
} elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
} elseif (Parser::preg_match('/^(.'.($legacyOmittedKeySupport ? '+' : '*').'?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
$output = $match[1];
$i += strlen($output);
$i += \strlen($output);
} else {
throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename);
}
// a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber + 1, $output, self::$parsedFilename);
}
if ($output && '%' === $output[0]) {
@trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.' , $output), E_USER_DEPRECATED);
@trigger_error(self::getDeprecationMessage(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output)), E_USER_DEPRECATED);
}
if ($evaluate) {
@@ -329,22 +366,22 @@ class Inline
}
/**
* Parses a quoted scalar to YAML.
* Parses a YAML quoted scalar.
*
* @param string $scalar
* @param int &$i
*
* @return string A YAML string
* @return string
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseQuotedScalar($scalar, &$i)
{
if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
$output = substr($match[0], 1, strlen($match[0]) - 2);
$output = substr($match[0], 1, \strlen($match[0]) - 2);
$unescaper = new Unescaper();
if ('"' == $scalar[$i]) {
@@ -353,51 +390,56 @@ class Inline
$output = $unescaper->unescapeSingleQuotedString($output);
}
$i += strlen($match[0]);
$i += \strlen($match[0]);
return $output;
}
/**
* Parses a sequence to a YAML string.
* Parses a YAML sequence.
*
* @param string $sequence
* @param int $flags
* @param int &$i
* @param array $references
*
* @return string A YAML string
* @return array
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseSequence($sequence, $flags, &$i = 0, $references = array())
{
$output = array();
$len = strlen($sequence);
$len = \strlen($sequence);
++$i;
// [foo, bar, ...]
while ($i < $len) {
if (']' === $sequence[$i]) {
return $output;
}
if (',' === $sequence[$i] || ' ' === $sequence[$i]) {
++$i;
continue;
}
$tag = self::parseTag($sequence, $i, $flags);
switch ($sequence[$i]) {
case '[':
// nested sequence
$output[] = self::parseSequence($sequence, $flags, $i, $references);
$value = self::parseSequence($sequence, $flags, $i, $references);
break;
case '{':
// nested mapping
$output[] = self::parseMapping($sequence, $flags, $i, $references);
break;
case ']':
return $output;
case ',':
case ' ':
$value = self::parseMapping($sequence, $flags, $i, $references);
break;
default:
$isQuoted = in_array($sequence[$i], array('"', "'"));
$value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references);
$isQuoted = \in_array($sequence[$i], array('"', "'"));
$value = self::parseScalar($sequence, $flags, array(',', ']'), $i, null === $tag, $references);
// the value can be an array if a reference has been resolved to an array var
if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
if (\is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
// embedded mapping?
try {
$pos = 0;
@@ -407,34 +449,39 @@ class Inline
}
}
$output[] = $value;
--$i;
}
if (null !== $tag) {
$value = new TaggedValue($tag, $value);
}
$output[] = $value;
++$i;
}
throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence));
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename);
}
/**
* Parses a mapping to a YAML string.
* Parses a YAML mapping.
*
* @param string $mapping
* @param int $flags
* @param int &$i
* @param array $references
*
* @return string A YAML string
* @return array|\stdClass
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
{
$output = array();
$len = strlen($mapping);
$len = \strlen($mapping);
++$i;
$allowOverwrite = false;
// {foo: bar, bar:foo, ...}
while ($i < $len) {
@@ -452,12 +499,41 @@ class Inline
}
// key
$key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
$isKeyQuoted = \in_array($mapping[$i], array('"', "'"), true);
$key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array(), true);
// value
$done = false;
if (':' !== $key && false === $i = strpos($mapping, ':', $i)) {
break;
}
if (':' === $key) {
@trigger_error(self::getDeprecationMessage('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0.'), E_USER_DEPRECATED);
}
if (!$isKeyQuoted) {
$evaluatedKey = self::evaluateScalar($key, $flags, $references);
if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) {
@trigger_error(self::getDeprecationMessage('Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.'), E_USER_DEPRECATED);
}
}
if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) {
@trigger_error(self::getDeprecationMessage('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since Symfony 3.2 and will throw a ParseException in 4.0.'), E_USER_DEPRECATED);
}
if ('<<' === $key) {
$allowOverwrite = true;
}
while ($i < $len) {
if (':' === $mapping[$i] || ' ' === $mapping[$i]) {
++$i;
continue;
}
$tag = self::parseTag($mapping, $i, $flags);
switch ($mapping[$i]) {
case '[':
// nested sequence
@@ -465,10 +541,20 @@ class Inline
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since lines
// are processed sequentially.
if (!isset($output[$key])) {
$output[$key] = $value;
// But overwriting is allowed when a merge node is used in current block.
if ('<<' === $key) {
foreach ($value as $parsedValue) {
$output += $parsedValue;
}
} elseif ($allowOverwrite || !isset($output[$key])) {
if (null !== $tag) {
$output[$key] = new TaggedValue($tag, $value);
} else {
$output[$key] = $value;
}
} elseif (isset($output[$key])) {
@trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
}
$done = true;
break;
case '{':
// nested mapping
@@ -476,35 +562,45 @@ class Inline
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since lines
// are processed sequentially.
if (!isset($output[$key])) {
$output[$key] = $value;
// But overwriting is allowed when a merge node is used in current block.
if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key])) {
if (null !== $tag) {
$output[$key] = new TaggedValue($tag, $value);
} else {
$output[$key] = $value;
}
} elseif (isset($output[$key])) {
@trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
}
$done = true;
break;
case ':':
case ' ':
break;
default:
$value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
$value = self::parseScalar($mapping, $flags, array(',', '}'), $i, null === $tag, $references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since lines
// are processed sequentially.
if (!isset($output[$key])) {
$output[$key] = $value;
// But overwriting is allowed when a merge node is used in current block.
if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key])) {
if (null !== $tag) {
$output[$key] = new TaggedValue($tag, $value);
} else {
$output[$key] = $value;
}
} elseif (isset($output[$key])) {
@trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
}
$done = true;
--$i;
}
++$i;
if ($done) {
continue 2;
}
continue 2;
}
}
throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping));
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename);
}
/**
@@ -514,7 +610,7 @@ class Inline
* @param int $flags
* @param array $references
*
* @return string A YAML string
* @return mixed The evaluated YAML string
*
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
*/
@@ -532,11 +628,11 @@ class Inline
// an unquoted *
if (false === $value || '' === $value) {
throw new ParseException('A reference must contain at least one character.');
throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}
if (!array_key_exists($value, $references)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}
return $references[$value];
@@ -551,37 +647,97 @@ class Inline
return true;
case 'false' === $scalarLower:
return false;
// Optimise for returning strings.
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
case '!' === $scalar[0]:
switch (true) {
case 0 === strpos($scalar, '!str'):
@trigger_error(self::getDeprecationMessage('Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead.'), E_USER_DEPRECATED);
return (string) substr($scalar, 5);
case 0 === strpos($scalar, '!!str '):
return (string) substr($scalar, 6);
case 0 === strpos($scalar, '! '):
@trigger_error(self::getDeprecationMessage('Using the non-specific tag "!" is deprecated since Symfony 3.4 as its behavior will change in 4.0. It will force non-evaluating your values in 4.0. Use plain integers or !!float instead.'), E_USER_DEPRECATED);
return (int) self::parseScalar(substr($scalar, 2), $flags);
case 0 === strpos($scalar, '!php/object:'):
if (self::$objectSupport) {
@trigger_error(self::getDeprecationMessage('The !php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), E_USER_DEPRECATED);
return unserialize(substr($scalar, 12));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.');
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return;
case 0 === strpos($scalar, '!!php/object:'):
if (self::$objectSupport) {
@trigger_error('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead.', E_USER_DEPRECATED);
@trigger_error(self::getDeprecationMessage('The !!php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.1 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), E_USER_DEPRECATED);
return unserialize(substr($scalar, 13));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.');
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return;
case 0 === strpos($scalar, '!php/object'):
if (self::$objectSupport) {
return unserialize(self::parseScalar(substr($scalar, 12)));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return;
case 0 === strpos($scalar, '!php/const:'):
if (self::$constantSupport) {
@trigger_error(self::getDeprecationMessage('The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead.'), E_USER_DEPRECATED);
if (\defined($const = substr($scalar, 11))) {
return \constant($const);
}
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (self::$exceptionOnInvalidType) {
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return;
case 0 === strpos($scalar, '!php/const'):
if (self::$constantSupport) {
$i = 0;
if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) {
return \constant($const);
}
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (self::$exceptionOnInvalidType) {
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return;
case 0 === strpos($scalar, '!!float '):
return (float) substr($scalar, 8);
case 0 === strpos($scalar, '!!binary '):
return self::evaluateBinaryScalar(substr($scalar, 9));
default:
@trigger_error(self::getDeprecationMessage(sprintf('Using the unquoted scalar value "%s" is deprecated since Symfony 3.3 and will be considered as a tagged value in 4.0. You must quote it.', $scalar)), E_USER_DEPRECATED);
}
// Optimize for returning strings.
// no break
case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]):
switch (true) {
case Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
$scalar = str_replace('_', '', (string) $scalar);
// omitting the break / return as integers are handled in the next case
// no break
case ctype_digit($scalar):
$raw = $scalar;
$cast = (int) $scalar;
@@ -593,19 +749,25 @@ class Inline
return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
case is_numeric($scalar):
case preg_match(self::getHexRegex(), $scalar):
case Parser::preg_match(self::getHexRegex(), $scalar):
$scalar = str_replace('_', '', $scalar);
return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
case '.inf' === $scalarLower:
case '.nan' === $scalarLower:
return -log(0);
case '-.inf' === $scalarLower:
return log(0);
case 0 === strpos($scalar, '!!binary '):
return self::evaluateBinaryScalar(substr($scalar, 9));
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
return (float) str_replace(',', '', $scalar);
case preg_match(self::getTimestampRegex(), $scalar):
case Parser::preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
if (false !== strpos($scalar, ',')) {
@trigger_error(self::getDeprecationMessage('Using the comma as a group separator for floats is deprecated since Symfony 3.2 and will be removed in 4.0.'), E_USER_DEPRECATED);
}
return (float) str_replace(array(',', '_'), '', $scalar);
case Parser::preg_match(self::getTimestampRegex(), $scalar):
if (Yaml::PARSE_DATETIME & $flags) {
// When no timezone is provided in the parsed date, YAML spec says we must assume UTC.
return new \DateTime($scalar, new \DateTimeZone('UTC'));
}
@@ -616,9 +778,48 @@ class Inline
return $time;
}
default:
return (string) $scalar;
}
return (string) $scalar;
}
/**
* @param string $value
* @param int &$i
* @param int $flags
*
* @return null|string
*/
private static function parseTag($value, &$i, $flags)
{
if ('!' !== $value[$i]) {
return;
}
$tagLength = strcspn($value, " \t\n", $i + 1);
$tag = substr($value, $i + 1, $tagLength);
$nextOffset = $i + $tagLength + 1;
$nextOffset += strspn($value, ' ', $nextOffset);
// Is followed by a scalar
if ((!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], array('[', '{'), true)) && 'tagged' !== $tag) {
// Manage non-whitelisted scalars in {@link self::evaluateScalar()}
return;
}
// Built-in tags
if ($tag && '!' === $tag[0]) {
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}
if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
$i = $nextOffset;
return $tag;
}
throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}
/**
@@ -632,12 +833,12 @@ class Inline
{
$parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
if (0 !== (strlen($parsedBinaryData) % 4)) {
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData)));
if (0 !== (\strlen($parsedBinaryData) % 4)) {
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
if (!preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
return base64_decode($parsedBinaryData, true);
@@ -645,7 +846,7 @@ class Inline
private static function isBinaryString($value)
{
return !preg_match('//u', $value) || preg_match('/[^\x09-\x0d\x20-\xff]/', $value);
return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value);
}
/**
@@ -680,6 +881,21 @@ EOF;
*/
private static function getHexRegex()
{
return '~^0x[0-9a-f]++$~i';
return '~^0x[0-9a-f_]++$~i';
}
private static function getDeprecationMessage($message)
{
$message = rtrim($message, '.');
if (null !== self::$parsedFilename) {
$message .= ' in '.self::$parsedFilename;
}
if (-1 !== self::$parsedLineNumber) {
$message .= ' on line '.(self::$parsedLineNumber + 1);
}
return $message.'.';
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2016 Fabien Potencier
Copyright (c) 2004-2018 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -12,17 +12,21 @@
namespace Symfony\Component\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Tag\TaggedValue;
/**
* Parser parses YAML strings to convert them to PHP arrays.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since version 3.4
*/
class Parser
{
const TAG_PATTERN = '((?P<tag>![\w!.\/:-]+) +)?';
const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)';
const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
private $filename;
private $offset = 0;
private $totalNumberOfLines;
private $lines = array();
@@ -32,18 +36,48 @@ class Parser
private $skippedLineNumbers = array();
private $locallySkippedLineNumbers = array();
/**
* Constructor.
*
* @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
* @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser
*/
public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array())
public function __construct()
{
$this->offset = $offset;
$this->totalNumberOfLines = $totalNumberOfLines;
$this->skippedLineNumbers = $skippedLineNumbers;
if (\func_num_args() > 0) {
@trigger_error(sprintf('The constructor arguments $offset, $totalNumberOfLines, $skippedLineNumbers of %s are deprecated and will be removed in 4.0', self::class), E_USER_DEPRECATED);
$this->offset = func_get_arg(0);
if (\func_num_args() > 1) {
$this->totalNumberOfLines = func_get_arg(1);
}
if (\func_num_args() > 2) {
$this->skippedLineNumbers = func_get_arg(2);
}
}
}
/**
* Parses a YAML file into a PHP value.
*
* @param string $filename The path to the YAML file to be parsed
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
*
* @return mixed The YAML converted to a PHP value
*
* @throws ParseException If the file could not be read or the YAML is not valid
*/
public function parseFile($filename, $flags = 0)
{
if (!is_file($filename)) {
throw new ParseException(sprintf('File "%s" does not exist.', $filename));
}
if (!is_readable($filename)) {
throw new ParseException(sprintf('File "%s" cannot be read.', $filename));
}
$this->filename = $filename;
try {
return $this->parse(file_get_contents($filename), $flags);
} finally {
$this->filename = null;
}
}
/**
@@ -58,8 +92,8 @@ class Parser
*/
public function parse($value, $flags = 0)
{
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if (\is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if ($flags) {
$flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
@@ -68,75 +102,139 @@ class Parser
}
}
if (func_num_args() >= 3) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 3) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
if (func_get_arg(2)) {
$flags |= Yaml::PARSE_OBJECT;
}
}
if (func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
if (func_get_arg(3)) {
$flags |= Yaml::PARSE_OBJECT_FOR_MAP;
}
}
if (!preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
if (Yaml::PARSE_KEYS_AS_STRINGS & $flags) {
@trigger_error('Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.', E_USER_DEPRECATED);
}
$this->currentLineNb = -1;
$this->currentLine = '';
$value = $this->cleanup($value);
$this->lines = explode("\n", $value);
if (null === $this->totalNumberOfLines) {
$this->totalNumberOfLines = count($this->lines);
if (false === preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename);
}
$this->refs = array();
$mbEncoding = null;
$e = null;
$data = null;
if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
}
try {
$data = $this->doParse($value, $flags);
} catch (\Exception $e) {
} catch (\Throwable $e) {
}
if (null !== $mbEncoding) {
mb_internal_encoding($mbEncoding);
}
$this->lines = array();
$this->currentLine = '';
$this->refs = array();
$this->skippedLineNumbers = array();
$this->locallySkippedLineNumbers = array();
if (null !== $e) {
throw $e;
}
return $data;
}
private function doParse($value, $flags)
{
$this->currentLineNb = -1;
$this->currentLine = '';
$value = $this->cleanup($value);
$this->lines = explode("\n", $value);
$this->locallySkippedLineNumbers = array();
if (null === $this->totalNumberOfLines) {
$this->totalNumberOfLines = \count($this->lines);
}
if (!$this->moveToNextLine()) {
return null;
}
$data = array();
$context = null;
$allowOverwrite = false;
while ($this->moveToNextLine()) {
while ($this->isCurrentLineEmpty()) {
if (!$this->moveToNextLine()) {
return null;
}
}
// Resolves the tag and returns if end of the document
if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) {
return new TaggedValue($tag, '');
}
do {
if ($this->isCurrentLineEmpty()) {
continue;
}
// tab?
if ("\t" === $this->currentLine[0]) {
throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename);
$isRef = $mergeNode = false;
if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) {
if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
$context = 'sequence';
if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}
if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) {
@trigger_error($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'), E_USER_DEPRECATED);
}
// array
if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
$data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags);
} elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) {
$data[] = new TaggedValue(
$subTag,
$this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags)
);
} else {
if (isset($values['leadspaces'])
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
&& self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->trimTag($values['value']), $matches)
) {
// this is a compact notation element, add to next block and parse
$block = $values['value'];
if ($this->isNextLineIndented()) {
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1);
}
$data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
@@ -147,16 +245,25 @@ class Parser
if ($isRef) {
$this->refs[$isRef] = end($data);
}
} 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 (
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
&& (false === strpos($values['key'], ' #') || \in_array($values['key'][0], array('"', "'")))
) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine, $this->filename);
}
$context = 'mapping';
// force correct settings
Inline::parse(null, $flags, $this->refs);
try {
$key = Inline::parseScalar($values['key']);
$i = 0;
$evaluateKey = !(Yaml::PARSE_KEYS_AS_STRINGS & $flags);
// constants in key will be evaluated anyway
if (isset($values['key'][0]) && '!' === $values['key'][0] && Yaml::PARSE_CONSTANT & $flags) {
$evaluateKey = true;
}
$key = Inline::parseScalar($values['key'], 0, null, $i, $evaluateKey);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
@@ -164,41 +271,50 @@ class Parser
throw $e;
}
if (!\is_string($key) && !\is_int($key)) {
$keyType = is_numeric($key) ? 'numeric key' : 'non-string key';
@trigger_error($this->getDeprecationMessage(sprintf('Implicit casting of %s to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.', $keyType)), E_USER_DEPRECATED);
}
// Convert float keys to strings, to avoid being converted to integers by PHP
if (is_float($key)) {
if (\is_float($key)) {
$key = (string) $key;
}
if ('<<' === $key) {
if ('<<' === $key && (!isset($values['value']) || !self::preg_match('#^&(?P<ref>[^ ]+)#u', $values['value'], $refMatches))) {
$mergeNode = true;
$allowOverwrite = true;
if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
$refName = substr($values['value'], 1);
if (isset($values['value'][0]) && '*' === $values['value'][0]) {
$refName = substr(rtrim($values['value']), 1);
if (!array_key_exists($refName, $this->refs)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
$refValue = $this->refs[$refName];
if (!is_array($refValue)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) {
$refValue = (array) $refValue;
}
foreach ($refValue as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
if (!\is_array($refValue)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
$data += $refValue; // array union
} else {
if (isset($values['value']) && $values['value'] !== '') {
if (isset($values['value']) && '' !== $values['value']) {
$value = $values['value'];
} else {
$value = $this->getNextEmbedBlock();
}
$parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
if (!is_array($parsed)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) {
$parsed = (array) $parsed;
}
if (!\is_array($parsed)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
if (isset($parsed[0])) {
@@ -206,56 +322,75 @@ class Parser
// and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
// in the sequence override keys specified in later mapping nodes.
foreach ($parsed as $parsedItem) {
if (!is_array($parsedItem)) {
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) {
$parsedItem = (array) $parsedItem;
}
foreach ($parsedItem as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
if (!\is_array($parsedItem)) {
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename);
}
$data += $parsedItem; // array union
}
} else {
// If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
// current mapping, unless the key already exists in it.
foreach ($parsed as $key => $value) {
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
$data += $parsed; // array union
}
}
} elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
} elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}
$subTag = null;
if ($mergeNode) {
// Merge keys
} elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
} elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) {
// hash
// if next line is less indented or equal, then it means that the current value is null
if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = null;
if (null !== $subTag) {
$data[$key] = new TaggedValue($subTag, '');
} else {
$data[$key] = null;
}
} else {
@trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
}
} else {
$value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
if ('<<' === $key) {
$this->refs[$refMatches['ref']] = $value;
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) {
$value = (array) $value;
}
$data += $value;
} elseif ($allowOverwrite || !isset($data[$key])) {
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if (null !== $subTag) {
$data[$key] = new TaggedValue($subTag, $value);
} else {
$data[$key] = $value;
}
} else {
@trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
}
}
} else {
$value = $this->parseValue($values['value'], $flags, $context);
$value = $this->parseValue(rtrim($values['value']), $flags, $context);
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
} else {
@trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
}
}
if ($isRef) {
@@ -264,11 +399,15 @@ class Parser
} else {
// multiple documents are not supported
if ('---' === $this->currentLine) {
throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine);
throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename);
}
if ($deprecatedUsage = (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1])) {
@trigger_error($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'), E_USER_DEPRECATED);
}
// 1-liner optionally followed by newline(s)
if (is_string($value) && $this->lines[0] === trim($value)) {
if (\is_string($value) && $this->lines[0] === trim($value)) {
try {
$value = Inline::parse($this->lines[0], $flags, $this->refs);
} catch (ParseException $e) {
@@ -278,42 +417,60 @@ class Parser
throw $e;
}
if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}
return $value;
}
switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
break;
default:
$error = 'Unable to parse.';
// try to parse the value as a multi-line string as a last resort
if (0 === $this->currentLineNb) {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = false;
$value = '';
foreach ($this->lines as $line) {
// If the indentation is not consistent at offset 0, it is to be considered as a ParseError
if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
if ('' === trim($line)) {
$value .= "\n";
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
$value .= ' ';
}
if ('' !== trim($line) && '\\' === substr($line, -1)) {
$value .= ltrim(substr($line, 0, -1));
} elseif ('' !== trim($line)) {
$value .= trim($line);
}
if ('' === trim($line)) {
$previousLineWasNewline = true;
$previousLineWasTerminatedWithBackslash = false;
} elseif ('\\' === substr($line, -1)) {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = true;
} else {
$previousLineWasNewline = false;
$previousLineWasTerminatedWithBackslash = false;
}
}
try {
return Inline::parse(trim($value));
} catch (ParseException $e) {
// fall-through to the ParseException thrown below
}
}
throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
} while ($this->moveToNextLine());
if (null !== $tag) {
$data = new TaggedValue($tag, $data);
}
if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) {
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !\is_object($data) && 'mapping' === $context) {
$object = new \stdClass();
foreach ($data as $key => $value) {
@@ -338,18 +495,23 @@ class Parser
$skippedLineNumbers[] = $lineNumber;
}
$parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers);
$parser = new self();
$parser->offset = $offset;
$parser->totalNumberOfLines = $this->totalNumberOfLines;
$parser->skippedLineNumbers = $skippedLineNumbers;
$parser->refs = &$this->refs;
return $parser->parse($yaml, $flags);
return $parser->doParse($yaml, $flags);
}
/**
* Returns the current line number (takes the offset into account).
*
* @internal
*
* @return int The current line number
*/
private function getRealCurrentLineNb()
public function getRealCurrentLineNb()
{
$realCurrentLineNumber = $this->currentLineNb + $this->offset;
@@ -371,7 +533,7 @@ class Parser
*/
private function getCurrentLineIndentation()
{
return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' '));
return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' '));
}
/**
@@ -390,7 +552,7 @@ class Parser
$blockScalarIndentations = array();
if ($this->isBlockScalarHeader()) {
$blockScalarIndentations[] = $this->getCurrentLineIndentation();
$blockScalarIndentations[] = $oldLineIndentation;
}
if (!$this->moveToNextLine()) {
@@ -398,12 +560,32 @@ class Parser
}
if (null === $indentation) {
$newIndent = $this->getCurrentLineIndentation();
$newIndent = null;
$movements = 0;
do {
$EOF = false;
// empty and comment-like lines do not influence the indentation depth
if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
$EOF = !$this->moveToNextLine();
if (!$EOF) {
++$movements;
}
} else {
$newIndent = $this->getCurrentLineIndentation();
}
} while (!$EOF && null === $newIndent);
for ($i = 0; $i < $movements; ++$i) {
$this->moveToPreviousLine();
}
$unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem();
if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
} else {
$newIndent = $indentation;
@@ -412,6 +594,8 @@ class Parser
$data = array();
if ($this->getCurrentLineIndentation() >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
$data[] = $this->currentLine;
} else {
$this->moveToPreviousLine();
@@ -438,21 +622,21 @@ class Parser
$indent = $this->getCurrentLineIndentation();
// terminate all block scalars that are more indented than the current line
if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && trim($this->currentLine) !== '') {
if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && '' !== trim($this->currentLine)) {
foreach ($blockScalarIndentations as $key => $blockScalarIndentation) {
if ($blockScalarIndentation >= $this->getCurrentLineIndentation()) {
if ($blockScalarIndentation >= $indent) {
unset($blockScalarIndentations[$key]);
}
}
}
if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) {
$blockScalarIndentations[] = $this->getCurrentLineIndentation();
$blockScalarIndentations[] = $indent;
}
$previousLineIndentation = $indent;
if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
$this->moveToPreviousLine();
break;
}
@@ -462,27 +646,16 @@ class Parser
continue;
}
// we ignore "comment" lines only when we are not inside a scalar block
if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) {
// remember ignored comment lines (they are used later in nested
// parser calls to determine real line numbers)
//
// CAUTION: beware to not populate the global property here as it
// will otherwise influence the getRealCurrentLineNb() call here
// for consecutive comment lines and subsequent embedded blocks
$this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb();
continue;
}
if ($indent >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} elseif ($this->isCurrentLineComment()) {
$data[] = $this->currentLine;
} elseif (0 == $indent) {
$this->moveToPreviousLine();
break;
} else {
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
}
@@ -496,7 +669,7 @@ class Parser
*/
private function moveToNextLine()
{
if ($this->currentLineNb >= count($this->lines) - 1) {
if ($this->currentLineNb >= \count($this->lines) - 1) {
return false;
}
@@ -542,29 +715,75 @@ class Parser
}
if (!array_key_exists($value, $this->refs)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine);
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
}
return $this->refs[$value];
}
if (preg_match('/^'.self::TAG_PATTERN.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
if (self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
$data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers));
if (isset($matches['tag']) && '!!binary' === $matches['tag']) {
return Inline::evaluateBinaryScalar($data);
if ('' !== $matches['tag']) {
if ('!!binary' === $matches['tag']) {
return Inline::evaluateBinaryScalar($data);
} elseif ('tagged' === $matches['tag']) {
return new TaggedValue(substr($matches['tag'], 1), $data);
} elseif ('!' !== $matches['tag']) {
@trigger_error($this->getDeprecationMessage(sprintf('Using the custom tag "%s" for the value "%s" is deprecated since Symfony 3.3. It will be replaced by an instance of %s in 4.0.', $matches['tag'], $data, TaggedValue::class)), E_USER_DEPRECATED);
}
}
return $data;
}
try {
$quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null;
// do not take following lines into account when the current line is a quoted single line value
if (null !== $quotation && self::preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) {
return Inline::parse($value, $flags, $this->refs);
}
$lines = array();
while ($this->moveToNextLine()) {
// unquoted strings end before the first unindented line
if (null === $quotation && 0 === $this->getCurrentLineIndentation()) {
$this->moveToPreviousLine();
break;
}
$lines[] = trim($this->currentLine);
// quoted string values end with a line that is terminated with the quotation character
if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) {
break;
}
}
for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
if ('' === $lines[$i]) {
$value .= "\n";
$previousLineBlank = true;
} elseif ($previousLineBlank) {
$value .= $lines[$i];
$previousLineBlank = false;
} else {
$value .= ' '.$lines[$i];
$previousLineBlank = false;
}
}
Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
$parsedValue = Inline::parse($value, $flags, $this->refs);
if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
throw new ParseException('A colon cannot be used in an unquoted mapping value.');
if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}
return $parsedValue;
@@ -606,8 +825,8 @@ class Parser
// determine indentation if not specified
if (0 === $indentation) {
if (preg_match('/^ +/', $this->currentLine, $matches)) {
$indentation = strlen($matches[0]);
if (self::preg_match('/^ +/', $this->currentLine, $matches)) {
$indentation = \strlen($matches[0]);
}
}
@@ -617,10 +836,10 @@ class Parser
while (
$notEOF && (
$isCurrentLineBlank ||
preg_match($pattern, $this->currentLine, $matches)
self::preg_match($pattern, $this->currentLine, $matches)
)
) {
if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) {
if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) {
$blockLines[] = substr($this->currentLine, $indentation);
} elseif ($isCurrentLineBlank) {
$blockLines[] = '';
@@ -650,7 +869,7 @@ class Parser
$previousLineIndented = false;
$previousLineBlank = false;
for ($i = 0; $i < count($blockLines); ++$i) {
for ($i = 0, $blockLinesCount = \count($blockLines); $i < $blockLinesCount; ++$i) {
if ('' === $blockLines[$i]) {
$text .= "\n";
$previousLineIndented = false;
@@ -695,22 +914,25 @@ class Parser
private function isNextLineIndented()
{
$currentIndentation = $this->getCurrentLineIndentation();
$EOF = !$this->moveToNextLine();
$movements = 0;
while (!$EOF && $this->isCurrentLineEmpty()) {
do {
$EOF = !$this->moveToNextLine();
}
if (!$EOF) {
++$movements;
}
} while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));
if ($EOF) {
return false;
}
$ret = false;
if ($this->getCurrentLineIndentation() > $currentIndentation) {
$ret = true;
}
$ret = $this->getCurrentLineIndentation() > $currentIndentation;
$this->moveToPreviousLine();
for ($i = 0; $i < $movements; ++$i) {
$this->moveToPreviousLine();
}
return $ret;
}
@@ -745,7 +967,7 @@ class Parser
//checking explicitly the first char of the trim is faster than loops or strpos
$ltrimmedLine = ltrim($this->currentLine, ' ');
return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#';
return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0];
}
private function isCurrentLineLastLineInDocument()
@@ -771,7 +993,7 @@ class Parser
// remove leading comments
$trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
if ($count == 1) {
if (1 === $count) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
$value = $trimmedValue;
@@ -779,7 +1001,7 @@ class Parser
// remove start of the document marker (---)
$trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
if ($count == 1) {
if (1 === $count) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
$value = $trimmedValue;
@@ -799,26 +1021,25 @@ class Parser
private function isNextLineUnIndentedCollection()
{
$currentIndentation = $this->getCurrentLineIndentation();
$notEOF = $this->moveToNextLine();
$movements = 0;
while ($notEOF && $this->isCurrentLineEmpty()) {
$notEOF = $this->moveToNextLine();
}
do {
$EOF = !$this->moveToNextLine();
if (false === $notEOF) {
if (!$EOF) {
++$movements;
}
} while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));
if ($EOF) {
return false;
}
$ret = false;
if (
$this->getCurrentLineIndentation() == $currentIndentation
&&
$this->isStringUnIndentedCollectionItem()
) {
$ret = true;
}
$ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();
$this->moveToPreviousLine();
for ($i = 0; $i < $movements; ++$i) {
$this->moveToPreviousLine();
}
return $ret;
}
@@ -840,6 +1061,100 @@ class Parser
*/
private function isBlockScalarHeader()
{
return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
}
/**
* A local wrapper for `preg_match` which will throw a ParseException if there
* is an internal error in the PCRE engine.
*
* This avoids us needing to check for "false" every time PCRE is used
* in the YAML engine
*
* @throws ParseException on a PCRE internal error
*
* @see preg_last_error()
*
* @internal
*/
public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0)
{
if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) {
switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
break;
default:
$error = 'Error.';
}
throw new ParseException($error);
}
return $ret;
}
/**
* Trim the tag on top of the value.
*
* Prevent values such as `!foo {quz: bar}` to be considered as
* a mapping block.
*/
private function trimTag($value)
{
if ('!' === $value[0]) {
return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' ');
}
return $value;
}
private function getLineTag($value, $flags, $nextLineCheck = true)
{
if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) {
return;
}
if ($nextLineCheck && !$this->isNextLineIndented()) {
return;
}
$tag = substr($matches['tag'], 1);
// Built-in tags
if ($tag && '!' === $tag[0]) {
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}
if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
return $tag;
}
throw new ParseException(sprintf('Tags support is not enabled. You must use the flag `Yaml::PARSE_CUSTOM_TAGS` to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
}
private function getDeprecationMessage($message)
{
$message = rtrim($message, '.');
if (null !== $this->filename) {
$message .= ' in '.$this->filename;
}
$message .= ' on line '.($this->getRealCurrentLineNb() + 1);
return $message.'.';
}
}

48
vendor/symfony/yaml/Tag/TaggedValue.php vendored Normal file
View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Yaml\Tag;
/**
* @author Nicolas Grekas <p@tchwork.com>
* @author Guilhem N. <egetick@gmail.com>
*/
final class TaggedValue
{
private $tag;
private $value;
/**
* @param string $tag
* @param mixed $value
*/
public function __construct($tag, $value)
{
$this->tag = $tag;
$this->value = $value;
}
/**
* @return string
*/
public function getTag()
{
return $this->tag;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Yaml\Tests\Command;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Yaml\Command\LintCommand;
/**
* Tests the YamlLintCommand.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class LintCommandTest extends TestCase
{
private $files;
public function testLintCorrectFile()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('foo: bar');
$ret = $tester->execute(array('filename' => $filename), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false));
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
$this->assertRegExp('/^\/\/ OK in /', trim($tester->getDisplay()));
}
public function testLintIncorrectFile()
{
$incorrectContent = '
foo:
bar';
$tester = $this->createCommandTester();
$filename = $this->createFile($incorrectContent);
$ret = $tester->execute(array('filename' => $filename), array('decorated' => false));
$this->assertEquals(1, $ret, 'Returns 1 in case of error');
$this->assertContains('Unable to parse at line 3 (near "bar").', trim($tester->getDisplay()));
}
public function testConstantAsKey()
{
$yaml = <<<YAML
!php/const 'Symfony\Component\Yaml\Tests\Command\Foo::TEST': bar
YAML;
$ret = $this->createCommandTester()->execute(array('filename' => $this->createFile($yaml)), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false));
$this->assertSame(0, $ret, 'lint:yaml exits with code 0 in case of success');
}
public function testCustomTags()
{
$yaml = <<<YAML
foo: !my_tag {foo: bar}
YAML;
$ret = $this->createCommandTester()->execute(array('filename' => $this->createFile($yaml), '--parse-tags' => true), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false));
$this->assertSame(0, $ret, 'lint:yaml exits with code 0 in case of success');
}
public function testCustomTagsError()
{
$yaml = <<<YAML
foo: !my_tag {foo: bar}
YAML;
$ret = $this->createCommandTester()->execute(array('filename' => $this->createFile($yaml)), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false));
$this->assertSame(1, $ret, 'lint:yaml exits with code 1 in case of error');
}
/**
* @expectedException \RuntimeException
*/
public function testLintFileNotReadable()
{
$tester = $this->createCommandTester();
$filename = $this->createFile('');
unlink($filename);
$ret = $tester->execute(array('filename' => $filename), array('decorated' => false));
}
/**
* @return string Path to the new file
*/
private function createFile($content)
{
$filename = tempnam(sys_get_temp_dir().'/framework-yml-lint-test', 'sf-');
file_put_contents($filename, $content);
$this->files[] = $filename;
return $filename;
}
/**
* @return CommandTester
*/
protected function createCommandTester()
{
$application = new Application();
$application->add(new LintCommand());
$command = $application->find('lint:yaml');
return new CommandTester($command);
}
protected function setUp()
{
$this->files = array();
@mkdir(sys_get_temp_dir().'/framework-yml-lint-test');
}
protected function tearDown()
{
foreach ($this->files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
rmdir(sys_get_temp_dir().'/framework-yml-lint-test');
}
}
class Foo
{
const TEST = 'foo';
}

View File

@@ -11,11 +11,12 @@
namespace Symfony\Component\Yaml\Tests;
use Symfony\Component\Yaml\Parser;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Dumper;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Yaml;
class DumperTest extends \PHPUnit_Framework_TestCase
class DumperTest extends TestCase
{
protected $parser;
protected $dumper;
@@ -209,7 +210,7 @@ EOF;
{
$dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_OBJECT);
$this->assertEquals('{ foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $dump, '->dump() is able to dump objects');
$this->assertEquals('{ foo: !php/object \'O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}\', bar: 1 }', $dump, '->dump() is able to dump objects');
}
/**
@@ -219,7 +220,7 @@ EOF;
{
$dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true);
$this->assertEquals('{ foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $dump, '->dump() is able to dump objects');
$this->assertEquals('{ foo: !php/object \'O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}\', bar: 1 }', $dump, '->dump() is able to dump objects');
}
public function testObjectSupportDisabledButNoExceptions()
@@ -246,6 +247,24 @@ EOF;
$this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true);
}
public function testEmptyArray()
{
$dump = $this->dumper->dump(array());
$this->assertEquals('{ }', $dump);
$dump = $this->dumper->dump(array(), 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
$this->assertEquals('[]', $dump);
$dump = $this->dumper->dump(array(), 9, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
$this->assertEquals('[]', $dump);
$dump = $this->dumper->dump(new \ArrayObject(), 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE | Yaml::DUMP_OBJECT_AS_MAP);
$this->assertEquals('{ }', $dump);
$dump = $this->dumper->dump(new \stdClass(), 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE | Yaml::DUMP_OBJECT_AS_MAP);
$this->assertEquals('{ }', $dump);
}
/**
* @dataProvider getEscapeSequences
*/
@@ -257,23 +276,25 @@ EOF;
public function getEscapeSequences()
{
return array(
'null' => array("\t\\0", '"\t\\\\0"'),
'bell' => array("\t\\a", '"\t\\\\a"'),
'backspace' => array("\t\\b", '"\t\\\\b"'),
'horizontal-tab' => array("\t\\t", '"\t\\\\t"'),
'line-feed' => array("\t\\n", '"\t\\\\n"'),
'vertical-tab' => array("\t\\v", '"\t\\\\v"'),
'form-feed' => array("\t\\f", '"\t\\\\f"'),
'carriage-return' => array("\t\\r", '"\t\\\\r"'),
'escape' => array("\t\\e", '"\t\\\\e"'),
'space' => array("\t\\ ", '"\t\\\\ "'),
'double-quote' => array("\t\\\"", '"\t\\\\\\""'),
'slash' => array("\t\\/", '"\t\\\\/"'),
'backslash' => array("\t\\\\", '"\t\\\\\\\\"'),
'next-line' => array("\t\\N", '"\t\\\\N"'),
'non-breaking-space' => array("\t\\<EFBFBD>", '"\t\\\\<5C>"'),
'line-separator' => array("\t\\L", '"\t\\\\L"'),
'paragraph-separator' => array("\t\\P", '"\t\\\\P"'),
'empty string' => array('', "''"),
'null' => array("\x0", '"\\0"'),
'bell' => array("\x7", '"\\a"'),
'backspace' => array("\x8", '"\\b"'),
'horizontal-tab' => array("\t", '"\\t"'),
'line-feed' => array("\n", '"\\n"'),
'vertical-tab' => array("\v", '"\\v"'),
'form-feed' => array("\xC", '"\\f"'),
'carriage-return' => array("\r", '"\\r"'),
'escape' => array("\x1B", '"\\e"'),
'space' => array(' ', "' '"),
'double-quote' => array('"', "'\"'"),
'slash' => array('/', '/'),
'backslash' => array('\\', '\\'),
'next-line' => array("\xC2\x85", '"\\N"'),
'non-breaking-space' => array("\xc2\xa0", '"\\_"'),
'line-separator' => array("\xE2\x80\xA8", '"\\L"'),
'paragraph-separator' => array("\xE2\x80\xA9", '"\\P"'),
'colon' => array(':', "':'"),
);
}
@@ -330,19 +351,123 @@ EOF;
return $tests;
}
public function testDumpingArrayObjectInstancesRespectsInlineLevel()
{
$deep = new \ArrayObject(array('deep1' => 'd', 'deep2' => 'e'));
$inner = new \ArrayObject(array('inner1' => 'b', 'inner2' => 'c', 'inner3' => $deep));
$outer = new \ArrayObject(array('outer1' => 'a', 'outer2' => $inner));
$yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP);
$expected = <<<YAML
outer1: a
outer2:
inner1: b
inner2: c
inner3: { deep1: d, deep2: e }
YAML;
$this->assertSame($expected, $yaml);
}
public function testDumpingArrayObjectInstancesWithNumericKeysInlined()
{
$deep = new \ArrayObject(array('d', 'e'));
$inner = new \ArrayObject(array('b', 'c', $deep));
$outer = new \ArrayObject(array('a', $inner));
$yaml = $this->dumper->dump($outer, 0, 0, Yaml::DUMP_OBJECT_AS_MAP);
$expected = <<<YAML
{ 0: a, 1: { 0: b, 1: c, 2: { 0: d, 1: e } } }
YAML;
$this->assertSame($expected, $yaml);
}
public function testDumpingArrayObjectInstancesWithNumericKeysRespectsInlineLevel()
{
$deep = new \ArrayObject(array('d', 'e'));
$inner = new \ArrayObject(array('b', 'c', $deep));
$outer = new \ArrayObject(array('a', $inner));
$yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP);
$expected = <<<YAML
0: a
1:
0: b
1: c
2: { 0: d, 1: e }
YAML;
$this->assertEquals($expected, $yaml);
}
public function testDumpEmptyArrayObjectInstanceAsMap()
{
$this->assertSame('{ }', $this->dumper->dump(new \ArrayObject(), 2, 0, Yaml::DUMP_OBJECT_AS_MAP));
}
public function testDumpEmptyStdClassInstanceAsMap()
{
$this->assertSame('{ }', $this->dumper->dump(new \stdClass(), 2, 0, Yaml::DUMP_OBJECT_AS_MAP));
}
public function testDumpingStdClassInstancesRespectsInlineLevel()
{
$deep = new \stdClass();
$deep->deep1 = 'd';
$deep->deep2 = 'e';
$inner = new \stdClass();
$inner->inner1 = 'b';
$inner->inner2 = 'c';
$inner->inner3 = $deep;
$outer = new \stdClass();
$outer->outer1 = 'a';
$outer->outer2 = $inner;
$yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP);
$expected = <<<YAML
outer1: a
outer2:
inner1: b
inner2: c
inner3: { deep1: d, deep2: e }
YAML;
$this->assertSame($expected, $yaml);
}
public function testDumpMultiLineStringAsScalarBlock()
{
$data = array(
'data' => array(
'single_line' => 'foo bar baz',
'multi_line' => "foo\nline with trailing spaces:\n \nbar\r\ninteger like line:\n123456789\nempty line:\n\nbaz",
'multi_line' => "foo\nline with trailing spaces:\n \nbar\ninteger like line:\n123456789\nempty line:\n\nbaz",
'multi_line_with_carriage_return' => "foo\nbar\r\nbaz",
'nested_inlined_multi_line_string' => array(
'inlined_multi_line' => "foo\nbar\r\nempty line:\n\nbaz",
),
),
);
$this->assertSame(file_get_contents(__DIR__.'/Fixtures/multiple_lines_as_literal_block.yml'), $this->dumper->dump($data, 3, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
$this->assertSame(file_get_contents(__DIR__.'/Fixtures/multiple_lines_as_literal_block.yml'), $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
}
public function testDumpMultiLineStringAsScalarBlockWhenFirstLineHasLeadingSpace()
{
$data = array(
'data' => array(
'multi_line' => " the first line has leading spaces\nThe second line does not.",
),
);
$this->assertSame(file_get_contents(__DIR__.'/Fixtures/multiple_lines_as_literal_block_leading_space_in_first_line.yml'), $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
}
public function testCarriageReturnIsMaintainedWhenDumpingAsMultiLineLiteralBlock()
{
$this->assertSame("- \"a\\r\\nb\\nc\"\n", $this->dumper->dump(array("a\r\nb\nc"), 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
}
/**

View File

@@ -509,24 +509,31 @@ test: Integers
spec: 2.19
yaml: |
canonical: 12345
decimal: +12,345
octal: 014
hexadecimal: 0xC
php: |
array(
'canonical' => 12345,
'decimal' => 12345.0,
'octal' => 014,
'hexadecimal' => 0xC
)
---
test: Decimal Integer
deprecated: true
spec: 2.19
yaml: |
decimal: +12,345
php: |
array(
'decimal' => 12345.0,
)
---
# FIX: spec shows parens around -inf and NaN
test: Floating point
spec: 2.20
yaml: |
canonical: 1.23015e+3
exponential: 12.3015e+02
fixed: 1,230.15
negative infinity: -.inf
not a number: .NaN
float as whole number: !!float 1
@@ -534,25 +541,19 @@ php: |
array(
'canonical' => 1230.15,
'exponential' => 1230.15,
'fixed' => 1230.15,
'negative infinity' => log(0),
'not a number' => -log(0),
'float as whole number' => (float) 1
)
---
test: Miscellaneous
spec: 2.21
test: Fixed Floating point
deprecated: true
spec: 2.20
yaml: |
null: ~
true: true
false: false
string: '12345'
fixed: 1,230.15
php: |
array(
'' => null,
1 => true,
0 => false,
'string' => '12345'
'fixed' => 1230.15,
)
---
test: Timestamps
@@ -591,7 +592,7 @@ test: Various explicit families
todo: true
spec: 2.23
yaml: |
not-date: !str 2002-04-28
not-date: !!str 2002-04-28
picture: !binary |
R0lGODlhDAAMAIQAAP//9/X
17unp5WZmZgAAAOfn515eXv
@@ -927,10 +928,11 @@ documents: 2
---
test: Explicit typing
deprecated: Using the non-specific tag "!" is deprecated since Symfony 3.4 as its behavior will change in 4.0.
yaml: |
integer: 12
also int: ! "12"
string: !str 12
string: !!str 12
php: |
array( 'integer' => 12, 'also int' => 12, 'string' => '12' )
---
@@ -962,7 +964,7 @@ documents: 2
test: Type family under yaml.org
yaml: |
# The URI is 'tag:yaml.org,2002:str'
- !str a Unicode string
- !!str a Unicode string
php: |
array( 'a Unicode string' )
---
@@ -1349,7 +1351,7 @@ yaml: |
second: 12 ## This is an integer.
third: !str 12 ## This is a string.
third: !!str 12 ## This is a string.
span: this contains
six spaces
@@ -1418,7 +1420,7 @@ yaml: |
# The following scalars
# are loaded to the
# string value '1' '2'.
- !str 12
- !!str 12
- '12'
- "12"
- "\
@@ -1517,44 +1519,46 @@ ruby: |
}
---
test: Boolean
yaml: |
false: used as key
logical: true
answer: false
php: |
array(
false => 'used as key',
'logical' => true,
'answer' => false
)
---
test: Integer
yaml: |
canonical: 12345
decimal: +12,345
octal: 014
hexadecimal: 0xC
php: |
array(
'canonical' => 12345,
'decimal' => 12345.0,
'octal' => 12,
'hexadecimal' => 12
)
---
test: Decimal
deprecated: true
yaml: |
decimal: +12,345
php: |
array(
'decimal' => 12345.0,
)
---
test: Fixed Float
deprecated: true
yaml: |
fixed: 1,230.15
php: |
array(
'fixed' => 1230.15,
)
---
test: Float
yaml: |
canonical: 1.23015e+3
exponential: 12.3015e+02
fixed: 1,230.15
negative infinity: -.inf
not a number: .NaN
php: |
array(
'canonical' => 1230.15,
'exponential' => 1230.15,
'fixed' => 1230.15,
'negative infinity' => log(0),
'not a number' => -log(0)
)

View File

@@ -52,10 +52,10 @@ php: |
test: Forcing Strings
brief: >
Any YAML type can be forced into a string using the
explicit !str method.
explicit !!str method.
yaml: |
date string: !str 2001-08-01
number string: !str 192
date string: !!str 2001-08-01
number string: !!str 192
php: |
array(
'date string' => '2001-08-01',
@@ -176,28 +176,38 @@ brief: >
yaml: |
zero: 0
simple: 12
one-thousand: 1,000
negative one-thousand: -1,000
php: |
array(
'zero' => 0,
'simple' => 12,
'one-thousand' => 1000.0,
'negative one-thousand' => -1000.0
)
---
test: Integers as Map Keys
test: Positive Big Integer
deprecated: true
dump_skip: true
brief: >
An integer can be used a dictionary key.
An integer is a series of numbers, optionally
starting with a positive or negative sign. Integers
may also contain commas for readability.
yaml: |
1: one
2: two
3: three
one-thousand: 1,000
php: |
array(
1 => 'one',
2 => 'two',
3 => 'three'
'one-thousand' => 1000.0,
)
---
test: Negative Big Integer
deprecated: true
dump_skip: true
brief: >
An integer is a series of numbers, optionally
starting with a positive or negative sign. Integers
may also contain commas for readability.
yaml: |
negative one-thousand: -1,000
php: |
array(
'negative one-thousand' => -1000.0
)
---
test: Floats
@@ -208,15 +218,27 @@ brief: >
positive and negative infinity and "not a number."
yaml: |
a simple float: 2.00
larger float: 1,000.09
scientific notation: 1.00009e+3
php: |
array(
'a simple float' => 2.0,
'larger float' => 1000.09,
'scientific notation' => 1000.09
)
---
test: Larger Float
dump_skip: true
deprecated: true
brief: >
Floats are represented by numbers with decimals,
allowing for scientific notation, as well as
positive and negative infinity and "not a number."
yaml: |
larger float: 1,000.09
php: |
array(
'larger float' => 1000.09,
)
---
test: Time
todo: true
brief: >

View File

@@ -0,0 +1,11 @@
--- %YAML:1.0
test: Miscellaneous
spec: 2.21
yaml: |
true: true
false: false
php: |
array(
'true' => true,
'false' => false,
)

View File

@@ -4,7 +4,7 @@ yaml: |
php: |
"\\0 \\ \\a \\b \\n"
---
test: null
test: 'null'
yaml: |
"\0"
php: |

View File

@@ -0,0 +1,23 @@
--- %YAML:1.0
test: Miscellaneous
spec: 2.21
yaml: |
true: true
false: false
php: |
array(
1 => true,
0 => false,
)
---
test: Boolean
yaml: |
false: used as key
logical: true
answer: false
php: |
array(
false => 'used as key',
'logical' => true,
'answer' => false
)

View File

@@ -0,0 +1,2 @@
- legacyBooleanMappingKeys
- legacyNullMappingKey

View File

@@ -0,0 +1,9 @@
--- %YAML:1.0
test: Miscellaneous
spec: 2.21
yaml: |
null: ~
php: |
array(
'' => null,
)

View File

@@ -10,5 +10,5 @@ data:
empty line:
baz
nested_inlined_multi_line_string:
inlined_multi_line: "foo\nbar\r\nempty line:\n\nbaz"
multi_line_with_carriage_return: "foo\nbar\r\nbaz"
nested_inlined_multi_line_string: { inlined_multi_line: "foo\nbar\r\nempty line:\n\nbaz" }

View File

@@ -0,0 +1,4 @@
data:
multi_line: |4
the first line has leading spaces
The second line does not.

View File

@@ -0,0 +1,3 @@
- booleanMappingKeys
- numericMappingKeys
- nullMappingKey

View File

@@ -0,0 +1,18 @@
- escapedCharacters
- sfComments
- sfCompact
- sfTests
- sfObjects
- sfMergeKey
- sfQuotes
- YtsAnchorAlias
- YtsBasicTests
- YtsBlockMapping
- YtsDocumentSeparator
- YtsErrorTests
- YtsFlowCollections
- YtsFoldedScalars
- YtsNullsAndEmpties
- YtsSpecificationExamples
- YtsTypeTransfers
- unindentedCollections

View File

@@ -0,0 +1,9 @@
--- %YAML:1.0
test: Miscellaneous
spec: 2.21
yaml: |
null: ~
php: |
array(
'null' => null,
)

View File

@@ -0,0 +1,23 @@
--- %YAML:1.0
test: A sequence with an unordered array
brief: >
A sequence with an unordered array
yaml: |
1: foo
0: bar
php: |
array(1 => 'foo', 0 => 'bar')
---
test: Integers as Map Keys
brief: >
An integer can be used as dictionary key.
yaml: |
1: one
2: two
3: three
php: |
array(
1 => 'one',
2 => 'two',
3 => 'three'
)

View File

@@ -10,19 +10,18 @@ yaml: |
a: Steve
b: Clark
c: Brian
e: notnull
bar:
a: before
d: other
e: ~
<<: *foo
b: new
x: Oren
c:
foo: bar
foo: ignore
bar: foo
duplicate:
foo: bar
foo: ignore
bar_inline: {a: before, d: other, <<: *foo, b: new, x: Oren, c: { foo: bar, bar: foo}}
foo2: &foo2
a: Ballmer
ding: &dong [ fi, fei, fo, fam]
@@ -44,15 +43,19 @@ yaml: |
p: 12345
z:
<<: *nestedref
head_inline: &head_inline { <<: [ *foo , *dong , *foo2 ] }
recursive_inline: { <<: *head_inline, c: { <<: *foo2 } }
php: |
array(
'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'),
'bar' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'),
'duplicate' => array('foo' => 'bar'),
'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull'),
'bar' => array('a' => 'before', 'd' => 'other', 'e' => null, 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'),
'bar_inline' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'e' => 'notnull', 'x' => 'Oren'),
'foo2' => array('a' => 'Ballmer'),
'ding' => array('fi', 'fei', 'fo', 'fam'),
'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'),
'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam'),
'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'),
'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'),
'taz' => array('a' => 'Steve', 'w' => array('p' => 1234)),
'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345))
'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)),
'head_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'),
'recursive_inline' => array('a' => 'Steve', 'b' => 'Clark', 'c' => array('a' => 'Ballmer'), 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'),
)

View File

@@ -96,15 +96,6 @@ yaml: |
php: |
array('foo', array('bar' => array('bar' => 'foo')))
---
test: A sequence with an unordered array
brief: >
A sequence with an unordered array
yaml: |
1: foo
0: bar
php: |
array(1 => 'foo', 0 => 'bar')
---
test: Octal
brief: as in spec example 2.19, octal value is converted
yaml: |

View File

@@ -11,30 +11,96 @@
namespace Symfony\Component\Yaml\Tests;
use Symfony\Bridge\PhpUnit\ErrorAssert;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Inline;
use Symfony\Component\Yaml\Yaml;
class InlineTest extends \PHPUnit_Framework_TestCase
class InlineTest extends TestCase
{
protected function setUp()
{
Inline::initialize(0, 0);
}
/**
* @dataProvider getTestsForParse
*/
public function testParse($yaml, $value)
public function testParse($yaml, $value, $flags = 0)
{
$this->assertSame($value, Inline::parse($yaml), sprintf('::parse() converts an inline YAML to a PHP structure (%s)', $yaml));
$this->assertSame($value, Inline::parse($yaml, $flags), sprintf('::parse() converts an inline YAML to a PHP structure (%s)', $yaml));
}
/**
* @dataProvider getTestsForParseWithMapObjects
*/
public function testParseWithMapObjects($yaml, $value)
public function testParseWithMapObjects($yaml, $value, $flags = Yaml::PARSE_OBJECT_FOR_MAP)
{
$actual = Inline::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP);
$actual = Inline::parse($yaml, $flags);
$this->assertSame(serialize($value), serialize($actual));
}
/**
* @dataProvider getTestsForParsePhpConstants
*/
public function testParsePhpConstants($yaml, $value)
{
$actual = Inline::parse($yaml, Yaml::PARSE_CONSTANT);
$this->assertSame($value, $actual);
}
public function getTestsForParsePhpConstants()
{
return array(
array('!php/const Symfony\Component\Yaml\Yaml::PARSE_CONSTANT', Yaml::PARSE_CONSTANT),
array('!php/const PHP_INT_MAX', PHP_INT_MAX),
array('[!php/const PHP_INT_MAX]', array(PHP_INT_MAX)),
array('{ foo: !php/const PHP_INT_MAX }', array('foo' => PHP_INT_MAX)),
array('!php/const NULL', null),
);
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage The constant "WRONG_CONSTANT" is not defined
*/
public function testParsePhpConstantThrowsExceptionWhenUndefined()
{
Inline::parse('!php/const WRONG_CONSTANT', Yaml::PARSE_CONSTANT);
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessageRegExp #The string "!php/const PHP_INT_MAX" could not be parsed as a constant.*#
*/
public function testParsePhpConstantThrowsExceptionOnInvalidType()
{
Inline::parse('!php/const PHP_INT_MAX', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
}
/**
* @group legacy
* @expectedDeprecation The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead on line 1.
* @dataProvider getTestsForParseLegacyPhpConstants
*/
public function testDeprecatedConstantTag($yaml, $expectedValue)
{
$this->assertSame($expectedValue, Inline::parse($yaml, Yaml::PARSE_CONSTANT));
}
public function getTestsForParseLegacyPhpConstants()
{
return array(
array('!php/const:Symfony\Component\Yaml\Yaml::PARSE_CONSTANT', Yaml::PARSE_CONSTANT),
array('!php/const:PHP_INT_MAX', PHP_INT_MAX),
array('[!php/const:PHP_INT_MAX]', array(PHP_INT_MAX)),
array('{ foo: !php/const:PHP_INT_MAX }', array('foo' => PHP_INT_MAX)),
array('!php/const:NULL', null),
);
}
/**
* @group legacy
* @dataProvider getTestsForParseWithMapObjects
@@ -49,11 +115,11 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getTestsForDump
*/
public function testDump($yaml, $value)
public function testDump($yaml, $value, $parseFlags = 0)
{
$this->assertEquals($yaml, Inline::dump($value), sprintf('::dump() converts a PHP structure to an inline YAML (%s)', $yaml));
$this->assertSame($value, Inline::parse(Inline::dump($value)), 'check consistency');
$this->assertSame($value, Inline::parse(Inline::dump($value), $parseFlags), 'check consistency');
}
public function testDumpNumericValueWithLocale()
@@ -127,6 +193,16 @@ class InlineTest extends \PHPUnit_Framework_TestCase
Inline::parse($value);
}
/**
* @group legacy
* @expectedDeprecation Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since Symfony 3.2 and will throw a ParseException in 4.0 on line 1.
* throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
*/
public function testParseMappingKeyWithColonNotFollowedBySpace()
{
Inline::parse('{1:""}');
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
@@ -207,7 +283,7 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage A reference must contain at least one character.
* @expectedExceptionMessage A reference must contain at least one character at line 1.
*/
public function testParseUnquotedAsterisk()
{
@@ -216,7 +292,7 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage A reference must contain at least one character.
* @expectedExceptionMessage A reference must contain at least one character at line 1.
*/
public function testParseUnquotedAsteriskFollowedByAComment()
{
@@ -225,11 +301,16 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getReservedIndicators
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
*/
public function testParseUnquotedScalarStartingWithReservedIndicator($indicator)
{
if (method_exists($this, 'expectExceptionMessage')) {
$this->expectException(ParseException::class);
$this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator));
} else {
$this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator));
}
Inline::parse(sprintf('{ foo: %sfoo }', $indicator));
}
@@ -240,11 +321,16 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getScalarIndicators
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
*/
public function testParseUnquotedScalarStartingWithScalarIndicator($indicator)
{
if (method_exists($this, 'expectExceptionMessage')) {
$this->expectException(ParseException::class);
$this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator));
} else {
$this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar at line 1 (near "%sfoo ").', $indicator));
}
Inline::parse(sprintf('{ foo: %sfoo }', $indicator));
}
@@ -255,14 +341,12 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @group legacy
* @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered
* @expectedDeprecation Not quoting the scalar "%bar " starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0 on line 1.
* throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
*/
public function testParseUnquotedScalarStartingWithPercentCharacter()
{
ErrorAssert::assertDeprecationsAreTriggered('Not quoting the scalar "%foo " starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', function () {
Inline::parse('{ foo: %foo }');
});
Inline::parse('{ foo: %bar }');
}
/**
@@ -292,11 +376,17 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('true', true),
array('12', 12),
array('-12', -12),
array('1_2', 12),
array('_12', '_12'),
array('12_', 12),
array('"quoted string"', 'quoted string'),
array("'quoted string'", 'quoted string'),
array('12.30e+02', 12.30e+02),
array('123.45_67', 123.4567),
array('0x4D2', 0x4D2),
array('0x_4_D_2_', 0x4D2),
array('02333', 02333),
array('0_2_3_3_3', 02333),
array('.Inf', -log(0)),
array('-.Inf', log(0)),
array("'686e444'", '686e444'),
@@ -332,18 +422,22 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')),
// mappings
array('{foo:bar,bar:foo,false:false,null:null,integer:12}', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
array('{ foo : bar, bar : foo, false : false, null : null, integer : 12 }', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
array('{foo: bar,bar: foo,"false": false, "null": null,integer: 12}', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
array('{ foo : bar, bar : foo, "false" : false, "null" : null, integer : 12 }', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
array('{foo: \'bar\', bar: \'foo: bar\'}', array('foo' => 'bar', 'bar' => 'foo: bar')),
array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', array('foo' => 'bar', 'bar' => 'foo: bar')),
array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', array('foo\'' => 'bar', 'bar"' => 'foo: bar')),
array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', array('foo: ' => 'bar', 'bar: ' => 'foo: bar')),
array('{"foo:bar": "baz"}', array('foo:bar' => 'baz')),
array('{"foo":"bar"}', array('foo' => 'bar')),
// nested sequences and mappings
array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))),
array('[foo, {bar: foo}]', array('foo', array('bar' => 'foo'))),
array('{ foo: {bar: foo} }', array('foo' => array('bar' => 'foo'))),
array('{ foo: [bar, foo] }', array('foo' => array('bar', 'foo'))),
array('{ foo:{bar: foo} }', array('foo' => array('bar' => 'foo'))),
array('{ foo:[bar, foo] }', array('foo' => array('bar', 'foo'))),
array('[ foo, [ bar, foo ] ]', array('foo', array('bar', 'foo'))),
@@ -399,12 +493,14 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')),
// mappings
array('{foo:bar,bar:foo,false:false,null:null,integer:12}', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
array('{ foo : bar, bar : foo, false : false, null : null, integer : 12 }', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
array('{foo: bar,bar: foo,"false": false,"null": null,integer: 12}', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), Yaml::PARSE_OBJECT_FOR_MAP),
array('{ foo : bar, bar : foo, "false" : false, "null" : null, integer : 12 }', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), Yaml::PARSE_OBJECT_FOR_MAP),
array('{foo: \'bar\', bar: \'foo: bar\'}', (object) array('foo' => 'bar', 'bar' => 'foo: bar')),
array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', (object) array('foo' => 'bar', 'bar' => 'foo: bar')),
array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', (object) array('foo\'' => 'bar', 'bar"' => 'foo: bar')),
array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', (object) array('foo: ' => 'bar', 'bar: ' => 'foo: bar')),
array('{"foo:bar": "baz"}', (object) array('foo:bar' => 'baz')),
array('{"foo":"bar"}', (object) array('foo' => 'bar')),
// nested sequences and mappings
array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))),
@@ -444,10 +540,15 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('false', false),
array('true', true),
array('12', 12),
array("'1_2'", '1_2'),
array('_12', '_12'),
array("'12_'", '12_'),
array("'quoted string'", 'quoted string'),
array('!!float 1230', 12.30e+02),
array('1234', 0x4D2),
array('1243', 02333),
array("'0x_4_D_2_'", '0x_4_D_2_'),
array("'0_2_3_3_3'", '0_2_3_3_3'),
array('.Inf', -log(0)),
array('-.Inf', log(0)),
array("'686e444'", '686e444'),
@@ -505,23 +606,30 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getTimestampTests
*/
public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second)
public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second, $timezone)
{
$expected = new \DateTime($yaml);
$expected->setTimeZone(new \DateTimeZone('UTC'));
$expected->setDate($year, $month, $day);
$expected->setTime($hour, $minute, $second);
$this->assertEquals($expected, Inline::parse($yaml, Yaml::PARSE_DATETIME));
if (\PHP_VERSION_ID >= 70100) {
$expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second));
} else {
$expected->setTime($hour, $minute, $second);
}
$date = Inline::parse($yaml, Yaml::PARSE_DATETIME);
$this->assertEquals($expected, $date);
$this->assertSame($timezone, $date->format('O'));
}
public function getTimestampTests()
{
return array(
'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43),
'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43),
'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43),
'date' => array('2001-12-15', 2001, 12, 15, 0, 0, 0),
'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43.1, '+0000'),
'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43.1, '-0500'),
'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43.1, '-0500'),
'date' => array('2001-12-15', 2001, 12, 15, 0, 0, 0, '+0000'),
);
}
@@ -533,7 +641,11 @@ class InlineTest extends \PHPUnit_Framework_TestCase
$expected = new \DateTime($yaml);
$expected->setTimeZone(new \DateTimeZone('UTC'));
$expected->setDate($year, $month, $day);
$expected->setTime($hour, $minute, $second);
if (\PHP_VERSION_ID >= 70100) {
$expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second));
} else {
$expected->setTime($hour, $minute, $second);
}
$expectedNested = array('nested' => array($expected));
$yamlNested = "{nested: [$yaml]}";
@@ -581,10 +693,15 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider getInvalidBinaryData
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
public function testParseInvalidBinaryData($data, $expectedMessage)
{
$this->setExpectedExceptionRegExp('\Symfony\Component\Yaml\Exception\ParseException', $expectedMessage);
if (method_exists($this, 'expectException')) {
$this->expectExceptionMessageRegExp($expectedMessage);
} else {
$this->setExpectedExceptionRegExp(ParseException::class, $expectedMessage);
}
Inline::parse($data);
}
@@ -598,4 +715,102 @@ class InlineTest extends \PHPUnit_Framework_TestCase
'misplaced equals character' => array('!!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'),
);
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage Malformed inline YAML string: {this, is not, supported} at line 1.
*/
public function testNotSupportedMissingValue()
{
Inline::parse('{this, is not, supported}');
}
public function testVeryLongQuotedStrings()
{
$longStringWithQuotes = str_repeat("x\r\n\\\"x\"x", 1000);
$yamlString = Inline::dump(array('longStringWithQuotes' => $longStringWithQuotes));
$arrayFromYaml = Inline::parse($yamlString);
$this->assertEquals($longStringWithQuotes, $arrayFromYaml['longStringWithQuotes']);
}
/**
* @group legacy
* @expectedDeprecation Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0 on line 1.
*/
public function testOmittedMappingKeyIsParsedAsColon()
{
$this->assertSame(array(':' => 'foo'), Inline::parse('{: foo}'));
}
/**
* @dataProvider getTestsForNullValues
*/
public function testParseMissingMappingValueAsNull($yaml, $expected)
{
$this->assertSame($expected, Inline::parse($yaml));
}
public function getTestsForNullValues()
{
return array(
'null before closing curly brace' => array('{foo:}', array('foo' => null)),
'null before comma' => array('{foo:, bar: baz}', array('foo' => null, 'bar' => 'baz')),
);
}
public function testTheEmptyStringIsAValidMappingKey()
{
$this->assertSame(array('' => 'foo'), Inline::parse('{ "": foo }'));
}
/**
* @group legacy
* @expectedDeprecation Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1.
* @dataProvider getNotPhpCompatibleMappingKeyData
*/
public function testImplicitStringCastingOfMappingKeysIsDeprecated($yaml, $expected)
{
$this->assertSame($expected, Inline::parse($yaml));
}
/**
* @group legacy
* @expectedDeprecation Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.
* @expectedDeprecation Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line 1.
* @dataProvider getNotPhpCompatibleMappingKeyData
*/
public function testExplicitStringCastingOfMappingKeys($yaml, $expected)
{
$this->assertSame($expected, Yaml::parse($yaml, Yaml::PARSE_KEYS_AS_STRINGS));
}
public function getNotPhpCompatibleMappingKeyData()
{
return array(
'boolean-true' => array('{true: "foo"}', array('true' => 'foo')),
'boolean-false' => array('{false: "foo"}', array('false' => 'foo')),
'null' => array('{null: "foo"}', array('null' => 'foo')),
'float' => array('{0.25: "foo"}', array('0.25' => 'foo')),
);
}
/**
* @group legacy
* @expectedDeprecation Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead on line 1.
*/
public function testDeprecatedStrTag()
{
$this->assertSame(array('foo' => 'bar'), Inline::parse('{ foo: !str bar }'));
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage Unexpected end of line, expected one of ",}" at line 1 (near "{abc: 'def'").
*/
public function testUnfinishedInlineMap()
{
Inline::parse("{abc: 'def'");
}
}

View File

@@ -11,9 +11,10 @@
namespace Symfony\Component\Yaml\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Exception\ParseException;
class ParseExceptionTest extends \PHPUnit_Framework_TestCase
class ParseExceptionTest extends TestCase
{
public function testGetMessage()
{

File diff suppressed because it is too large Load Diff

View File

@@ -11,9 +11,10 @@
namespace Symfony\Component\Yaml\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Yaml;
class YamlTest extends \PHPUnit_Framework_TestCase
class YamlTest extends TestCase
{
public function testParseAndDump()
{

View File

@@ -128,15 +128,15 @@ class Unescaper
private static function utf8chr($c)
{
if (0x80 > $c %= 0x200000) {
return chr($c);
return \chr($c);
}
if (0x800 > $c) {
return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
}
if (0x10000 > $c) {
return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
}
return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 & 0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
}
}

View File

@@ -17,6 +17,8 @@ use Symfony\Component\Yaml\Exception\ParseException;
* Yaml offers convenience methods to load and dump YAML.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since version 3.4
*/
class Yaml
{
@@ -28,6 +30,37 @@ class Yaml
const PARSE_DATETIME = 32;
const DUMP_OBJECT_AS_MAP = 64;
const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
const PARSE_CONSTANT = 256;
const PARSE_CUSTOM_TAGS = 512;
const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
/**
* @deprecated since version 3.4, to be removed in 4.0. Quote your evaluable keys instead.
*/
const PARSE_KEYS_AS_STRINGS = 2048;
/**
* Parses a YAML file into a PHP value.
*
* Usage:
* <code>
* $array = Yaml::parseFile('config.yml');
* print_r($array);
* </code>
*
* @param string $filename The path to the YAML file to be parsed
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
*
* @return mixed The YAML converted to a PHP value
*
* @throws ParseException If the file could not be read or the YAML is not valid
*/
public static function parseFile($filename, $flags = 0)
{
$yaml = new Parser();
return $yaml->parseFile($filename, $flags);
}
/**
* Parses YAML into a PHP value.
@@ -47,8 +80,8 @@ class Yaml
*/
public static function parse($input, $flags = 0)
{
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if (\is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if ($flags) {
$flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE;
@@ -57,16 +90,16 @@ class Yaml
}
}
if (func_num_args() >= 3) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 3) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
if (func_get_arg(2)) {
$flags |= self::PARSE_OBJECT;
}
}
if (func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
if (func_get_arg(3)) {
$flags |= self::PARSE_OBJECT_FOR_MAP;
@@ -93,8 +126,8 @@ class Yaml
*/
public static function dump($input, $inline = 2, $indent = 4, $flags = 0)
{
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if (\is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
if ($flags) {
$flags = self::DUMP_EXCEPTION_ON_INVALID_TYPE;
@@ -103,8 +136,8 @@ class Yaml
}
}
if (func_num_args() >= 5) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
if (\func_num_args() >= 5) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
if (func_get_arg(4)) {
$flags |= self::DUMP_OBJECT;

View File

@@ -16,7 +16,17 @@
}
],
"require": {
"php": ">=5.5.9"
"php": "^5.5.9|>=7.0.8",
"symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/console": "~3.4|~4.0"
},
"conflict": {
"symfony/console": "<3.4"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Yaml\\": "" },
@@ -27,7 +37,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
"dev-master": "3.4-dev"
}
}
}

View File

@@ -5,6 +5,8 @@
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />