从 BSON 反序列化

警告

从技术上讲,BSON 文档可以包含重复键,因为文档以键值对列表的形式存储;但是,应用程序应该避免生成包含重复键的文档,因为服务器和驱动程序的行为可能未定义。由于 PHP 对象和数组不能包含重复键,因此在解码包含重复键的 BSON 文档时,数据也可能丢失。

传统的 mongo 扩展将 BSON 文档和数组都反序列化为 PHP 数组。虽然 PHP 数组使用起来很方便,但这种行为存在问题,因为不同的 BSON 类型可能会反序列化为相同的 PHP 值(例如 {"0": "foo"}["foo"]),这使得无法推断原始的 BSON 类型。默认情况下,mongodb 扩展通过确保 BSON 数组和文档分别转换为 PHP 数组和对象来解决此问题。

对于复合类型,有三种数据类型

仅指顶层 BSON 文档

文档

仅指嵌入式 BSON 文档

数组

指 BSON 数组

除了这三种集合类型外,还可以配置文档中的特定字段以映射到下面提到的数据类型。例如,以下类型映射允许您将 "addresses" 数组中的每个嵌入式文档映射到 Address 类,以及这些嵌入式地址文档中的每个 "city" 字段映射到 City

[
    'fieldPaths' => [
        'addresses.$' => 'MyProject\Address',
        'addresses.$.city' => 'MyProject\City',
    ],
]

这三种数据类型以及字段特定映射可以映射到不同的 PHP 类型。可能的映射值是

未设置NULL(默认)

  • BSON 数组将被反序列化为 PHP array

  • 没有 __pclass 属性 [1] 的 BSON 文档(根或嵌入)将成为 PHP stdClass 对象,每个 BSON 文档键都设置为公共的 stdClass 属性。

  • 具有 __pclass 属性 [1] 的 BSON 文档(根或嵌入)将成为 PHP 对象,其类名由 __pclass 属性定义。

    如果命名类实现了 MongoDB\BSON\Persistable 接口,则 BSON 文档的属性(包括 __pclass 属性)将作为关联数组发送到 MongoDB\BSON\Unserializable::bsonUnserialize() 函数以初始化对象的属性。

    如果命名类不存在或没有实现 MongoDB\BSON\Persistable 接口,将使用 stdClass,并且每个 BSON 文档键(包括 __pclass)将设置为公共的 stdClass 属性。

    __pclass 功能依赖于该属性是检索到的 MongoDB 文档的一部分。如果您在查询文档时使用 投影,则需要在投影中包含 __pclass 字段才能使此功能正常工作。

"array"

将 BSON 数组或 BSON 文档转换为 PHP 数组。不会对 __pclass 属性 [1] 进行特殊处理,但如果它存在于 BSON 文档中,它可能会作为返回数组中的一个元素设置。

"object""stdClass"

将 BSON 数组或 BSON 文档转换为 stdClass 对象。不会对 __pclass 属性 [1] 进行特殊处理,但如果它存在于 BSON 文档中,它可能会作为返回对象中的一个公共属性设置。

"bson"

将 BSON 数组转换为 MongoDB\BSON\PackedArray,将 BSON 文档转换为 MongoDB\BSON\Document,无论 BSON 文档是否具有 __pclass 属性 [1]

注意: bson 值仅适用于三种根类型,不适用于字段特定映射。

任何其他字符串

定义 BSON 数组或 BSON 对象应反序列化的类名。对于包含 __pclass 属性的 BSON 对象,该类将优先考虑。

如果命名类不存在、不是具体的(即它是抽象的或是一个接口)、或者没有实现 MongoDB\BSON\Unserializable,则会抛出 MongoDB\Driver\Exception\InvalidArgumentException 异常。

如果 BSON 对象具有 __pclass 属性,并且该类存在并实现了 MongoDB\BSON\Persistable,它将覆盖类型映射中提供的类。

BSON 文档的属性(包括如果存在则包括 __pclass 属性)将作为关联数组发送到 MongoDB\BSON\Unserializable::bsonUnserialize() 函数以初始化对象的属性。

类型映射

类型映射可以通过 MongoDB\Driver\Cursor 对象上的 MongoDB\Driver\Cursor::setTypeMap() 方法,或 MongoDB\BSON\toPHP()MongoDB\BSON\Document::toPHP()MongoDB\BSON\PackedArray::toPHP()$typeMap 参数设置。除了字段特定类型外,还可以单独设置三种类(文档数组)。

如果映射中的值为 NULL,则表示与该项目的默认值相同。

示例

这些示例使用以下类

MyClass

没有实现任何接口

YourClass

它实现了 MongoDB\BSON\Unserializable

OurClass

它实现了 MongoDB\BSON\Persistable

TheirClass

它扩展了 OurClass

YourClass、OurClass 和 TheirClass 的 MongoDB\BSON\Unserializable::bsonUnserialize() 方法遍历数组并设置属性,而不会进行修改。它$unserialized 属性设置为 true

<?php

function bsonUnserialize( array $map )
{
foreach (
$map as $k => $value )
{
$this->$k = $value;
}
$this->unserialized = true;
}

/* typemap: [] (all defaults) */
{ "foo": "yes", "bar" : false }
  -> stdClass { $foo => 'yes', $bar => false }

{ "foo": "no", "array" : [ 5, 6 ] }
  -> stdClass { $foo => 'no', $array => [ 5, 6 ] }

{ "foo": "no", "obj" : { "embedded" : 3.14 } }
  -> stdClass { $foo => 'no', $obj => stdClass { $embedded => 3.14 } }

{ "foo": "yes", "__pclass": "MyClass" }
  -> stdClass { $foo => 'yes', $__pclass => 'MyClass' }

{ "foo": "yes", "__pclass": { "$type" : "80", "$binary" : "MyClass" } }
  -> stdClass { $foo => 'yes', $__pclass => Binary(0x80, 'MyClass') }

{ "foo": "yes", "__pclass": { "$type" : "80", "$binary" : "YourClass") }
  -> stdClass { $foo => 'yes', $__pclass => Binary(0x80, 'YourClass') }

{ "foo": "yes", "__pclass": { "$type" : "80", "$binary" : "OurClass") }
  -> OurClass { $foo => 'yes', $__pclass => Binary(0x80, 'OurClass'), $unserialized => true }

{ "foo": "yes", "__pclass": { "$type" : "44", "$binary" : "YourClass") }
  -> stdClass { $foo => 'yes', $__pclass => Binary(0x44, 'YourClass') }

/* typemap: [ "root" => "MissingClass" ] */
{ "foo": "yes" }
  -> MongoDB\Driver\Exception\InvalidArgumentException("MissingClass does not exist")

/* typemap: [ "root" => "MyClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MyClass" } }
  -> MongoDB\Driver\Exception\InvalidArgumentException("MyClass does not implement Unserializable interface")

/* typemap: [ "root" => "MongoDB\BSON\Unserializable" ] */
{ "foo": "yes" }
  -> MongoDB\Driver\Exception\InvalidArgumentException("Unserializable is not a concrete class")

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MongoDB\BSON\Unserializable" } }
  -> YourClass { $foo => "yes", $__pclass => Binary(0x80, "MongoDB\BSON\Unserializable"), $unserialized => true }

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MyClass" } }
  -> YourClass { $foo => "yes", $__pclass => Binary(0x80, "MyClass"), $unserialized => true }

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "OurClass" } }
  -> OurClass { $foo => "yes", $__pclass => Binary(0x80, "OurClass"), $unserialized => true }

/* typemap: [ "root" => "YourClass" ] */
{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "TheirClass" } }
  -> TheirClass { $foo => "yes", $__pclass => Binary(0x80, "TheirClass"), $unserialized => true }

/* typemap: [ "root" => "OurClass" ] */
{ foo: "yes", "__pclass" : { "$type": "80", "$binary": "TheirClass" } }
  -> TheirClass { $foo => "yes", $__pclass => Binary(0x80, "TheirClass"), $unserialized => true }

/* typemap: [ 'root' => 'YourClass' ] */
{ foo: "yes", "__pclass" : { "$type": "80", "$binary": "YourClass" } }
  -> YourClass { $foo => 'yes', $__pclass => Binary(0x80, 'YourClass'), $unserialized => true }

/* typemap: [ 'root' => 'array', 'document' => 'array' ] */
{ "foo": "yes", "bar" : false }
  -> [ "foo" => "yes", "bar" => false ]

{ "foo": "no", "array" : [ 5, 6 ] }
  -> [ "foo" => "no", "array" => [ 5, 6 ] ]

{ "foo": "no", "obj" : { "embedded" : 3.14 } }
  -> [ "foo" => "no", "obj" => [ "embedded => 3.14 ] ]

{ "foo": "yes", "__pclass": "MyClass" }
  -> [ "foo" => "yes", "__pclass" => "MyClass" ]

{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "MyClass" } }
  -> [ "foo" => "yes", "__pclass" => Binary(0x80, "MyClass") ]

{ "foo": "yes", "__pclass" : { "$type": "80", "$binary": "OurClass" } }
  -> [ "foo" => "yes", "__pclass" => Binary(0x80, "OurClass") ]

/* typemap: [ 'root' => 'object', 'document' => 'object' ] */
{ "foo": "yes", "__pclass": { "$type": "80", "$binary": "MyClass" } }
  -> stdClass { $foo => "yes", "__pclass" => Binary(0x80, "MyClass") }

添加笔记

用户贡献笔记 3 条笔记

Miguel
6 年前
确保在投影中包含字段 "__pclass",如果您希望 ODM 自动调用类的 bsonUnserialize。

如果您在查询中没有获取该字段,ODM 将无法知道要调用哪个类,因此您必须使用“typemap”变量指定它。
hello at stefandjokic dot com
4 年前
对之前评论的小修正

使用“fieldPaths”来指定嵌套属性的类型。

例如。

'typeMap' => [
'array' => 'array',
'fieldPaths' => [
'notifications.email.to' => 'array',
'notifications.data.params' => 'array',
],
],
hello at stefandjokic dot com
4 年前
您还可以为嵌套的文档项目指定 typemap,例如

<?php
.
.
.
// 默认情况下,其他所有内容都是对象
'typeMap' => [
'customer.phones' => 'array',
],
To Top