本文檔討論如何在 BSON 和 PHP 值之間轉換複合結構(例如文件、陣列和物件)。
如果一個陣列是*緊密陣列* — 也就是空陣列或鍵值從 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 文件。僅保留 公開 屬性,並忽略 保護 和 私有 屬性。
如果物件屬於實作了 MongoDB\BSON\Serializable 介面的類別,則呼叫 MongoDB\BSON\Serializable::bsonSerialize() 並使用返回的陣列或 stdClass 序列化為 BSON 文件或陣列。BSON 類型將由以下決定:
根文件必須序列化為 BSON 文件。
MongoDB\BSON\Persistable 物件必須序列化為 BSON 文件。
如果 MongoDB\BSON\Serializable::bsonSerialize() 返回一個緊密排列的陣列,則序列化為 BSON 陣列。
如果 MongoDB\BSON\Serializable::bsonSerialize() 返回一個非緊密排列的陣列或 stdClass,則序列化為 BSON 文件。
如果 MongoDB\BSON\Serializable::bsonSerialize() 未返回陣列或 stdClass,則拋出 MongoDB\Driver\Exception\UnexpectedValueException 例外。
如果物件屬於實作了 MongoDB\BSON\Persistable 介面(這意味著也實作了 MongoDB\BSON\Serializable)的類別,則以與前述段落類似的方式取得屬性,但同時還要新增一個額外的屬性 __pclass 作為二進位值,其子類型為 0x80,且資料包含正在被序列化的物件的完整類別名稱。
加到 MongoDB\BSON\Serializable::bsonSerialize() 所返回的陣列或物件的 __pclass 屬性,表示它會覆寫 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"}
/**
* Nesting Serializable classes
*/
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"} as a root class
["foo", "bar"] as a nested value
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 文件技術上可以包含重複的鍵,因為文件是以鍵值對列表的形式儲存的;然而,應用程式應避免產生具有重複鍵的文件,因為伺服器和驅動程式的行為可能未定義。由於 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 類型。可能的映射值如下:
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 欄位才能使此功能正常運作。
「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,則表示與該項的*預設*值相同。
這些範例使用以下類別
未實作任何介面的
實作了 MongoDB\BSON\Unserializable 介面
實作了 MongoDB\BSON\Persistable 介面
繼承自 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") }