PHP 日本会议 2024

PHP 变量在 MongoDB 中的序列化和反序列化

本文档讨论了复合结构(即文档、数组和对象)如何在 BSON 和 PHP 值之间转换。

序列化到 BSON

数组

如果数组是 *打包数组* — 即空数组或键从 0 开始且顺序连续没有间隙:*BSON 数组*。

如果数组不是打包的 — 即具有关联(字符串)键,键不是从 0 开始,或者存在间隙:: *BSON 对象*

顶级(根)文档*始终*序列化为 BSON 文档。

示例

这些序列化为 BSON 数组

[ 8, 5, 2, 3 ] => [ 8, 5, 2, 3 ]
[ 0 => 4, 1 => 9 ] => [ 4, 9 ]

这些序列化为 BSON 文档

[ 0 => 1, 2 => 8, 3 => 12 ] => { "0" : 1, "2" : 8, "3" : 12 }
[ "foo" => 42 ] => { "foo" : 42 }
[ 1 => 9, 0 => 10 ] => { "1" : 9, "0" : 10 }

请注意,这五个示例是完整文档的*摘录*,并且仅表示文档内的*一个*值。

对象

如果对象属于 stdClass 类,则序列化为 *BSON 文档*。

如果对象是支持的类并实现了 MongoDB\BSON\Type,则使用该特定类型的 BSON 序列化逻辑。MongoDB\BSON\Type 实例(不包括 MongoDB\BSON\Serializable)只能作为文档字段值序列化。尝试将此类对象作为根文档序列化将抛出 MongoDB\Driver\Exception\UnexpectedValueException

如果对象属于未知类并实现了 MongoDB\BSON\Type 接口,则抛出 MongoDB\Driver\Exception\UnexpectedValueException

如果对象属于任何其他类,并且没有实现任何特殊接口,则将其序列化为 *BSON 文档*。仅保留 *public* 属性,忽略 *protected* 和 *private* 属性。

如果对象属于实现了 MongoDB\BSON\Serializable 接口的类,则调用 MongoDB\BSON\Serializable::bsonSerialize() 并使用返回的数组或 stdClass 将其序列化为 BSON 文档或数组。BSON 类型将由以下内容确定

  1. 根文档必须序列化为 BSON 文档。

  2. MongoDB\BSON\Persistable 对象必须序列化为 BSON 文档。

  3. 如果 MongoDB\BSON\Serializable::bsonSerialize() 返回一个打包数组,则序列化为 BSON 数组。

  4. 如果 MongoDB\BSON\Serializable::bsonSerialize() 返回一个非打包数组或 stdClass,则序列化为 BSON 文档。

  5. 如果 MongoDB\BSON\Serializable::bsonSerialize() 没有返回数组或 stdClass,则抛出 MongoDB\Driver\Exception\UnexpectedValueException 异常。

如果对象属于实现了 MongoDB\BSON\Persistable 接口的类(这意味着 MongoDB\BSON\Serializable),则以与前面段落类似的方式获取属性,但*还*添加一个附加属性 __pclass 作为二进制值,子类型为 0x80,数据包含正在序列化的对象的完全限定类名。

__pclass 属性添加到 MongoDB\BSON\Serializable::bsonSerialize() 返回的数组或对象中,这意味着它将覆盖 MongoDB\BSON\Serializable::bsonSerialize() 返回值中的任何 __pclass 键/属性。如果您想避免这种行为并设置您自己的 __pclass 值,则*不能*实现 MongoDB\BSON\Persistable,而应直接实现 MongoDB\BSON\Serializable

示例

<?php

class stdClass
{
public
$foo = 42;
}
// => {"foo": 42}

class MyClass
{
public
$foo = 42;
protected
$prot = 'wine';
private
$fpr = 'cheese';
}
// => {"foo": 42}

class AnotherClass1 implements MongoDB\BSON\Serializable
{
public
$foo = 42;
protected
$prot = 'wine';
private
$fpr = 'cheese';

public function
bsonSerialize(): array
{
return [
'foo' => $this->foo, 'prot' => $this->prot];
}
}
// => {"foo": 42, "prot": "wine"}

class AnotherClass2 implements MongoDB\BSON\Serializable
{
public
$foo = 42;

public function
bsonSerialize(): self
{
return
$this;
}
}
// => MongoDB\Driver\Exception\UnexpectedValueException("bsonSerialize() did not return an array or stdClass")

class AnotherClass3 implements MongoDB\BSON\Serializable
{
private
$elements = ['foo', 'bar'];

public function
bsonSerialize(): array
{
return
$this->elements;
}
}
// => {"0": "foo", "1": "bar"}

/**
* 嵌套可序列化类
*/

class AnotherClass4 implements MongoDB\BSON\Serializable
{
private
$elements = [0 => 'foo', 2 => 'bar'];

public function
bsonSerialize(): array
{
return
$this->elements;
}
}
// => {"0": "foo", "2": "bar"}

class ContainerClass1 implements MongoDB\BSON\Serializable
{
public
$things;

public function
__construct()
{
$this->things = new AnotherClass4();
}

function
bsonSerialize(): array
{
return [
'things' => $this->things];
}
}
// => {"things": {"0": "foo", "2": "bar"}}


class AnotherClass5 implements MongoDB\BSON\Serializable
{
private
$elements = [0 => 'foo', 2 => 'bar'];

public function
bsonSerialize(): array
{
return
array_values($this->elements);
}
}
// => {"0": "foo", "1": "bar"} 作为根类
["foo", "bar"] 作为嵌套值

class ContainerClass2 implements MongoDB\BSON\Serializable
{
public
$things;

public function
__construct()
{
$this->things = new AnotherClass5();
}

public function
bsonSerialize(): array
{
return [
'things' => $this->things];
}
}
// => {"things": ["foo", "bar"]}


class AnotherClass6 implements MongoDB\BSON\Serializable
{
private
$elements = ['foo', 'bar'];

function
bsonSerialize(): object
{
return (object)
$this->elements;
}
}
// => {"0": "foo", "1": "bar"}

class ContainerClass3 implements MongoDB\BSON\Serializable
{
public
$things;

public function
__construct()
{
$this->things = new AnotherClass6();
}

public function
bsonSerialize(): array
{
return [
'things' => $this->things];
}
}
// => {"things": {"0": "foo", "1": "bar"}}

class UpperClass implements MongoDB\BSON\Persistable
{
public
$foo = 42;
protected
$prot = 'wine';
private
$fpr = 'cheese';

private
$data;

public function
bsonUnserialize(array $data): void
{
$this->data = $data;
}

public function
bsonSerialize(): array
{
return [
'foo' => $this->foo, 'prot' => $this->prot];
}
}
// => {"foo": 42, "prot": "wine", "__pclass": {"$type": "80", "$binary": "VXBwZXJDbGFzcw=="}}

?>

从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 数组

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

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

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

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

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

"数组"

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

"对象""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") }

添加注释

用户贡献的注释

此页面没有用户贡献的注释。
To Top