Update Composer

Signed-off-by: Olivier Paroz (oparoz) <github@oparoz.com>
This commit is contained in:
Olivier Paroz (oparoz) 2017-03-12 00:36:21 +01:00
parent ed760ecf5c
commit 21e82c3e43
No known key found for this signature in database
GPG Key ID: 165E66587C7FE8B1
27 changed files with 2521 additions and 1064 deletions

View File

@ -1,15 +1,15 @@
{ {
"name": "owncloud/gallery", "name": "nextcloud/gallery",
"type": "project", "type": "project",
"description": "Media gallery for ownCloud which includes previews for all media types supported by your ownCloud installation.", "description": "Media gallery for Nextcloud which includes previews for all media types supported by your Nextcloud installation.",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"homepage": "https://github.com/owncloud/gallery", "homepage": "https://github.com/nextcloud/gallery",
"authors": [ "authors": [
{ {
"name": "Olivier Paroz", "name": "Olivier Paroz",
"email": "galleryapps@oparoz.com", "email": "galleryapps@oparoz.com",
"role": "Developer", "role": "Developer",
"homepage": "http://www.interfasys.ch" "homepage": "http://www.oparoz.com"
}, },
{ {
"name": "Robin Appelman", "name": "Robin Appelman",
@ -19,22 +19,22 @@
} }
], ],
"support": { "support": {
"issues": "https://github.com/owncloud/gallery/issues", "issues": "https://github.com/nextcloud/gallery/issues",
"wiki": "https://github.com/owncloud/gallery/wiki", "wiki": "https://github.com/nextcloud/gallery/wiki",
"source": "https://github.com/owncloud/gallery" "source": "https://github.com/nextcloud/gallery"
}, },
"require": { "require": {
"symfony/yaml": "~2.6" "symfony/yaml": "~3.2"
}, },
"require-dev": { "require-dev": {
"guzzlehttp/guzzle": "^5.0|^6.2", "guzzlehttp/guzzle": "^6.2",
"phpunit/phpunit": "^4.0|^5.4", "phpunit/phpunit": "^5.7|^6.0",
"codeception/codeception": "2.2.*", "codeception/codeception": "2.2.*",
"codeception/phpbuiltinserver": "*", "codeception/phpbuiltinserver": "*",
"composer-plugin-api": "^1.0", "composer-plugin-api": "^1.0",
"codeception/c3": "2.*", "codeception/c3": "2.*",
"flow/jsonpath": "^0.3.1", "flow/jsonpath": "^0.3.4",
"phpdocumentor/phpdocumentor": "^2.8", "phpdocumentor/phpdocumentor": "^2.9",
"jakub-onderka/php-parallel-lint": "0.*", "jakub-onderka/php-parallel-lint": "0.*",
"jakub-onderka/php-console-highlighter": "0.*" "jakub-onderka/php-console-highlighter": "0.*"
} }

1697
composer.lock generated

File diff suppressed because it is too large Load Diff

2
vendor/autoload.php vendored
View File

@ -2,6 +2,6 @@
// autoload.php @generated by Composer // autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php'; require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit3cfd72445e0ad28db5332442bcda1e66::getLoader(); return ComposerAutoloaderInit3cfd72445e0ad28db5332442bcda1e66::getLoader();

View File

@ -53,8 +53,9 @@ class ClassLoader
private $useIncludePath = false; private $useIncludePath = false;
private $classMap = array(); private $classMap = array();
private $classMapAuthoritative = false; private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes() public function getPrefixes()
{ {
@ -271,6 +272,26 @@ class ClassLoader
return $this->classMapAuthoritative; return $this->classMapAuthoritative;
} }
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/** /**
* Registers this instance as an autoloader. * Registers this instance as an autoloader.
* *
@ -313,29 +334,34 @@ class ClassLoader
*/ */
public function findFile($class) public function findFile($class)
{ {
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup // class map lookup
if (isset($this->classMap[$class])) { if (isset($this->classMap[$class])) {
return $this->classMap[$class]; return $this->classMap[$class];
} }
if ($this->classMapAuthoritative) { if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false; return false;
} }
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php'); $file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM // Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) { if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh'); $file = $this->findFileWithExtension($class, '.hh');
} }
if ($file === null) { if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist. // Remember that this class does not exist.
return $this->classMap[$class] = false; $this->missingClasses[$class] = true;
} }
return $file; return $file;
@ -348,9 +374,13 @@ class ClassLoader
$first = $class[0]; $first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) { if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { $subPath = $class;
if (0 === strpos($class, $prefix)) { while (false !== $lastPos = strrpos($subPath, '\\')) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) { $subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\';
if (isset($this->prefixDirsPsr4[$search])) {
foreach ($this->prefixDirsPsr4[$search] as $dir) {
$length = $this->prefixLengthsPsr4[$first][$search];
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file; return $file;
} }
@ -399,6 +429,8 @@ class ClassLoader
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file; return $file;
} }
return false;
} }
} }

View File

@ -1,5 +1,5 @@
Copyright (c) 2016 Nils Adermann, Jordi Boggiano Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php',
'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php', 'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php',
'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Escaper.php', 'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Escaper.php',
'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Exception/DumpException.php', 'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Exception/DumpException.php',

View File

@ -23,7 +23,7 @@ class ComposerAutoloaderInit3cfd72445e0ad28db5332442bcda1e66
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit3cfd72445e0ad28db5332442bcda1e66', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInit3cfd72445e0ad28db5332442bcda1e66', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION'); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) { if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php'; require_once __DIR__ . '/autoload_static.php';

View File

@ -21,6 +21,7 @@ class ComposerStaticInit3cfd72445e0ad28db5332442bcda1e66
); );
public static $classMap = array ( public static $classMap = array (
'Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php',
'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php', 'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php',
'Symfony\\Component\\Yaml\\Escaper' => __DIR__ . '/..' . '/symfony/yaml/Escaper.php', 'Symfony\\Component\\Yaml\\Escaper' => __DIR__ . '/..' . '/symfony/yaml/Escaper.php',
'Symfony\\Component\\Yaml\\Exception\\DumpException' => __DIR__ . '/..' . '/symfony/yaml/Exception/DumpException.php', 'Symfony\\Component\\Yaml\\Exception\\DumpException' => __DIR__ . '/..' . '/symfony/yaml/Exception/DumpException.php',

View File

@ -1,27 +1,33 @@
[ [
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v2.8.7", "version": "v3.2.6",
"version_normalized": "2.8.7.0", "version_normalized": "3.2.6.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/yaml.git", "url": "https://github.com/symfony/yaml.git",
"reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34" "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/815fabf3f48c7d1df345a69d1ad1a88f59757b34", "url": "https://api.github.com/repos/symfony/yaml/zipball/093e416ad096355149e265ea2e4cc1f9ee40ab1a",
"reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34", "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.5.9"
}, },
"time": "2016-06-06 11:11:27", "require-dev": {
"symfony/console": "~2.8|~3.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"time": "2017-03-07T16:47:02+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.8-dev" "dev-master": "3.2-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",

View File

@ -1,6 +1,61 @@
CHANGELOG CHANGELOG
========= =========
3.2.0
-----
* Mappings with a colon (`:`) that is not followed by a whitespace are deprecated
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
-----
* Strings that are not UTF-8 encoded will be dumped as base64 encoded binary
data.
* Added support for dumping multi line strings as literal blocks.
* Added support for parsing base64 encoded binary data when they are tagged
with the `!!binary` tag.
* Added support for parsing timestamps as `\DateTime` objects:
```php
Yaml::parse('2001-12-15 21:59:43.10 -5', Yaml::PARSE_DATETIME);
```
* `\DateTime` and `\DateTimeImmutable` objects are dumped as YAML timestamps.
* Deprecated usage of `%` at the beginning of an unquoted string.
* Added support for customizing the YAML parser behavior through an optional bit field:
```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE | Yaml::PARSE_OBJECT | Yaml::PARSE_OBJECT_FOR_MAP);
```
* Added support for customizing the dumped YAML string through an optional bit field:
```php
Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE | Yaml::DUMP_OBJECT);
```
3.0.0
-----
* Yaml::parse() now throws an exception when a blackslash is not escaped
in double-quoted strings
2.8.0 2.8.0
----- -----

View File

@ -23,18 +23,28 @@ class Dumper
* *
* @var int * @var int
*/ */
protected $indentation = 4; protected $indentation;
/**
* @param int $indentation
*/
public function __construct($indentation = 4)
{
if ($indentation < 1) {
throw new \InvalidArgumentException('The indentation must be greater than zero.');
}
$this->indentation = $indentation;
}
/** /**
* Sets the indentation. * Sets the indentation.
* *
* @param int $num The amount of spaces to use for indentation of nested nodes. * @param int $num The amount of spaces to use for indentation of nested nodes
*/ */
public function setIndentation($num) public function setIndentation($num)
{ {
if ($num < 1) { @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);
throw new \InvalidArgumentException('The indentation must be greater than zero.');
}
$this->indentation = (int) $num; $this->indentation = (int) $num;
} }
@ -42,32 +52,59 @@ class Dumper
/** /**
* Dumps a PHP value to YAML. * Dumps a PHP value to YAML.
* *
* @param mixed $input The PHP value * @param mixed $input The PHP value
* @param int $inline The level where you switch to inline YAML * @param int $inline The level where you switch to inline YAML
* @param int $indent The level of indentation (used internally) * @param int $indent The level of indentation (used internally)
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
* @param bool $objectSupport true if object support is enabled, false otherwise
* *
* @return string The YAML representation of the PHP value * @return string The YAML representation of the PHP value
*/ */
public function dump($input, $inline = 0, $indent = 0, $exceptionOnInvalidType = false, $objectSupport = false) 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 ($flags) {
$flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
}
}
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_get_arg(4)) {
$flags |= Yaml::DUMP_OBJECT;
}
}
$output = ''; $output = '';
$prefix = $indent ? str_repeat(' ', $indent) : ''; $prefix = $indent ? str_repeat(' ', $indent) : '';
if ($inline <= 0 || !is_array($input) || empty($input)) { if ($inline <= 0 || !is_array($input) || empty($input)) {
$output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport); $output .= $prefix.Inline::dump($input, $flags);
} else { } else {
$isAHash = Inline::isHash($input); $isAHash = Inline::isHash($input);
foreach ($input as $key => $value) { 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).':' : '-', '');
foreach (preg_split('/\n|\r\n/', $value) as $row) {
$output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row);
}
continue;
}
$willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value);
$output .= sprintf('%s%s%s%s', $output .= sprintf('%s%s%s%s',
$prefix, $prefix,
$isAHash ? Inline::dump($key, $exceptionOnInvalidType, $objectSupport).':' : '-', $isAHash ? Inline::dump($key, $flags).':' : '-',
$willBeInlined ? ' ' : "\n", $willBeInlined ? ' ' : "\n",
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $exceptionOnInvalidType, $objectSupport) $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
).($willBeInlined ? "\n" : ''); ).($willBeInlined ? "\n" : '');
} }
} }

View File

@ -46,7 +46,7 @@ class Escaper
* *
* @param string $value A PHP value * @param string $value A PHP value
* *
* @return bool True if the value would require double quotes. * @return bool True if the value would require double quotes
*/ */
public static function requiresDoubleQuoting($value) public static function requiresDoubleQuoting($value)
{ {
@ -70,7 +70,7 @@ class Escaper
* *
* @param string $value A PHP value * @param string $value A PHP value
* *
* @return bool True if the value would require single quotes. * @return bool True if the value would require single quotes
*/ */
public static function requiresSingleQuoting($value) public static function requiresSingleQuoting($value)
{ {

View File

@ -123,12 +123,7 @@ class ParseException extends RuntimeException
} }
if (null !== $this->parsedFile) { if (null !== $this->parsedFile) {
if (PHP_VERSION_ID >= 50400) { $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
$jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
} else {
$jsonOptions = 0;
}
$this->message .= sprintf(' in %s', json_encode($this->parsedFile, $jsonOptions));
} }
if ($this->parsedLine >= 0) { if ($this->parsedLine >= 0) {

View File

@ -18,33 +18,69 @@ use Symfony\Component\Yaml\Exception\DumpException;
* Inline implements a YAML parser/dumper for the YAML inline syntax. * Inline implements a YAML parser/dumper for the YAML inline syntax.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/ */
class Inline class Inline
{ {
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
public static $parsedLineNumber;
private static $exceptionOnInvalidType = false; private static $exceptionOnInvalidType = false;
private static $objectSupport = false; private static $objectSupport = false;
private static $objectForMap = false; private static $objectForMap = false;
private static $constantSupport = false;
/** /**
* Converts a YAML string to a PHP array. * Converts a YAML string to a PHP value.
* *
* @param string $value A YAML string * @param string $value A YAML string
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
* @param bool $objectSupport true if object support is enabled, false otherwise * @param array $references Mapping of variable names to values
* @param bool $objectForMap true if maps should return a stdClass instead of array()
* @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 * @throws ParseException
*/ */
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false, $references = array()) public static function parse($value, $flags = 0, $references = array())
{ {
self::$exceptionOnInvalidType = $exceptionOnInvalidType; if (is_bool($flags)) {
self::$objectSupport = $objectSupport; @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);
self::$objectForMap = $objectForMap;
if ($flags) {
$flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
}
}
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 ($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_get_arg(3)) {
$flags |= Yaml::PARSE_OBJECT_FOR_MAP;
}
}
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::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);
$value = trim($value); $value = trim($value);
@ -60,15 +96,15 @@ class Inline
$i = 0; $i = 0;
switch ($value[0]) { switch ($value[0]) {
case '[': case '[':
$result = self::parseSequence($value, $i, $references); $result = self::parseSequence($value, $flags, $i, $references);
++$i; ++$i;
break; break;
case '{': case '{':
$result = self::parseMapping($value, $i, $references); $result = self::parseMapping($value, $flags, $i, $references);
++$i; ++$i;
break; break;
default: default:
$result = self::parseScalar($value, null, array('"', "'"), $i, true, $references); $result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references);
} }
// some comments are allowed at the end // some comments are allowed at the end
@ -86,35 +122,58 @@ class Inline
/** /**
* Dumps a given PHP variable to a YAML string. * Dumps a given PHP variable to a YAML string.
* *
* @param mixed $value The PHP variable to convert * @param mixed $value The PHP variable to convert
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
* @param bool $objectSupport true if object support is enabled, false otherwise
* *
* @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 * @throws DumpException When trying to dump PHP resource
*/ */
public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false) 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 ($flags) {
$flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
}
}
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_get_arg(2)) {
$flags |= Yaml::DUMP_OBJECT;
}
}
switch (true) { switch (true) {
case is_resource($value): case is_resource($value):
if ($exceptionOnInvalidType) { 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))); throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
} }
return 'null'; return 'null';
case $value instanceof \DateTimeInterface:
return $value->format('c');
case is_object($value): case is_object($value):
if ($objectSupport) { if (Yaml::DUMP_OBJECT & $flags) {
return '!php/object:'.serialize($value); return '!php/object:'.serialize($value);
} }
if ($exceptionOnInvalidType) { if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
return self::dumpArray((array) $value, $flags);
}
if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
throw new DumpException('Object support when dumping a YAML file has been disabled.'); throw new DumpException('Object support when dumping a YAML file has been disabled.');
} }
return 'null'; return 'null';
case is_array($value): case is_array($value):
return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport); return self::dumpArray($value, $flags);
case null === $value: case null === $value:
return 'null'; return 'null';
case true === $value: case true === $value:
@ -146,9 +205,12 @@ class Inline
return $repr; return $repr;
case '' == $value: case '' == $value:
return "''"; return "''";
case self::isBinaryString($value):
return '!!binary '.base64_encode($value);
case Escaper::requiresDoubleQuoting($value): case Escaper::requiresDoubleQuoting($value):
return Escaper::escapeWithDoubleQuotes($value); return Escaper::escapeWithDoubleQuotes($value);
case Escaper::requiresSingleQuoting($value): case Escaper::requiresSingleQuoting($value):
case preg_match('{^[0-9]+[_0-9]*$}', $value):
case preg_match(self::getHexRegex(), $value): case preg_match(self::getHexRegex(), $value):
case preg_match(self::getTimestampRegex(), $value): case preg_match(self::getTimestampRegex(), $value):
return Escaper::escapeWithSingleQuotes($value); return Escaper::escapeWithSingleQuotes($value);
@ -182,19 +244,18 @@ class Inline
/** /**
* Dumps a PHP array to a YAML string. * Dumps a PHP array to a YAML string.
* *
* @param array $value The PHP array to dump * @param array $value The PHP array to dump
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
* @param bool $objectSupport true if object support is enabled, false otherwise
* *
* @return string The YAML string representing the PHP array * @return string The YAML string representing the PHP array
*/ */
private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport) private static function dumpArray($value, $flags)
{ {
// array // array
if ($value && !self::isHash($value)) { if ($value && !self::isHash($value)) {
$output = array(); $output = array();
foreach ($value as $val) { foreach ($value as $val) {
$output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport); $output[] = self::dump($val, $flags);
} }
return sprintf('[%s]', implode(', ', $output)); return sprintf('[%s]', implode(', ', $output));
@ -203,29 +264,30 @@ class Inline
// hash // hash
$output = array(); $output = array();
foreach ($value as $key => $val) { foreach ($value as $key => $val) {
$output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport)); $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
} }
return sprintf('{ %s }', implode(', ', $output)); return sprintf('{ %s }', implode(', ', $output));
} }
/** /**
* Parses a scalar to a YAML string. * Parses a YAML scalar.
* *
* @param string $scalar * @param string $scalar
* @param int $flags
* @param string $delimiters * @param string $delimiters
* @param array $stringDelimiters * @param array $stringDelimiters
* @param int &$i * @param int &$i
* @param bool $evaluate * @param bool $evaluate
* @param array $references * @param array $references
* *
* @return string A YAML string * @return string
* *
* @throws ParseException When malformed inline YAML string is parsed * @throws ParseException When malformed inline YAML string is parsed
* *
* @internal * @internal
*/ */
public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array()) public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
{ {
if (in_array($scalar[$i], $stringDelimiters)) { if (in_array($scalar[$i], $stringDelimiters)) {
// quoted scalar // quoted scalar
@ -251,19 +313,20 @@ class Inline
$output = $match[1]; $output = $match[1];
$i += strlen($output); $i += strlen($output);
} else { } else {
throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar)); throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
} }
// a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) // 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])) { if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
@trigger_error(sprintf('Not quoting the scalar "%s" starting with "%s" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $output, $output[0]), E_USER_DEPRECATED); throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
}
// to be thrown in 3.0 if ($output && '%' === $output[0]) {
// throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $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);
} }
if ($evaluate) { if ($evaluate) {
$output = self::evaluateScalar($output, $references); $output = self::evaluateScalar($output, $flags, $references);
} }
} }
@ -271,19 +334,19 @@ class Inline
} }
/** /**
* Parses a quoted scalar to YAML. * Parses a YAML quoted scalar.
* *
* @param string $scalar * @param string $scalar
* @param int &$i * @param int &$i
* *
* @return string A YAML string * @return string
* *
* @throws ParseException When malformed inline YAML string is parsed * @throws ParseException When malformed inline YAML string is parsed
*/ */
private static function parseQuotedScalar($scalar, &$i) private static function parseQuotedScalar($scalar, &$i)
{ {
if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
} }
$output = substr($match[0], 1, strlen($match[0]) - 2); $output = substr($match[0], 1, strlen($match[0]) - 2);
@ -301,17 +364,18 @@ class Inline
} }
/** /**
* Parses a sequence to a YAML string. * Parses a YAML sequence.
* *
* @param string $sequence * @param string $sequence
* @param int $flags
* @param int &$i * @param int &$i
* @param array $references * @param array $references
* *
* @return string A YAML string * @return array
* *
* @throws ParseException When malformed inline YAML string is parsed * @throws ParseException When malformed inline YAML string is parsed
*/ */
private static function parseSequence($sequence, &$i = 0, $references = array()) private static function parseSequence($sequence, $flags, &$i = 0, $references = array())
{ {
$output = array(); $output = array();
$len = strlen($sequence); $len = strlen($sequence);
@ -322,11 +386,11 @@ class Inline
switch ($sequence[$i]) { switch ($sequence[$i]) {
case '[': case '[':
// nested sequence // nested sequence
$output[] = self::parseSequence($sequence, $i, $references); $output[] = self::parseSequence($sequence, $flags, $i, $references);
break; break;
case '{': case '{':
// nested mapping // nested mapping
$output[] = self::parseMapping($sequence, $i, $references); $output[] = self::parseMapping($sequence, $flags, $i, $references);
break; break;
case ']': case ']':
return $output; return $output;
@ -335,14 +399,14 @@ class Inline
break; break;
default: default:
$isQuoted = in_array($sequence[$i], array('"', "'")); $isQuoted = in_array($sequence[$i], array('"', "'"));
$value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references); $value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references);
// the value can be an array if a reference has been resolved to an array var // the value can be an array if a reference has been resolved to an array var
if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) { if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
// embedded mapping? // embedded mapping?
try { try {
$pos = 0; $pos = 0;
$value = self::parseMapping('{'.$value.'}', $pos, $references); $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
} catch (\InvalidArgumentException $e) { } catch (\InvalidArgumentException $e) {
// no, it's not // no, it's not
} }
@ -356,21 +420,22 @@ class Inline
++$i; ++$i;
} }
throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence)); throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
} }
/** /**
* Parses a mapping to a YAML string. * Parses a YAML mapping.
* *
* @param string $mapping * @param string $mapping
* @param int $flags
* @param int &$i * @param int &$i
* @param array $references * @param array $references
* *
* @return string A YAML string * @return array|\stdClass
* *
* @throws ParseException When malformed inline YAML string is parsed * @throws ParseException When malformed inline YAML string is parsed
*/ */
private static function parseMapping($mapping, &$i = 0, $references = array()) private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
{ {
$output = array(); $output = array();
$len = strlen($mapping); $len = strlen($mapping);
@ -392,7 +457,15 @@ class Inline
} }
// key // key
$key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); $key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
if (':' !== $key && false === $i = strpos($mapping, ':', $i)) {
break;
}
if (':' !== $key && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) {
@trigger_error('Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
}
// value // value
$done = false; $done = false;
@ -401,23 +474,27 @@ class Inline
switch ($mapping[$i]) { switch ($mapping[$i]) {
case '[': case '[':
// nested sequence // nested sequence
$value = self::parseSequence($mapping, $i, $references); $value = self::parseSequence($mapping, $flags, $i, $references);
// Spec: Keys MUST be unique; first one wins. // Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since lines // Parser cannot abort this mapping earlier, since lines
// are processed sequentially. // are processed sequentially.
if (!isset($output[$key])) { if (!isset($output[$key])) {
$output[$key] = $value; $output[$key] = $value;
} else {
@trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
} }
$done = true; $done = true;
break; break;
case '{': case '{':
// nested mapping // nested mapping
$value = self::parseMapping($mapping, $i, $references); $value = self::parseMapping($mapping, $flags, $i, $references);
// Spec: Keys MUST be unique; first one wins. // Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since lines // Parser cannot abort this mapping earlier, since lines
// are processed sequentially. // are processed sequentially.
if (!isset($output[$key])) { if (!isset($output[$key])) {
$output[$key] = $value; $output[$key] = $value;
} else {
@trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
} }
$done = true; $done = true;
break; break;
@ -425,12 +502,14 @@ class Inline
case ' ': case ' ':
break; break;
default: default:
$value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references); $value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
// Spec: Keys MUST be unique; first one wins. // Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since lines // Parser cannot abort this mapping earlier, since lines
// are processed sequentially. // are processed sequentially.
if (!isset($output[$key])) { if (!isset($output[$key])) {
$output[$key] = $value; $output[$key] = $value;
} else {
@trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
} }
$done = true; $done = true;
--$i; --$i;
@ -444,20 +523,21 @@ class Inline
} }
} }
throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping)); throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
} }
/** /**
* Evaluates scalars and replaces magic values. * Evaluates scalars and replaces magic values.
* *
* @param string $scalar * @param string $scalar
* @param int $flags
* @param array $references * @param array $references
* *
* @return string A YAML string * @return string A 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 * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
*/ */
private static function evaluateScalar($scalar, $references = array()) private static function evaluateScalar($scalar, $flags, $references = array())
{ {
$scalar = trim($scalar); $scalar = trim($scalar);
$scalarLower = strtolower($scalar); $scalarLower = strtolower($scalar);
@ -496,7 +576,7 @@ class Inline
case 0 === strpos($scalar, '!str'): case 0 === strpos($scalar, '!str'):
return (string) substr($scalar, 5); return (string) substr($scalar, 5);
case 0 === strpos($scalar, '! '): case 0 === strpos($scalar, '! '):
return (int) self::parseScalar(substr($scalar, 2)); return (int) self::parseScalar(substr($scalar, 2), $flags);
case 0 === strpos($scalar, '!php/object:'): case 0 === strpos($scalar, '!php/object:'):
if (self::$objectSupport) { if (self::$objectSupport) {
return unserialize(substr($scalar, 12)); return unserialize(substr($scalar, 12));
@ -509,6 +589,8 @@ class Inline
return; return;
case 0 === strpos($scalar, '!!php/object:'): case 0 === strpos($scalar, '!!php/object:'):
if (self::$objectSupport) { 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);
return unserialize(substr($scalar, 13)); return unserialize(substr($scalar, 13));
} }
@ -516,9 +598,25 @@ class Inline
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.');
} }
return;
case 0 === strpos($scalar, '!php/const:'):
if (self::$constantSupport) {
if (defined($const = substr($scalar, 11))) {
return constant($const);
}
throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
}
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));
}
return; return;
case 0 === strpos($scalar, '!!float '): case 0 === strpos($scalar, '!!float '):
return (float) substr($scalar, 8); return (float) substr($scalar, 8);
case preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
$scalar = str_replace('_', '', (string) $scalar);
// omitting the break / return as integers are handled in the next case
case ctype_digit($scalar): case ctype_digit($scalar):
$raw = $scalar; $raw = $scalar;
$cast = (int) $scalar; $cast = (int) $scalar;
@ -531,15 +629,29 @@ class Inline
return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
case is_numeric($scalar): case is_numeric($scalar):
case preg_match(self::getHexRegex(), $scalar): case preg_match(self::getHexRegex(), $scalar):
$scalar = str_replace('_', '', $scalar);
return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
case '.inf' === $scalarLower: case '.inf' === $scalarLower:
case '.nan' === $scalarLower: case '.nan' === $scalarLower:
return -log(0); return -log(0);
case '-.inf' === $scalarLower: case '-.inf' === $scalarLower:
return log(0); return log(0);
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): case 0 === strpos($scalar, '!!binary '):
return (float) str_replace(',', '', $scalar); return self::evaluateBinaryScalar(substr($scalar, 9));
case preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
case preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
if (false !== strpos($scalar, ',')) {
@trigger_error('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0.', E_USER_DEPRECATED);
}
return (float) str_replace(array(',', '_'), '', $scalar);
case preg_match(self::getTimestampRegex(), $scalar): case 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'));
}
$timeZone = date_default_timezone_get(); $timeZone = date_default_timezone_get();
date_default_timezone_set('UTC'); date_default_timezone_set('UTC');
$time = strtotime($scalar); $time = strtotime($scalar);
@ -552,6 +664,33 @@ class Inline
} }
} }
/**
* @param string $scalar
*
* @return string
*
* @internal
*/
public static function evaluateBinaryScalar($scalar)
{
$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 (!preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
}
return base64_decode($parsedBinaryData, true);
}
private static function isBinaryString($value)
{
return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value);
}
/** /**
* Gets a regex that matches a YAML date. * Gets a regex that matches a YAML date.
* *
@ -584,6 +723,6 @@ EOF;
*/ */
private static function getHexRegex() private static function getHexRegex()
{ {
return '~^0x[0-9a-f]++$~i'; return '~^0x[0-9a-f_]++$~i';
} }
} }

View File

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

View File

@ -20,9 +20,8 @@ use Symfony\Component\Yaml\Exception\ParseException;
*/ */
class Parser class Parser
{ {
const TAG_PATTERN = '((?P<tag>![\w!.\/:-]+) +)?';
const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?'; const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
// BC - wrongly named
const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN;
private $offset = 0; private $offset = 0;
private $totalNumberOfLines; private $totalNumberOfLines;
@ -30,33 +29,61 @@ class Parser
private $currentLineNb = -1; private $currentLineNb = -1;
private $currentLine = ''; private $currentLine = '';
private $refs = array(); private $refs = array();
private $skippedLineNumbers = array();
private $locallySkippedLineNumbers = array();
/** /**
* Constructor. * Constructor.
* *
* @param int $offset The offset of YAML document (used for line numbers in error messages) * @param int $offset The offset of YAML document (used for line numbers in error messages)
* @param int|null $totalNumberOfLines The overall number of lines being parsed * @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) public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array())
{ {
$this->offset = $offset; $this->offset = $offset;
$this->totalNumberOfLines = $totalNumberOfLines; $this->totalNumberOfLines = $totalNumberOfLines;
$this->skippedLineNumbers = $skippedLineNumbers;
} }
/** /**
* Parses a YAML string to a PHP value. * Parses a YAML string to a PHP value.
* *
* @param string $value A YAML string * @param string $value A YAML string
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
* @param bool $objectSupport true if object support is enabled, false otherwise
* @param bool $objectForMap true if maps should return a stdClass instead of array()
* *
* @return mixed A PHP value * @return mixed A PHP value
* *
* @throws ParseException If the YAML is not valid * @throws ParseException If the YAML is not valid
*/ */
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) 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 ($flags) {
$flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
}
}
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_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_get_arg(3)) {
$flags |= Yaml::PARSE_OBJECT_FOR_MAP;
}
}
if (!preg_match('//u', $value)) { if (!preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.'); throw new ParseException('The YAML value does not appear to be valid UTF-8.');
} }
@ -101,27 +128,20 @@ class Parser
// array // array
if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
$c = $this->getRealCurrentLineNb() + 1; $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags);
$parser = new self($c, $this->totalNumberOfLines);
$parser->refs = &$this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
} else { } else {
if (isset($values['leadspaces']) if (isset($values['leadspaces'])
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches) && preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
) { ) {
// this is a compact notation element, add to next block and parse // this is a compact notation element, add to next block and parse
$c = $this->getRealCurrentLineNb();
$parser = new self($c, $this->totalNumberOfLines);
$parser->refs = &$this->refs;
$block = $values['value']; $block = $values['value'];
if ($this->isNextLineIndented()) { if ($this->isNextLineIndented()) {
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1); $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
} }
$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport, $objectForMap); $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
} else { } else {
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context); $data[] = $this->parseValue($values['value'], $flags, $context);
} }
} }
if ($isRef) { if ($isRef) {
@ -134,8 +154,9 @@ class Parser
$context = 'mapping'; $context = 'mapping';
// force correct settings // force correct settings
Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); Inline::parse(null, $flags, $this->refs);
try { try {
Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
$key = Inline::parseScalar($values['key']); $key = Inline::parseScalar($values['key']);
} catch (ParseException $e) { } catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setParsedLine($this->getRealCurrentLineNb() + 1);
@ -164,21 +185,14 @@ class Parser
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
} }
foreach ($refValue as $key => $value) { $data += $refValue; // array union
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
} else { } else {
if (isset($values['value']) && $values['value'] !== '') { if (isset($values['value']) && $values['value'] !== '') {
$value = $values['value']; $value = $values['value'];
} else { } else {
$value = $this->getNextEmbedBlock(); $value = $this->getNextEmbedBlock();
} }
$c = $this->getRealCurrentLineNb() + 1; $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
$parser = new self($c, $this->totalNumberOfLines);
$parser->refs = &$this->refs;
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap);
if (!is_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); throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
@ -193,20 +207,12 @@ class Parser
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
} }
foreach ($parsedItem as $key => $value) { $data += $parsedItem; // array union
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
} }
} else { } else {
// If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the // 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. // current mapping, unless the key already exists in it.
foreach ($parsed as $key => $value) { $data += $parsed; // array union
if (!isset($data[$key])) {
$data[$key] = $value;
}
}
} }
} }
} elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { } elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
@ -224,24 +230,29 @@ class Parser
// But overwriting is allowed when a merge node is used in current block. // But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) { if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = null; $data[$key] = null;
} else {
@trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
} }
} else { } else {
$c = $this->getRealCurrentLineNb() + 1; // remember the parsed line number here in case we need it to provide some contexts in error messages below
$parser = new self($c, $this->totalNumberOfLines); $realCurrentLineNbKey = $this->getRealCurrentLineNb();
$parser->refs = &$this->refs; $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
$value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);
// Spec: Keys MUST be unique; first one wins. // Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block. // But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) { if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value; $data[$key] = $value;
} else {
@trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $realCurrentLineNbKey + 1), E_USER_DEPRECATED);
} }
} }
} else { } else {
$value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context); $value = $this->parseValue($values['value'], $flags, $context);
// Spec: Keys MUST be unique; first one wins. // Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block. // But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) { if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value; $data[$key] = $value;
} else {
@trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
} }
} }
if ($isRef) { if ($isRef) {
@ -256,7 +267,8 @@ class Parser
// 1-liner optionally followed by newline(s) // 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 { try {
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
$value = Inline::parse($this->lines[0], $flags, $this->refs);
} catch (ParseException $e) { } catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine); $e->setSnippet($this->currentLine);
@ -264,17 +276,6 @@ class Parser
throw $e; throw $e;
} }
if (is_array($value)) {
$first = reset($value);
if (is_string($first) && 0 === strpos($first, '*')) {
$data = array();
foreach ($value as $alias) {
$data[] = $this->refs[substr($alias, 1)];
}
$value = $data;
}
}
if (isset($mbEncoding)) { if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding); mb_internal_encoding($mbEncoding);
} }
@ -310,7 +311,7 @@ class Parser
mb_internal_encoding($mbEncoding); mb_internal_encoding($mbEncoding);
} }
if ($objectForMap && !is_object($data) && 'mapping' === $context) { if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) {
$object = new \stdClass(); $object = new \stdClass();
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
@ -323,6 +324,24 @@ class Parser
return empty($data) ? null : $data; return empty($data) ? null : $data;
} }
private function parseBlock($offset, $yaml, $flags)
{
$skippedLineNumbers = $this->skippedLineNumbers;
foreach ($this->locallySkippedLineNumbers as $lineNumber) {
if ($lineNumber < $offset) {
continue;
}
$skippedLineNumbers[] = $lineNumber;
}
$parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers);
$parser->refs = &$this->refs;
return $parser->parse($yaml, $flags);
}
/** /**
* Returns the current line number (takes the offset into account). * Returns the current line number (takes the offset into account).
* *
@ -330,7 +349,17 @@ class Parser
*/ */
private function getRealCurrentLineNb() private function getRealCurrentLineNb()
{ {
return $this->currentLineNb + $this->offset; $realCurrentLineNumber = $this->currentLineNb + $this->offset;
foreach ($this->skippedLineNumbers as $skippedLineNumber) {
if ($skippedLineNumber > $realCurrentLineNumber) {
break;
}
++$realCurrentLineNumber;
}
return $realCurrentLineNumber;
} }
/** /**
@ -421,7 +450,7 @@ class Parser
$previousLineIndentation = $indent; $previousLineIndentation = $indent;
if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
$this->moveToPreviousLine(); $this->moveToPreviousLine();
break; break;
} }
@ -433,6 +462,14 @@ class Parser
// we ignore "comment" lines only when we are not inside a scalar block // we ignore "comment" lines only when we are not inside a scalar block
if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) { 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; continue;
} }
@ -468,26 +505,32 @@ class Parser
/** /**
* Moves the parser to the previous line. * Moves the parser to the previous line.
*
* @return bool
*/ */
private function moveToPreviousLine() private function moveToPreviousLine()
{ {
if ($this->currentLineNb < 1) {
return false;
}
$this->currentLine = $this->lines[--$this->currentLineNb]; $this->currentLine = $this->lines[--$this->currentLineNb];
return true;
} }
/** /**
* Parses a YAML value. * Parses a YAML value.
* *
* @param string $value A YAML value * @param string $value A YAML value
* @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
* @param bool $objectSupport True if object support is enabled, false otherwise * @param string $context The parser context (either sequence or mapping)
* @param bool $objectForMap true if maps should return a stdClass instead of array()
* @param string $context The parser context (either sequence or mapping)
* *
* @return mixed A PHP value * @return mixed A PHP value
* *
* @throws ParseException When reference does not exist * @throws ParseException When reference does not exist
*/ */
private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $context) private function parseValue($value, $flags, $context)
{ {
if (0 === strpos($value, '*')) { if (0 === strpos($value, '*')) {
if (false !== $pos = strpos($value, '#')) { if (false !== $pos = strpos($value, '#')) {
@ -503,20 +546,47 @@ class Parser
return $this->refs[$value]; return $this->refs[$value];
} }
if (preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { if (preg_match('/^'.self::TAG_PATTERN.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
return $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers));
if (isset($matches['tag']) && '!!binary' === $matches['tag']) {
return Inline::evaluateBinaryScalar($data);
}
return $data;
} }
try { try {
$parsedValue = Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null;
if ('mapping' === $context && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { // do not take following lines into account when the current line is a quoted single line value
@trigger_error(sprintf('Using a colon in the unquoted mapping value "%s" in line %d is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $value, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); if (null !== $quotation && preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) {
return Inline::parse($value, $flags, $this->refs);
}
// to be thrown in 3.0 while ($this->moveToNextLine()) {
// throw new ParseException('A colon cannot be used in an unquoted mapping value.'); // unquoted strings end before the first unindented line
if (null === $quotation && $this->getCurrentLineIndentation() === 0) {
$this->moveToPreviousLine();
break;
}
$value .= ' '.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;
}
}
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.');
} }
return $parsedValue; return $parsedValue;
@ -602,7 +672,7 @@ class Parser
$previousLineIndented = false; $previousLineIndented = false;
$previousLineBlank = false; $previousLineBlank = false;
for ($i = 0; $i < count($blockLines); ++$i) { for ($i = 0, $blockLinesCount = count($blockLines); $i < $blockLinesCount; ++$i) {
if ('' === $blockLines[$i]) { if ('' === $blockLines[$i]) {
$text .= "\n"; $text .= "\n";
$previousLineIndented = false; $previousLineIndented = false;

View File

@ -11,10 +11,12 @@
namespace Symfony\Component\Yaml\Tests; namespace Symfony\Component\Yaml\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Dumper; use Symfony\Component\Yaml\Dumper;
use Symfony\Component\Yaml\Yaml;
class DumperTest extends \PHPUnit_Framework_TestCase class DumperTest extends TestCase
{ {
protected $parser; protected $parser;
protected $dumper; protected $dumper;
@ -50,6 +52,34 @@ class DumperTest extends \PHPUnit_Framework_TestCase
$this->array = null; $this->array = null;
} }
public function testIndentationInConstructor()
{
$dumper = new Dumper(7);
$expected = <<<'EOF'
'': bar
foo: '#bar'
'foo''bar': { }
bar:
- 1
- foo
foobar:
foo: bar
bar:
- 1
- foo
foobar:
foo: bar
bar:
- 1
- foo
EOF;
$this->assertEquals($expected, $dumper->dump($this->array, 4, 0));
}
/**
* @group legacy
*/
public function testSetIndentation() public function testSetIndentation()
{ {
$this->dumper->setIndentation(7); $this->dumper->setIndentation(7);
@ -177,6 +207,16 @@ EOF;
} }
public function testObjectSupportEnabled() public function testObjectSupportEnabled()
{
$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');
}
/**
* @group legacy
*/
public function testObjectSupportEnabledPassingTrue()
{ {
$dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true);
@ -195,7 +235,16 @@ EOF;
*/ */
public function testObjectSupportDisabledWithExceptions() public function testObjectSupportDisabledWithExceptions()
{ {
$this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true, false); $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE);
}
/**
* @group legacy
* @expectedException \Symfony\Component\Yaml\Exception\DumpException
*/
public function testObjectSupportDisabledWithExceptionsPassingTrue()
{
$this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true);
} }
/** /**
@ -209,33 +258,103 @@ EOF;
public function getEscapeSequences() public function getEscapeSequences()
{ {
return array( return array(
'null' => array("\t\\0", '"\t\\\\0"'), 'empty string' => array('', "''"),
'bell' => array("\t\\a", '"\t\\\\a"'), 'null' => array("\x0", '"\\0"'),
'backspace' => array("\t\\b", '"\t\\\\b"'), 'bell' => array("\x7", '"\\a"'),
'horizontal-tab' => array("\t\\t", '"\t\\\\t"'), 'backspace' => array("\x8", '"\\b"'),
'line-feed' => array("\t\\n", '"\t\\\\n"'), 'horizontal-tab' => array("\t", '"\\t"'),
'vertical-tab' => array("\t\\v", '"\t\\\\v"'), 'line-feed' => array("\n", '"\\n"'),
'form-feed' => array("\t\\f", '"\t\\\\f"'), 'vertical-tab' => array("\v", '"\\v"'),
'carriage-return' => array("\t\\r", '"\t\\\\r"'), 'form-feed' => array("\xC", '"\\f"'),
'escape' => array("\t\\e", '"\t\\\\e"'), 'carriage-return' => array("\r", '"\\r"'),
'space' => array("\t\\ ", '"\t\\\\ "'), 'escape' => array("\x1B", '"\\e"'),
'double-quote' => array("\t\\\"", '"\t\\\\\\""'), 'space' => array(' ', "' '"),
'slash' => array("\t\\/", '"\t\\\\/"'), 'double-quote' => array('"', "'\"'"),
'backslash' => array("\t\\\\", '"\t\\\\\\\\"'), 'slash' => array('/', '/'),
'next-line' => array("\t\\N", '"\t\\\\N"'), 'backslash' => array('\\', '\\'),
'non-breaking-space' => array("\t\\<EFBFBD>", '"\t\\\\<5C>"'), 'next-line' => array("\xC2\x85", '"\\N"'),
'line-separator' => array("\t\\L", '"\t\\\\L"'), 'non-breaking-space' => array("\xc2\xa0", '"\\_"'),
'paragraph-separator' => array("\t\\P", '"\t\\\\P"'), 'line-separator' => array("\xE2\x80\xA8", '"\\L"'),
'paragraph-separator' => array("\xE2\x80\xA9", '"\\P"'),
'colon' => array(':', "':'"),
); );
} }
public function testBinaryDataIsDumpedBase64Encoded()
{
$binaryData = file_get_contents(__DIR__.'/Fixtures/arrow.gif');
$expected = '{ data: !!binary '.base64_encode($binaryData).' }';
$this->assertSame($expected, $this->dumper->dump(array('data' => $binaryData)));
}
public function testNonUtf8DataIsDumpedBase64Encoded()
{
// "für" (ISO-8859-1 encoded)
$this->assertSame('!!binary ZsM/cg==', $this->dumper->dump("f\xc3\x3fr"));
}
/**
* @dataProvider objectAsMapProvider
*/
public function testDumpObjectAsMap($object, $expected)
{
$yaml = $this->dumper->dump($object, 0, 0, Yaml::DUMP_OBJECT_AS_MAP);
$this->assertEquals($expected, Yaml::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
}
public function objectAsMapProvider()
{
$tests = array();
$bar = new \stdClass();
$bar->class = 'classBar';
$bar->args = array('bar');
$zar = new \stdClass();
$foo = new \stdClass();
$foo->bar = $bar;
$foo->zar = $zar;
$object = new \stdClass();
$object->foo = $foo;
$tests['stdClass'] = array($object, $object);
$arrayObject = new \ArrayObject();
$arrayObject['foo'] = 'bar';
$arrayObject['baz'] = 'foobar';
$parsedArrayObject = new \stdClass();
$parsedArrayObject->foo = 'bar';
$parsedArrayObject->baz = 'foobar';
$tests['ArrayObject'] = array($arrayObject, $parsedArrayObject);
$a = new A();
$tests['arbitrary-object'] = array($a, null);
return $tests;
}
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",
'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, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
}
/** /**
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
* @expectedExceptionMessage The indentation must be greater than zero * @expectedExceptionMessage The indentation must be greater than zero
*/ */
public function testZeroIndentationThrowsException() public function testZeroIndentationThrowsException()
{ {
$this->dumper->setIndentation(0); new Dumper(0);
} }
/** /**
@ -244,7 +363,7 @@ EOF;
*/ */
public function testNegativeIndentationThrowsException() public function testNegativeIndentationThrowsException()
{ {
$this->dumper->setIndentation(-4); new Dumper(-4);
} }
} }

View File

@ -509,24 +509,31 @@ test: Integers
spec: 2.19 spec: 2.19
yaml: | yaml: |
canonical: 12345 canonical: 12345
decimal: +12,345
octal: 014 octal: 014
hexadecimal: 0xC hexadecimal: 0xC
php: | php: |
array( array(
'canonical' => 12345, 'canonical' => 12345,
'decimal' => 12345.0,
'octal' => 014, 'octal' => 014,
'hexadecimal' => 0xC '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 # FIX: spec shows parens around -inf and NaN
test: Floating point test: Floating point
spec: 2.20 spec: 2.20
yaml: | yaml: |
canonical: 1.23015e+3 canonical: 1.23015e+3
exponential: 12.3015e+02 exponential: 12.3015e+02
fixed: 1,230.15
negative infinity: -.inf negative infinity: -.inf
not a number: .NaN not a number: .NaN
float as whole number: !!float 1 float as whole number: !!float 1
@ -534,12 +541,21 @@ php: |
array( array(
'canonical' => 1230.15, 'canonical' => 1230.15,
'exponential' => 1230.15, 'exponential' => 1230.15,
'fixed' => 1230.15,
'negative infinity' => log(0), 'negative infinity' => log(0),
'not a number' => -log(0), 'not a number' => -log(0),
'float as whole number' => (float) 1 'float as whole number' => (float) 1
) )
--- ---
test: Fixed Floating point
deprecated: true
spec: 2.20
yaml: |
fixed: 1,230.15
php: |
array(
'fixed' => 1230.15,
)
---
test: Miscellaneous test: Miscellaneous
spec: 2.21 spec: 2.21
yaml: | yaml: |
@ -1532,29 +1548,43 @@ php: |
test: Integer test: Integer
yaml: | yaml: |
canonical: 12345 canonical: 12345
decimal: +12,345
octal: 014 octal: 014
hexadecimal: 0xC hexadecimal: 0xC
php: | php: |
array( array(
'canonical' => 12345, 'canonical' => 12345,
'decimal' => 12345.0,
'octal' => 12, 'octal' => 12,
'hexadecimal' => 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 test: Float
yaml: | yaml: |
canonical: 1.23015e+3 canonical: 1.23015e+3
exponential: 12.3015e+02 exponential: 12.3015e+02
fixed: 1,230.15
negative infinity: -.inf negative infinity: -.inf
not a number: .NaN not a number: .NaN
php: | php: |
array( array(
'canonical' => 1230.15, 'canonical' => 1230.15,
'exponential' => 1230.15, 'exponential' => 1230.15,
'fixed' => 1230.15,
'negative infinity' => log(0), 'negative infinity' => log(0),
'not a number' => -log(0) 'not a number' => -log(0)
) )

View File

@ -176,13 +176,37 @@ brief: >
yaml: | yaml: |
zero: 0 zero: 0
simple: 12 simple: 12
one-thousand: 1,000
negative one-thousand: -1,000
php: | php: |
array( array(
'zero' => 0, 'zero' => 0,
'simple' => 12, 'simple' => 12,
)
---
test: Positive 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: |
one-thousand: 1,000
php: |
array(
'one-thousand' => 1000.0, '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 'negative one-thousand' => -1000.0
) )
--- ---
@ -208,15 +232,27 @@ brief: >
positive and negative infinity and "not a number." positive and negative infinity and "not a number."
yaml: | yaml: |
a simple float: 2.00 a simple float: 2.00
larger float: 1,000.09
scientific notation: 1.00009e+3 scientific notation: 1.00009e+3
php: | php: |
array( array(
'a simple float' => 2.0, 'a simple float' => 2.0,
'larger float' => 1000.09,
'scientific notation' => 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 test: Time
todo: true todo: true
brief: > brief: >

View File

@ -10,19 +10,17 @@ yaml: |
a: Steve a: Steve
b: Clark b: Clark
c: Brian c: Brian
e: notnull
bar: bar:
a: before a: before
d: other d: other
e: ~
<<: *foo <<: *foo
b: new b: new
x: Oren x: Oren
c: c:
foo: bar foo: bar
foo: ignore
bar: foo bar: foo
duplicate:
foo: bar
foo: ignore
foo2: &foo2 foo2: &foo2
a: Ballmer a: Ballmer
ding: &dong [ fi, fei, fo, fam] ding: &dong [ fi, fei, fo, fam]
@ -46,13 +44,12 @@ yaml: |
<<: *nestedref <<: *nestedref
php: | php: |
array( array(
'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull'),
'bar' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), 'bar' => array('a' => 'before', 'd' => 'other', 'e' => null, 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'),
'duplicate' => array('foo' => 'bar'),
'foo2' => array('a' => 'Ballmer'), 'foo2' => array('a' => 'Ballmer'),
'ding' => array('fi', 'fei', 'fo', 'fam'), 'ding' => array('fi', 'fei', 'fo', 'fam'),
'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'),
'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam'), 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'),
'taz' => array('a' => 'Steve', 'w' => array('p' => 1234)), '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))
) )

View File

@ -11,9 +11,12 @@
namespace Symfony\Component\Yaml\Tests; namespace Symfony\Component\Yaml\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Inline; use Symfony\Component\Yaml\Inline;
use Symfony\Component\Yaml\Yaml;
class InlineTest extends \PHPUnit_Framework_TestCase class InlineTest extends TestCase
{ {
/** /**
* @dataProvider getTestsForParse * @dataProvider getTestsForParse
@ -27,6 +30,55 @@ class InlineTest extends \PHPUnit_Framework_TestCase
* @dataProvider getTestsForParseWithMapObjects * @dataProvider getTestsForParseWithMapObjects
*/ */
public function testParseWithMapObjects($yaml, $value) public function testParseWithMapObjects($yaml, $value)
{
$actual = Inline::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP);
$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)),
);
}
/**
* @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
* @dataProvider getTestsForParseWithMapObjects
*/
public function testParseWithMapObjectsPassingTrue($yaml, $value)
{ {
$actual = Inline::parse($yaml, false, false, true); $actual = Inline::parse($yaml, false, false, true);
@ -58,10 +110,8 @@ class InlineTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('1.2', Inline::dump(1.2)); $this->assertEquals('1.2', Inline::dump(1.2));
$this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0))); $this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0)));
} finally {
setlocale(LC_NUMERIC, $locale); setlocale(LC_NUMERIC, $locale);
} catch (\Exception $e) {
setlocale(LC_NUMERIC, $locale);
throw $e;
} }
} }
@ -73,12 +123,12 @@ class InlineTest extends \PHPUnit_Framework_TestCase
} }
/** /**
* @group legacy * @expectedException \Symfony\Component\Yaml\Exception\ParseException
* throws \Symfony\Component\Yaml\Exception\ParseException in 3.0 * @expectedExceptionMessage Found unknown escape character "\V".
*/ */
public function testParseScalarWithNonEscapedBlackslashShouldThrowException() public function testParseScalarWithNonEscapedBlackslashShouldThrowException()
{ {
$this->assertSame('Foo\Var', Inline::parse('"Foo\Var"')); Inline::parse('"Foo\Var"');
} }
/** /**
@ -116,6 +166,16 @@ class InlineTest extends \PHPUnit_Framework_TestCase
Inline::parse($value); Inline::parse($value);
} }
/**
* @group legacy
* @expectedDeprecation Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.
* throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
*/
public function testParseMappingKeyWithColonNotFollowedBySpace()
{
Inline::parse('{1:""}');
}
/** /**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException * @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/ */
@ -144,6 +204,15 @@ class InlineTest extends \PHPUnit_Framework_TestCase
* @dataProvider getDataForParseReferences * @dataProvider getDataForParseReferences
*/ */
public function testParseReferences($yaml, $expected) public function testParseReferences($yaml, $expected)
{
$this->assertSame($expected, Inline::parse($yaml, 0, array('var' => 'var-value')));
}
/**
* @group legacy
* @dataProvider getDataForParseReferences
*/
public function testParseReferencesAsFifthArgument($yaml, $expected)
{ {
$this->assertSame($expected, Inline::parse($yaml, false, false, false, array('var' => 'var-value'))); $this->assertSame($expected, Inline::parse($yaml, false, false, false, array('var' => 'var-value')));
} }
@ -163,6 +232,19 @@ class InlineTest extends \PHPUnit_Framework_TestCase
} }
public function testParseMapReferenceInSequence() public function testParseMapReferenceInSequence()
{
$foo = array(
'a' => 'Steve',
'b' => 'Clark',
'c' => 'Brian',
);
$this->assertSame(array($foo), Inline::parse('[*foo]', 0, array('foo' => $foo)));
}
/**
* @group legacy
*/
public function testParseMapReferenceInSequenceAsFifthArgument()
{ {
$foo = array( $foo = array(
'a' => 'Steve', 'a' => 'Steve',
@ -191,9 +273,9 @@ class InlineTest extends \PHPUnit_Framework_TestCase
} }
/** /**
* @group legacy
* @dataProvider getReservedIndicators * @dataProvider getReservedIndicators
* throws \Symfony\Component\Yaml\Exception\ParseException in 3.0 * @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
*/ */
public function testParseUnquotedScalarStartingWithReservedIndicator($indicator) public function testParseUnquotedScalarStartingWithReservedIndicator($indicator)
{ {
@ -206,9 +288,9 @@ class InlineTest extends \PHPUnit_Framework_TestCase
} }
/** /**
* @group legacy
* @dataProvider getScalarIndicators * @dataProvider getScalarIndicators
* throws \Symfony\Component\Yaml\Exception\ParseException in 3.0 * @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
*/ */
public function testParseUnquotedScalarStartingWithScalarIndicator($indicator) public function testParseUnquotedScalarStartingWithScalarIndicator($indicator)
{ {
@ -220,6 +302,16 @@ class InlineTest extends \PHPUnit_Framework_TestCase
return array(array('|'), array('>')); return array(array('|'), array('>'));
} }
/**
* @group legacy
* @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.
* throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
*/
public function testParseUnquotedScalarStartingWithPercentCharacter()
{
Inline::parse('{ foo: %bar }');
}
/** /**
* @dataProvider getDataForIsHash * @dataProvider getDataForIsHash
*/ */
@ -247,11 +339,17 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('true', true), array('true', true),
array('12', 12), array('12', 12),
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("'quoted string'", 'quoted string'), array("'quoted string'", 'quoted string'),
array('12.30e+02', 12.30e+02), array('12.30e+02', 12.30e+02),
array('123.45_67', 123.4567),
array('0x4D2', 0x4D2), array('0x4D2', 0x4D2),
array('0x_4_D_2_', 0x4D2),
array('02333', 02333), array('02333', 02333),
array('0_2_3_3_3', 02333),
array('.Inf', -log(0)), array('.Inf', -log(0)),
array('-.Inf', log(0)), array('-.Inf', log(0)),
array("'686e444'", '686e444'), array("'686e444'", '686e444'),
@ -287,7 +385,7 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')), array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')),
// mappings // 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')),
@ -299,6 +397,8 @@ class InlineTest extends \PHPUnit_Framework_TestCase
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'))),
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'))),
@ -354,7 +454,7 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')), array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')),
// mappings // 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)), 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: 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')),
@ -399,10 +499,15 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('false', false), array('false', false),
array('true', true), array('true', true),
array('12', 12), array('12', 12),
array("'1_2'", '1_2'),
array('_12', '_12'),
array("'12_'", '12_'),
array("'quoted string'", 'quoted string'), array("'quoted string'", 'quoted string'),
array('!!float 1230', 12.30e+02), array('!!float 1230', 12.30e+02),
array('1234', 0x4D2), array('1234', 0x4D2),
array('1243', 02333), 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('-.Inf', log(0)), array('-.Inf', log(0)),
array("'686e444'", '686e444'), array("'686e444'", '686e444'),
@ -448,4 +553,160 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('{ foo: { bar: { 1: 2, baz: 3 } } }', array('foo' => array('bar' => array(1 => 2, 'baz' => 3)))), array('{ foo: { bar: { 1: 2, baz: 3 } } }', array('foo' => array('bar' => array(1 => 2, 'baz' => 3)))),
); );
} }
/**
* @dataProvider getTimestampTests
*/
public function testParseTimestampAsUnixTimestampByDefault($yaml, $year, $month, $day, $hour, $minute, $second)
{
$this->assertSame(gmmktime($hour, $minute, $second, $month, $day, $year), Inline::parse($yaml));
}
/**
* @dataProvider getTimestampTests
*/
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);
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.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'),
);
}
/**
* @dataProvider getTimestampTests
*/
public function testParseNestedTimestampListAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second)
{
$expected = new \DateTime($yaml);
$expected->setTimeZone(new \DateTimeZone('UTC'));
$expected->setDate($year, $month, $day);
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]}";
$this->assertEquals($expectedNested, Inline::parse($yamlNested, Yaml::PARSE_DATETIME));
}
/**
* @dataProvider getDateTimeDumpTests
*/
public function testDumpDateTime($dateTime, $expected)
{
$this->assertSame($expected, Inline::dump($dateTime));
}
public function getDateTimeDumpTests()
{
$tests = array();
$dateTime = new \DateTime('2001-12-15 21:59:43', new \DateTimeZone('UTC'));
$tests['date-time-utc'] = array($dateTime, '2001-12-15T21:59:43+00:00');
$dateTime = new \DateTimeImmutable('2001-07-15 21:59:43', new \DateTimeZone('Europe/Berlin'));
$tests['immutable-date-time-europe-berlin'] = array($dateTime, '2001-07-15T21:59:43+02:00');
return $tests;
}
/**
* @dataProvider getBinaryData
*/
public function testParseBinaryData($data)
{
$this->assertSame('Hello world', Inline::parse($data));
}
public function getBinaryData()
{
return array(
'enclosed with double quotes' => array('!!binary "SGVsbG8gd29ybGQ="'),
'enclosed with single quotes' => array("!!binary 'SGVsbG8gd29ybGQ='"),
'containing spaces' => array('!!binary "SGVs bG8gd 29ybGQ="'),
);
}
/**
* @dataProvider getInvalidBinaryData
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
public function testParseInvalidBinaryData($data, $expectedMessage)
{
if (method_exists($this, 'expectException')) {
$this->expectExceptionMessageRegExp($expectedMessage);
} else {
$this->setExpectedExceptionRegExp(ParseException::class, $expectedMessage);
}
Inline::parse($data);
}
public function getInvalidBinaryData()
{
return array(
'length not a multiple of four' => array('!!binary "SGVsbG8d29ybGQ="', '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/'),
'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'),
'too many equals characters' => array('!!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'),
'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}.
*/
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']);
}
public function testOmittedMappingKeyIsParsedAsColon()
{
$this->assertSame(array(':' => 'foo'), Inline::parse('{: foo}'));
}
public function testBooleanMappingKeysAreConvertedToStrings()
{
$this->assertSame(array('false' => 'foo'), Inline::parse('{false: foo}'));
$this->assertSame(array('true' => 'foo'), Inline::parse('{true: foo}'));
}
public function testTheEmptyStringIsAValidMappingKey()
{
$this->assertSame(array('' => 'foo'), Inline::parse('{ "": foo }'));
}
} }

View File

@ -11,18 +11,15 @@
namespace Symfony\Component\Yaml\Tests; namespace Symfony\Component\Yaml\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Exception\ParseException;
class ParseExceptionTest extends \PHPUnit_Framework_TestCase class ParseExceptionTest extends TestCase
{ {
public function testGetMessage() public function testGetMessage()
{ {
$exception = new ParseException('Error message', 42, 'foo: bar', '/var/www/app/config.yml'); $exception = new ParseException('Error message', 42, 'foo: bar', '/var/www/app/config.yml');
if (PHP_VERSION_ID >= 50400) { $message = 'Error message in "/var/www/app/config.yml" at line 42 (near "foo: bar")';
$message = 'Error message in "/var/www/app/config.yml" at line 42 (near "foo: bar")';
} else {
$message = 'Error message in "\\/var\\/www\\/app\\/config.yml" at line 42 (near "foo: bar")';
}
$this->assertEquals($message, $exception->getMessage()); $this->assertEquals($message, $exception->getMessage());
} }
@ -30,11 +27,7 @@ class ParseExceptionTest extends \PHPUnit_Framework_TestCase
public function testGetMessageWithUnicodeInFilename() public function testGetMessageWithUnicodeInFilename()
{ {
$exception = new ParseException('Error message', 42, 'foo: bar', 'äöü.yml'); $exception = new ParseException('Error message', 42, 'foo: bar', 'äöü.yml');
if (PHP_VERSION_ID >= 50400) { $message = 'Error message in "äöü.yml" at line 42 (near "foo: bar")';
$message = 'Error message in "äöü.yml" at line 42 (near "foo: bar")';
} else {
$message = 'Error message in "\u00e4\u00f6\u00fc.yml" at line 42 (near "foo: bar")';
}
$this->assertEquals($message, $exception->getMessage()); $this->assertEquals($message, $exception->getMessage());
} }

View File

@ -11,10 +11,12 @@
namespace Symfony\Component\Yaml\Tests; namespace Symfony\Component\Yaml\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Parser;
class ParserTest extends \PHPUnit_Framework_TestCase class ParserTest extends TestCase
{ {
protected $parser; protected $parser;
@ -31,9 +33,34 @@ class ParserTest extends \PHPUnit_Framework_TestCase
/** /**
* @dataProvider getDataFormSpecifications * @dataProvider getDataFormSpecifications
*/ */
public function testSpecifications($file, $expected, $yaml, $comment) public function testSpecifications($file, $expected, $yaml, $comment, $deprecated)
{ {
$deprecations = array();
if ($deprecated) {
set_error_handler(function ($type, $msg) use (&$deprecations) {
if (E_USER_DEPRECATED !== $type) {
restore_error_handler();
if (class_exists('PHPUnit_Util_ErrorHandler')) {
return call_user_func_array('PHPUnit_Util_ErrorHandler::handleError', func_get_args());
}
return call_user_func_array('PHPUnit\Util\ErrorHandler::handleError', func_get_args());
}
$deprecations[] = $msg;
});
}
$this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment); $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
if ($deprecated) {
restore_error_handler();
$this->assertCount(1, $deprecations);
$this->assertContains('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0.', $deprecations[0]);
}
} }
public function getDataFormSpecifications() public function getDataFormSpecifications()
@ -58,7 +85,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase
} else { } else {
eval('$expected = '.trim($test['php']).';'); eval('$expected = '.trim($test['php']).';');
$tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']); $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test'], isset($test['deprecated']) ? $test['deprecated'] : false);
} }
} }
} }
@ -189,6 +216,22 @@ EOF;
); );
$tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml); $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
$yaml = <<<'EOF'
foo:
- bar: |
one
two
EOF;
$expected = array(
'foo' => array(
array(
'bar' => "one\n\ntwo",
),
),
);
$tests['Literal block chomping clip with embedded blank line inside unindented collection'] = array($expected, $yaml);
$yaml = <<<'EOF' $yaml = <<<'EOF'
foo: | foo: |
one one
@ -421,19 +464,37 @@ EOF;
public function testObjectSupportEnabled() public function testObjectSupportEnabled()
{ {
$input = <<<EOF $input = <<<'EOF'
foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";} foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
bar: 1 bar: 1
EOF; EOF;
$this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects'); $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
}
$input = <<<EOF /**
* @group legacy
*/
public function testObjectSupportEnabledPassingTrue()
{
$input = <<<'EOF'
foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";} foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
bar: 1 bar: 1
EOF; EOF;
$this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects'); $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
} }
/**
* @group legacy
*/
public function testObjectSupportEnabledWithDeprecatedTag()
{
$input = <<<'EOF'
foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
bar: 1
EOF;
$this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, Yaml::PARSE_OBJECT), '->parse() is able to parse objects');
}
/** /**
* @dataProvider invalidDumpedObjectProvider * @dataProvider invalidDumpedObjectProvider
*/ */
@ -446,6 +507,15 @@ EOF;
* @dataProvider getObjectForMapTests * @dataProvider getObjectForMapTests
*/ */
public function testObjectForMap($yaml, $expected) public function testObjectForMap($yaml, $expected)
{
$this->assertEquals($expected, $this->parser->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP));
}
/**
* @group legacy
* @dataProvider getObjectForMapTests
*/
public function testObjectForMapEnabledWithMappingUsingBooleanToggles($yaml, $expected)
{ {
$this->assertEquals($expected, $this->parser->parse($yaml, false, false, true)); $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true));
} }
@ -454,7 +524,7 @@ EOF;
{ {
$tests = array(); $tests = array();
$yaml = <<<EOF $yaml = <<<'EOF'
foo: foo:
fiz: [cat] fiz: [cat]
EOF; EOF;
@ -475,7 +545,7 @@ EOF;
$expected->baz = 'foobar'; $expected->baz = 'foobar';
$tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected); $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected);
$yaml = <<<EOT $yaml = <<<'EOT'
array: array:
- key: one - key: one
- key: two - key: two
@ -488,7 +558,7 @@ EOT;
$expected->array[1]->key = 'two'; $expected->array[1]->key = 'two';
$tests['nest-map-and-sequence'] = array($yaml, $expected); $tests['nest-map-and-sequence'] = array($yaml, $expected);
$yaml = <<<YAML $yaml = <<<'YAML'
map: map:
1: one 1: one
2: two 2: two
@ -499,7 +569,7 @@ YAML;
$expected->map->{2} = 'two'; $expected->map->{2} = 'two';
$tests['numeric-keys'] = array($yaml, $expected); $tests['numeric-keys'] = array($yaml, $expected);
$yaml = <<<YAML $yaml = <<<'YAML'
map: map:
0: one 0: one
1: two 1: two
@ -519,16 +589,26 @@ YAML;
*/ */
public function testObjectsSupportDisabledWithExceptions($yaml) public function testObjectsSupportDisabledWithExceptions($yaml)
{ {
$this->parser->parse($yaml, true, false); $this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
}
/**
* @group legacy
* @dataProvider invalidDumpedObjectProvider
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
public function testObjectsSupportDisabledWithExceptionsUsingBooleanToggles($yaml)
{
$this->parser->parse($yaml, true);
} }
public function invalidDumpedObjectProvider() public function invalidDumpedObjectProvider()
{ {
$yamlTag = <<<EOF $yamlTag = <<<'EOF'
foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
bar: 1 bar: 1
EOF; EOF;
$localTag = <<<EOF $localTag = <<<'EOF'
foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";} foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
bar: 1 bar: 1
EOF; EOF;
@ -630,7 +710,7 @@ EOF
public function testSequenceInMappingStartedBySingleDashLine() public function testSequenceInMappingStartedBySingleDashLine()
{ {
$yaml = <<<EOT $yaml = <<<'EOT'
a: a:
- -
b: b:
@ -656,6 +736,25 @@ EOT;
$this->assertSame($expected, $this->parser->parse($yaml)); $this->assertSame($expected, $this->parser->parse($yaml));
} }
public function testSequenceFollowedByCommentEmbeddedInMapping()
{
$yaml = <<<'EOT'
a:
b:
- c
# comment
d: e
EOT;
$expected = array(
'a' => array(
'b' => array('c'),
'd' => 'e',
),
);
$this->assertSame($expected, $this->parser->parse($yaml));
}
/** /**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException * @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/ */
@ -675,7 +774,7 @@ EOF
*/ */
public function testScalarInSequence() public function testScalarInSequence()
{ {
Yaml::parse(<<<EOF Yaml::parse(<<<'EOF'
foo: foo:
- bar - bar
"missing colon" "missing colon"
@ -693,10 +792,11 @@ EOF
* *
* @see http://yaml.org/spec/1.2/spec.html#id2759572 * @see http://yaml.org/spec/1.2/spec.html#id2759572
* @see http://yaml.org/spec/1.1/#id932806 * @see http://yaml.org/spec/1.1/#id932806
* @group legacy
*/ */
public function testMappingDuplicateKeyBlock() public function testMappingDuplicateKeyBlock()
{ {
$input = <<<EOD $input = <<<'EOD'
parent: parent:
child: first child: first
child: duplicate child: duplicate
@ -712,9 +812,12 @@ EOD;
$this->assertSame($expected, Yaml::parse($input)); $this->assertSame($expected, Yaml::parse($input));
} }
/**
* @group legacy
*/
public function testMappingDuplicateKeyFlow() public function testMappingDuplicateKeyFlow()
{ {
$input = <<<EOD $input = <<<'EOD'
parent: { child: first, child: duplicate } parent: { child: first, child: duplicate }
parent: { child: duplicate, child: duplicate } parent: { child: duplicate, child: duplicate }
EOD; EOD;
@ -726,6 +829,74 @@ EOD;
$this->assertSame($expected, Yaml::parse($input)); $this->assertSame($expected, Yaml::parse($input));
} }
/**
* @group legacy
* @dataProvider getParseExceptionOnDuplicateData
* @expectedDeprecation Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated %s.
* throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
*/
public function testParseExceptionOnDuplicate($input, $duplicateKey, $lineNumber)
{
Yaml::parse($input);
}
public function getParseExceptionOnDuplicateData()
{
$tests = array();
$yaml = <<<EOD
parent: { child: first, child: duplicate }
EOD;
$tests[] = array($yaml, 'child', 1);
$yaml = <<<EOD
parent:
child: first,
child: duplicate
EOD;
$tests[] = array($yaml, 'child', 3);
$yaml = <<<EOD
parent: { child: foo }
parent: { child: bar }
EOD;
$tests[] = array($yaml, 'parent', 2);
$yaml = <<<EOD
parent: { child_mapping: { value: bar}, child_mapping: { value: bar} }
EOD;
$tests[] = array($yaml, 'child_mapping', 1);
$yaml = <<<EOD
parent:
child_mapping:
value: bar
child_mapping:
value: bar
EOD;
$tests[] = array($yaml, 'child_mapping', 4);
$yaml = <<<EOD
parent: { child_sequence: ['key1', 'key2', 'key3'], child_sequence: ['key1', 'key2', 'key3'] }
EOD;
$tests[] = array($yaml, 'child_sequence', 1);
$yaml = <<<EOD
parent:
child_sequence:
- key1
- key2
- key3
child_sequence:
- key1
- key2
- key3
EOD;
$tests[] = array($yaml, 'child_sequence', 6);
return $tests;
}
public function testEmptyValue() public function testEmptyValue()
{ {
$input = <<<'EOF' $input = <<<'EOF'
@ -906,37 +1077,21 @@ EOF;
} }
/** /**
* @group legacy * @expectedException \Symfony\Component\Yaml\Exception\ParseException
* throw ParseException in Symfony 3.0 * @expectedExceptionMessage A colon cannot be used in an unquoted mapping value
*/ */
public function testColonInMappingValueException() public function testColonInMappingValueException()
{ {
$yaml = <<<EOF $yaml = <<<'EOF'
foo: bar: baz foo: bar: baz
EOF; EOF;
$deprecations = array();
set_error_handler(function ($type, $msg) use (&$deprecations) {
if (E_USER_DEPRECATED !== $type) {
restore_error_handler();
return call_user_func_array('PHPUnit_Util_ErrorHandler::handleError', func_get_args());
}
$deprecations[] = $msg;
});
$this->parser->parse($yaml); $this->parser->parse($yaml);
restore_error_handler();
$this->assertCount(1, $deprecations);
$this->assertContains('Using a colon in the unquoted mapping value "bar: baz" in line 1 is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $deprecations[0]);
} }
public function testColonInMappingValueExceptionNotTriggeredByColonInComment() public function testColonInMappingValueExceptionNotTriggeredByColonInComment()
{ {
$yaml = <<<EOT $yaml = <<<'EOT'
foo: foo:
bar: foobar # Note: a comment after a colon bar: foobar # Note: a comment after a colon
EOT; EOT;
@ -1037,7 +1192,7 @@ EOT
); );
$tests[] = array($yaml, $expected); $tests[] = array($yaml, $expected);
$yaml = <<<EOT $yaml = <<<'EOT'
foo: foo:
bar: bar:
scalar-block: > scalar-block: >
@ -1080,7 +1235,7 @@ EOT;
public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks() public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks()
{ {
$yaml = <<<EOT $yaml = <<<'EOT'
test: > test: >
<h2>A heading</h2> <h2>A heading</h2>
@ -1092,7 +1247,7 @@ EOT;
$this->assertSame( $this->assertSame(
array( array(
'test' => <<<EOT 'test' => <<<'EOT'
<h2>A heading</h2> <h2>A heading</h2>
<ul> <li>a list</li> <li>may be a good example</li> </ul> <ul> <li>a list</li> <li>may be a good example</li> </ul>
EOT EOT
@ -1104,7 +1259,7 @@ EOT
public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks() public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks()
{ {
$yaml = <<<EOT $yaml = <<<'EOT'
test: > test: >
<h2>A heading</h2> <h2>A heading</h2>
@ -1116,7 +1271,7 @@ EOT;
$this->assertSame( $this->assertSame(
array( array(
'test' => <<<EOT 'test' => <<<'EOT'
<h2>A heading</h2> <h2>A heading</h2>
<ul> <ul>
<li>a list</li> <li>a list</li>
@ -1128,6 +1283,201 @@ EOT
$this->parser->parse($yaml) $this->parser->parse($yaml)
); );
} }
/**
* @dataProvider getBinaryData
*/
public function testParseBinaryData($data)
{
$this->assertSame(array('data' => 'Hello world'), $this->parser->parse($data));
}
public function getBinaryData()
{
return array(
'enclosed with double quotes' => array('data: !!binary "SGVsbG8gd29ybGQ="'),
'enclosed with single quotes' => array("data: !!binary 'SGVsbG8gd29ybGQ='"),
'containing spaces' => array('data: !!binary "SGVs bG8gd 29ybGQ="'),
'in block scalar' => array(
<<<'EOT'
data: !!binary |
SGVsbG8gd29ybGQ=
EOT
),
'containing spaces in block scalar' => array(
<<<'EOT'
data: !!binary |
SGVs bG8gd 29ybGQ=
EOT
),
);
}
/**
* @dataProvider getInvalidBinaryData
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
public function testParseInvalidBinaryData($data, $expectedMessage)
{
if (method_exists($this, 'expectException')) {
$this->expectExceptionMessageRegExp($expectedMessage);
} else {
$this->setExpectedExceptionRegExp(ParseException::class, $expectedMessage);
}
$this->parser->parse($data);
}
public function getInvalidBinaryData()
{
return array(
'length not a multiple of four' => array('data: !!binary "SGVsbG8d29ybGQ="', '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/'),
'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'),
'too many equals characters' => array('data: !!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'),
'misplaced equals character' => array('data: !!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'),
'length not a multiple of four in block scalar' => array(
<<<'EOT'
data: !!binary |
SGVsbG8d29ybGQ=
EOT
,
'/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/',
),
'invalid characters in block scalar' => array(
<<<'EOT'
data: !!binary |
SGVsbG8#d29ybGQ=
EOT
,
'/The base64 encoded data \(.*\) contains invalid characters/',
),
'too many equals characters in block scalar' => array(
<<<'EOT'
data: !!binary |
SGVsbG8gd29yb===
EOT
,
'/The base64 encoded data \(.*\) contains invalid characters/',
),
'misplaced equals character in block scalar' => array(
<<<'EOT'
data: !!binary |
SGVsbG8gd29ybG=Q
EOT
,
'/The base64 encoded data \(.*\) contains invalid characters/',
),
);
}
public function testParseDateAsMappingValue()
{
$yaml = <<<'EOT'
date: 2002-12-14
EOT;
$expectedDate = new \DateTime();
$expectedDate->setTimeZone(new \DateTimeZone('UTC'));
$expectedDate->setDate(2002, 12, 14);
$expectedDate->setTime(0, 0, 0);
$this->assertEquals(array('date' => $expectedDate), $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
}
/**
* @param $lineNumber
* @param $yaml
* @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
*/
public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
{
if (method_exists($this, 'expectException')) {
$this->expectException('\Symfony\Component\Yaml\Exception\ParseException');
$this->expectExceptionMessage(sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
} else {
$this->setExpectedException('\Symfony\Component\Yaml\Exception\ParseException', sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
}
$this->parser->parse($yaml);
}
public function parserThrowsExceptionWithCorrectLineNumberProvider()
{
return array(
array(
4,
<<<'YAML'
foo:
-
# bar
bar: "123",
YAML
),
array(
5,
<<<'YAML'
foo:
-
# bar
# bar
bar: "123",
YAML
),
array(
8,
<<<'YAML'
foo:
-
# foobar
baz: 123
bar:
-
# bar
bar: "123",
YAML
),
array(
10,
<<<'YAML'
foo:
-
# foobar
# foobar
baz: 123
bar:
-
# bar
# bar
bar: "123",
YAML
),
);
}
public function testParseMultiLineQuotedString()
{
$yaml = <<<EOT
foo: "bar
baz
foobar
foo"
bar: baz
EOT;
$this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
}
public function testParseMultiLineUnquotedString()
{
$yaml = <<<EOT
foo: bar
baz
foobar
foo
bar: baz
EOT;
$this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml));
}
} }
class B class B

View File

@ -11,9 +11,10 @@
namespace Symfony\Component\Yaml\Tests; namespace Symfony\Component\Yaml\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
class YamlTest extends \PHPUnit_Framework_TestCase class YamlTest extends TestCase
{ {
public function testParseAndDump() public function testParseAndDump()
{ {
@ -23,18 +24,6 @@ class YamlTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($data, $parsed); $this->assertEquals($data, $parsed);
} }
/**
* @group legacy
*/
public function testLegacyParseFromFile()
{
$filename = __DIR__.'/Fixtures/index.yml';
$contents = file_get_contents($filename);
$parsedByFilename = Yaml::parse($filename);
$parsedByContents = Yaml::parse($contents);
$this->assertEquals($parsedByFilename, $parsedByContents);
}
/** /**
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
* @expectedExceptionMessage The indentation must be greater than zero * @expectedExceptionMessage The indentation must be greater than zero

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\Yaml; namespace Symfony\Component\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;
/** /**
* Unescaper encapsulates unescaping rules for single and double-quoted * Unescaper encapsulates unescaping rules for single and double-quoted
* YAML strings. * YAML strings.
@ -21,16 +23,6 @@ namespace Symfony\Component\Yaml;
*/ */
class Unescaper class Unescaper
{ {
/**
* Parser and Inline assume UTF-8 encoding, so escaped Unicode characters
* must be converted to that encoding.
*
* @deprecated since version 2.5, to be removed in 3.0
*
* @internal
*/
const ENCODING = 'UTF-8';
/** /**
* Regex fragment that matches an escaped character in a double quoted string. * Regex fragment that matches an escaped character in a double quoted string.
*/ */
@ -39,9 +31,9 @@ class Unescaper
/** /**
* Unescapes a single quoted string. * Unescapes a single quoted string.
* *
* @param string $value A single quoted string. * @param string $value A single quoted string
* *
* @return string The unescaped string. * @return string The unescaped string
*/ */
public function unescapeSingleQuotedString($value) public function unescapeSingleQuotedString($value)
{ {
@ -51,15 +43,14 @@ class Unescaper
/** /**
* Unescapes a double quoted string. * Unescapes a double quoted string.
* *
* @param string $value A double quoted string. * @param string $value A double quoted string
* *
* @return string The unescaped string. * @return string The unescaped string
*/ */
public function unescapeDoubleQuotedString($value) public function unescapeDoubleQuotedString($value)
{ {
$self = $this; $callback = function ($match) {
$callback = function ($match) use ($self) { return $this->unescapeCharacter($match[0]);
return $self->unescapeCharacter($match[0]);
}; };
// evaluate the string // evaluate the string
@ -72,11 +63,8 @@ class Unescaper
* @param string $value An escaped character * @param string $value An escaped character
* *
* @return string The unescaped character * @return string The unescaped character
*
* @internal This method is public to be usable as callback. It should not
* be used in user code. Should be changed in 3.0.
*/ */
public function unescapeCharacter($value) private function unescapeCharacter($value)
{ {
switch ($value[1]) { switch ($value[1]) {
case '0': case '0':
@ -126,9 +114,7 @@ class Unescaper
case 'U': case 'U':
return self::utf8chr(hexdec(substr($value, 2, 8))); return self::utf8chr(hexdec(substr($value, 2, 8)));
default: default:
@trigger_error('Not escaping a backslash in a double-quoted string is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', E_USER_DEPRECATED); throw new ParseException(sprintf('Found unknown escape character "%s".', $value));
return $value;
} }
} }

View File

@ -20,6 +20,16 @@ use Symfony\Component\Yaml\Exception\ParseException;
*/ */
class Yaml class Yaml
{ {
const DUMP_OBJECT = 1;
const PARSE_EXCEPTION_ON_INVALID_TYPE = 2;
const PARSE_OBJECT = 4;
const PARSE_OBJECT_FOR_MAP = 8;
const DUMP_EXCEPTION_ON_INVALID_TYPE = 16;
const PARSE_DATETIME = 32;
const DUMP_OBJECT_AS_MAP = 64;
const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
const PARSE_CONSTANT = 256;
/** /**
* Parses YAML into a PHP value. * Parses YAML into a PHP value.
* *
@ -29,72 +39,81 @@ class Yaml
* print_r($array); * print_r($array);
* </code> * </code>
* *
* As this method accepts both plain strings and file names as an input, * @param string $input A string containing YAML
* you must validate the input before calling this method. Passing a file * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
* as an input is a deprecated feature and will be removed in 3.0.
*
* Note: the ability to pass file names to the Yaml::parse method is deprecated since version 2.2 and will be removed in 3.0. Pass the YAML contents of the file instead.
*
* @param string $input Path to a YAML file or a string containing YAML
* @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise
* @param bool $objectSupport True if object support is enabled, false otherwise
* @param bool $objectForMap True if maps should return a stdClass instead of array()
* *
* @return mixed The YAML converted to a PHP value * @return mixed The YAML converted to a PHP value
* *
* @throws ParseException If the YAML is not valid * @throws ParseException If the YAML is not valid
*/ */
public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) public static function parse($input, $flags = 0)
{ {
// if input is a file, process it if (is_bool($flags)) {
$file = ''; @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 (strpos($input, "\n") === false && is_file($input)) {
@trigger_error('The ability to pass file names to the '.__METHOD__.' method is deprecated since version 2.2 and will be removed in 3.0. Pass the YAML contents of the file instead.', E_USER_DEPRECATED);
if (false === is_readable($input)) { if ($flags) {
throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $input)); $flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
} }
}
$file = $input; if (func_num_args() >= 3) {
$input = file_get_contents($file); @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_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_get_arg(3)) {
$flags |= self::PARSE_OBJECT_FOR_MAP;
}
} }
$yaml = new Parser(); $yaml = new Parser();
try { return $yaml->parse($input, $flags);
return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport, $objectForMap);
} catch (ParseException $e) {
if ($file) {
$e->setParsedFile($file);
}
throw $e;
}
} }
/** /**
* Dumps a PHP array to a YAML string. * Dumps a PHP value to a YAML string.
* *
* The dump method, when supplied with an array, will do its best * The dump method, when supplied with an array, will do its best
* to convert the array into friendly YAML. * to convert the array into friendly YAML.
* *
* @param array $array PHP array * @param mixed $input The PHP value
* @param int $inline The level where you switch to inline YAML * @param int $inline The level where you switch to inline YAML
* @param int $indent The amount of spaces to use for indentation of nested nodes. * @param int $indent The amount of spaces to use for indentation of nested nodes
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string
* @param bool $objectSupport true if object support is enabled, false otherwise
* *
* @return string A YAML string representing the original PHP array * @return string A YAML string representing the original PHP value
*/ */
public static function dump($array, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false) public static function dump($input, $inline = 2, $indent = 4, $flags = 0)
{ {
if ($indent < 1) { if (is_bool($flags)) {
throw new \InvalidArgumentException('The indentation must be greater than zero.'); @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 ($flags) {
$flags = self::DUMP_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
}
} }
$yaml = new Dumper(); if (func_num_args() >= 5) {
$yaml->setIndentation($indent); @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);
return $yaml->dump($array, $inline, 0, $exceptionOnInvalidType, $objectSupport); if (func_get_arg(4)) {
$flags |= self::DUMP_OBJECT;
}
}
$yaml = new Dumper($indent);
return $yaml->dump($input, $inline, 0, $flags);
} }
} }

View File

@ -16,7 +16,13 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.5.9"
},
"require-dev": {
"symfony/console": "~2.8|~3.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
}, },
"autoload": { "autoload": {
"psr-4": { "Symfony\\Component\\Yaml\\": "" }, "psr-4": { "Symfony\\Component\\Yaml\\": "" },
@ -27,7 +33,7 @@
"minimum-stability": "dev", "minimum-stability": "dev",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.8-dev" "dev-master": "3.2-dev"
} }
} }
} }