跳到内容

Yaml 组件

编辑此页

Yaml 组件加载和转储 YAML 文件。

它是什么?

Symfony Yaml 组件解析 YAML 字符串以将其转换为 PHP 数组。它也能够将 PHP 数组转换为 YAML 字符串。

YAMLYAML 不是标记语言,是一种对所有编程语言都友好的数据序列化标准。 YAML 是配置文件的一个很好的格式。 YAML 文件和 XML 文件一样富有表现力,并且和 INI 文件一样可读。

提示

了解更多关于 YAML 规范的信息。

安装

1
$ composer require symfony/yaml

注意

如果你在 Symfony 应用程序之外安装此组件,你必须在你的代码中引入 vendor/autoload.php 文件,以启用 Composer 提供的类自动加载机制。阅读 这篇文章了解更多详情。

为什么?

快速

Symfony Yaml 的目标之一是在速度和功能之间找到合适的平衡。它仅支持处理配置文件所需的功能。 显著缺乏的功能包括:文档指令、多行引用消息、紧凑块集合和多文档文件。

真正的解析器

它支持真正的解析器,并且能够解析 YAML 规范的很大一部分子集,以满足你的所有配置需求。 这也意味着解析器非常健壮、易于理解并且足够简单以进行扩展。

清晰的错误消息

每当你的 YAML 文件出现语法问题时,该库都会输出一条有用的消息,其中包含文件名和问题发生的行号。 这大大简化了调试。

转储支持

它还能够将 PHP 数组转储为 YAML,并支持对象和内联级别配置,以获得漂亮的输出。

类型支持

它支持大多数 YAML 内置类型,例如日期、整数、八进制数、布尔值等等...

完全合并键支持

完全支持引用、别名和完全合并键。 通过引用通用配置位,避免重复自己。

使用 Symfony YAML 组件

Symfony Yaml 组件由两个主要类组成:一个解析 YAML 字符串 (Parser),另一个将 PHP 数组转储为 YAML 字符串 (Dumper)。

在这两个类之上,Yaml 类充当一个轻量级的包装器,简化了常用用法。

读取 YAML 内容

parse() 方法解析 YAML 字符串并将其转换为 PHP 数组

1
2
3
4
use Symfony\Component\Yaml\Yaml;

$value = Yaml::parse("foo: bar");
// $value = ['foo' => 'bar']

如果在解析过程中发生错误,解析器将抛出 ParseException 异常,指示错误类型以及原始 YAML 字符串中发生错误的行

1
2
3
4
5
6
7
use Symfony\Component\Yaml\Exception\ParseException;

try {
    $value = Yaml::parse('...');
} catch (ParseException $exception) {
    printf('Unable to parse the YAML string: %s', $exception->getMessage());
}

读取 YAML 文件

parseFile() 方法解析给定文件路径的 YAML 内容,并将其转换为 PHP 值

1
2
3
use Symfony\Component\Yaml\Yaml;

$value = Yaml::parseFile('/path/to/file.yaml');

如果在解析过程中发生错误,解析器将抛出 ParseException 异常。

写入 YAML 文件

dump() 方法将任何 PHP 数组转储为其 YAML 表示形式

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Yaml\Yaml;

$array = [
    'foo' => 'bar',
    'bar' => ['foo' => 'bar', 'bar' => 'baz'],
];

$yaml = Yaml::dump($array);

file_put_contents('/path/to/file.yaml', $yaml);

如果在转储过程中发生错误,解析器将抛出 DumpException 异常。

展开和内联数组

YAML 格式支持两种数组表示形式:展开的和内联的。 默认情况下,转储器使用展开的表示形式

1
2
3
4
foo: bar
bar:
    foo: bar
    bar: baz

dump() 方法的第二个参数自定义输出从展开表示形式切换到内联表示形式的级别

1
echo Yaml::dump($array, 1);
1
2
foo: bar
bar: { foo: bar, bar: baz }
1
echo Yaml::dump($array, 2);
1
2
3
4
foo: bar
bar:
    foo: bar
    bar: baz

缩进

默认情况下,YAML 组件将使用 4 个空格进行缩进。 这可以使用第三个参数进行更改,如下所示

1
2
// uses 8 spaces for indentation
echo Yaml::dump($array, 2, 8);
1
2
3
4
foo: bar
bar:
        foo: bar
        bar: baz

数字字面量

长的数字字面量,无论是整数、浮点数还是十六进制数,都以其在代码和配置文件中的可读性差而闻名。 这就是 YAML 文件允许添加下划线以提高其可读性的原因

1
2
3
4
5
parameters:
    credit_card_number: 1234_5678_9012_3456
    long_number: 10_000_000_000
    pi: 3.14159_26535_89793
    hex_words: 0x_CAFE_F00D

在解析 YAML 内容期间,所有 _ 字符都从数字字面量内容中删除,因此你可以包含的下划线数量或你分组内容的方式没有限制。

高级用法:标志

对象解析和转储

你可以使用 DUMP_OBJECT 标志转储对象

1
2
3
4
5
$object = new \stdClass();
$object->foo = 'bar';

$dumped = Yaml::dump($object, 2, 4, Yaml::DUMP_OBJECT);
// !php/object 'O:8:"stdClass":1:{s:5:"foo";s:7:"bar";}'

并使用 PARSE_OBJECT 标志解析它们

1
2
3
$parsed = Yaml::parse($dumped, Yaml::PARSE_OBJECT);
var_dump(is_object($parsed)); // true
echo $parsed->foo; // bar

YAML 组件使用 PHP 的 serialize() 方法来生成对象的字符串表示形式。

危险

对象序列化特定于此实现,其他 PHP YAML 解析器可能无法识别 php/object 标签,而非 PHP 实现肯定无法识别 - 请谨慎使用!

将对象解析和转储为映射

你可以使用 DUMP_OBJECT_AS_MAP 标志将对象转储为 Yaml 映射

1
2
3
4
5
$object = new \stdClass();
$object->foo = 'bar';

$dumped = Yaml::dump(['data' => $object], 2, 4, Yaml::DUMP_OBJECT_AS_MAP);
// $dumped = "data:\n    foo: bar"

并使用 PARSE_OBJECT_FOR_MAP 标志解析它们

1
2
3
4
$parsed = Yaml::parse($dumped, Yaml::PARSE_OBJECT_FOR_MAP);
var_dump(is_object($parsed)); // true
var_dump(is_object($parsed->data)); // true
echo $parsed->data->foo; // bar

YAML 组件使用 PHP 的 (array) 强制转换来生成对象作为映射的字符串表示形式。

处理无效类型

默认情况下,解析器会将无效类型编码为 null。 你可以使用 PARSE_EXCEPTION_ON_INVALID_TYPE 标志使解析器抛出异常

1
2
$yaml = '!php/object \'O:8:"stdClass":1:{s:5:"foo";s:7:"bar";}\'';
Yaml::parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); // throws an exception

类似地,在转储时你可以使用 DUMP_EXCEPTION_ON_INVALID_TYPE

1
2
$data = new \stdClass(); // by default objects are invalid.
Yaml::dump($data, 2, 4, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); // throws an exception

日期处理

默认情况下,YAML 解析器会将看起来像日期或日期时间的未加引号的字符串转换为 Unix 时间戳; 例如 2016-05-272016-05-27T02:59:43.1Z (ISO-8601)

1
Yaml::parse('2016-05-27'); // 1464307200

你可以使用 PARSE_DATETIME 标志使其转换为 DateTime 实例

1
2
$date = Yaml::parse('2016-05-27', Yaml::PARSE_DATETIME);
var_dump(get_class($date)); // DateTime

转储多行文字块

在 YAML 中,多行可以表示为文字块。 默认情况下,转储器会将多行编码为内联字符串

1
2
3
$string = ["string" => "Multiple\nLine\nString"];
$yaml = Yaml::dump($string);
echo $yaml; // string: "Multiple\nLine\nString"

你可以使用 DUMP_MULTI_LINE_LITERAL_BLOCK 标志使其使用文字块

1
2
3
4
5
6
7
$string = ["string" => "Multiple\nLine\nString"];
$yaml = Yaml::dump($string, 2, 4, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
echo $yaml;
//  string: |
//       Multiple
//       Line
//       String

解析 PHP 常量

默认情况下,YAML 解析器将内容中包含的 PHP 常量视为常规字符串。 使用 PARSE_CONSTANT 标志和特殊的 !php/const 语法将它们解析为正确的 PHP 常量

1
2
3
$yaml = '{ foo: PHP_INT_SIZE, bar: !php/const PHP_INT_SIZE }';
$parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT);
// $parameters = ['foo' => 'PHP_INT_SIZE', 'bar' => 8];

解析 PHP 枚举

YAML 解析器支持 PHP 枚举,包括单元枚举和支持枚举。 默认情况下,它们被解析为常规字符串。 使用 PARSE_CONSTANT 标志和特殊的 !php/enum 语法将它们解析为正确的 PHP 枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum FooEnum: string
{
    case Foo = 'foo';
    case Bar = 'bar';
}

// ...

$yaml = '{ foo: FooEnum::Foo, bar: !php/enum FooEnum::Foo }';
$parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT);
// the value of the 'foo' key is a string because it missed the `!php/enum` syntax
// $parameters = ['foo' => 'FooEnum::Foo', 'bar' => FooEnum::Foo];

$yaml = '{ foo: FooEnum::Foo, bar: !php/enum FooEnum::Foo->value }';
$parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT);
// the value of the 'foo' key is a string because it missed the `!php/enum` syntax
// $parameters = ['foo' => 'FooEnum::Foo', 'bar' => 'foo'];

你也可以使用 !php/enum,仅通过给出枚举 FQCN 来获取所有枚举案例

1
2
3
4
5
6
7
8
9
10
11
enum FooEnum: string
{
    case Foo = 'foo';
    case Bar = 'bar';
}

// ...

$yaml = '{ bar: !php/enum FooEnum }';
$parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT);
// $parameters = ['bar' => ['foo', 'bar']];

7.1

在 Symfony 7.1 中引入了对使用枚举 FQCN 而无需指定案例的支持。

二进制数据的解析和转储

非 UTF-8 编码的字符串被转储为 base64 编码的数据

1
2
3
4
$imageContents = file_get_contents(__DIR__.'/images/logo.png');

$dumped = Yaml::dump(['logo' => $imageContents]);
// logo: !!binary iVBORw0KGgoAAAANSUhEUgAAA6oAAADqCAY...

如果二进制数据包含 !!binary YAML 标签,则会自动解析

1
2
3
$dumped = 'logo: !!binary iVBORw0KGgoAAAANSUhEUgAAA6oAAADqCAY...';
$parsed = Yaml::parse($dumped);
$imageContents = $parsed['logo'];

自定义标签的解析和转储

除了对 !php/const!!binary 等标签的内置支持外,你还可以定义自己的自定义 YAML 标签,并使用 PARSE_CUSTOM_TAGS 标志解析它们

1
2
3
4
5
$data = "!my_tag { foo: bar }";
$parsed = Yaml::parse($data, Yaml::PARSE_CUSTOM_TAGS);
// $parsed = Symfony\Component\Yaml\Tag\TaggedValue('my_tag', ['foo' => 'bar']);
$tagName = $parsed->getTag();    // $tagName = 'my_tag'
$tagValue = $parsed->getValue(); // $tagValue = ['foo' => 'bar']

如果转储的内容包含 TaggedValue 对象,它们将自动转换为 YAML 标签

1
2
3
4
5
use Symfony\Component\Yaml\Tag\TaggedValue;

$data = new TaggedValue('my_tag', ['foo' => 'bar']);
$dumped = Yaml::dump($data);
// $dumped = '!my_tag { foo: bar }'

转储 Null 值

官方 YAML 规范同时使用 null~ 来表示 null 值。 默认情况下,此组件在转储 null 值时使用 null,但你可以使用 DUMP_NULL_AS_TILDE 标志将它们转储为 ~

1
2
3
4
5
$dumped = Yaml::dump(['foo' => null]);
// foo: null

$dumped = Yaml::dump(['foo' => null], 2, 4, Yaml::DUMP_NULL_AS_TILDE);
// foo: ~

将数字键转储为字符串

默认情况下,仅包含数字的数组键被转储为整数。 如果你想转储仅包含字符串的键,可以使用 DUMP_NUMERIC_KEY_AS_STRING 标志

1
2
3
4
5
$dumped = Yaml::dump([200 => 'foo']);
// 200: foo

$dumped = Yaml::dump([200 => 'foo'], 2, 4, Yaml::DUMP_NUMERIC_KEY_AS_STRING);
// '200': foo

语法验证

YAML 内容的语法可以通过 CLI 使用 LintCommand 命令进行验证。

首先,安装 Console 组件

1
$ composer require symfony/console

创建一个控制台应用程序,其中 lint:yaml 作为其唯一命令

1
2
3
4
5
6
7
8
9
// lint.php
use Symfony\Component\Console\Application;
use Symfony\Component\Yaml\Command\LintCommand;

(new Application('yaml/lint'))
    ->add(new LintCommand())
    ->getApplication()
    ->setDefaultCommand('lint:yaml', true)
    ->run();

然后,执行脚本以验证内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# validates a single file
$ php lint.php path/to/file.yaml

# or validates multiple files
$ php lint.php path/to/file1.yaml path/to/file2.yaml

# or all the files in a directory
$ php lint.php path/to/directory

# or all the files in multiple directories
$ php lint.php path/to/directory1 path/to/directory2

# or contents passed to STDIN
$ cat path/to/file.yaml | php lint.php

# you can also exclude one or more files from linting
$ php lint.php path/to/directory --exclude=path/to/directory/foo.yaml --exclude=path/to/directory/bar.yaml

结果默认写入 STDOUT 并使用纯文本格式。 添加 --format 选项以获取 JSON 格式的输出

1
$ php lint.php path/to/file.yaml --format=json

提示

lint 命令还将报告检查的 YAML 文件中的任何弃用。 例如,这可能有助于在自动化测试期间识别 YAML 文件内容的弃用。

这项工作,包括代码示例,均在 Creative Commons BY-SA 3.0 许可下获得许可。
目录
    版本