SunshinePHP 2020 CFP Started

Десериализация из BSON

Устаревшее расширение mongo десериализовало, как документы, так и массивы BSON в качестве массивов PHP. Хотя с PHP массивами удобно работать, такое поведение было проблематичным, поскольку различные типы BSON могут десериализоваться до одного и того же значения PHP (например, {"0": "foo"} и ["foo"]) и будет невозможно вывести оригинальный тип BSON. По умолчанию текущий драйвер решает эту проблему, обеспечивая преобразование массивов и документов BSON в массивы и объекты PHP соответственно.

Для составных типов существует три типа данных:

root

относится только к документу верхнего уровня BSON

document

относится только к встроенным документам BSON

array

относится к массивам BSON

Помимо трех групповых типов, также можно настроить определенные поля в документе для сопоставления с типами данных, указанными ниже. В качестве примера, следующая карта типов позволяет сопоставить каждый встроенный документ в массиве "addresses" с классом Address, а каждое поле "city" в этих документах с встроенным адресом с классом City:

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

Каждый из этих трех типов данных, а также сопоставления для конкретных полей могут быть сопоставлены с различными типами PHP. Возможные значения сопоставления:

не указано или NULL (по умолчанию)

  • Массив BSON будет десериализован, как PHP array.

  • Документ BSON (корневой или внедренный) без свойства __pclass [1] становится объектом PHP stdClass, причем каждый ключ документа BSON устанавливается в качестве открытого свойства stdClass.

  • Документ BSON (корневой или встроенный) со свойством __pclass [1] становится объектом PHP имени класса, как это определено свойством __pclass.

    Если указанный класс реализует интерфейс MMongoDB\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. Для объектов BSON, которые содержат свойства __pclass, этот класс будет иметь приоритет.

Если названный класс не существует, не является конкретным (то есть является абстрактным или интерфейсом) или не реализует MongoDB\BSON\Unserializable, то выдается исключение MongoDB\Driver\Exception\InvalidArgumentException.

Если объект BSON имеет свойство __pclass, и этот класс существует и реализует MongoDB\BSON\Persistable, он заменит класс, представленный в карте типов.

Свойства документа BSON, включая свойство __pclass, если оно существует, будут отправлены в виде ассоциативного массива в функцию MongoDB\BSON\Unserializable::bsonUnserialize() для инициализации свойств объекта.

TypeMaps

TypeMaps можно установить с помощью метода MongoDB\Driver\Cursor::setTypeMap() для объекта MongoDB\Driver\Cursor или аргумента $typeMap в MongoDB\BSON\toPHP(). Каждый из трех классов (root, document, и array) может быть задан индивидуально, в дополнение к типам полей.

Если значение на карте равно NULL, это означает то же самое, что и значение по умолчанию для этого элемента.

Примеры

В этих примерах используются следующие классы:

MyClass

который не реализует интерфейс

YourClass

который реализует MongoDB\BSON\Unserializable

OurClass

который реализует MongoDB\BSON\Persistable

TheirClass

который расширяет OurClass

Метод MongoDB\BSON\Unserializable::bsonUnserialize() класса YourClass, OurClass, OurClass выполняет итерацию по массиву и устанавливает свойства без изменений. Он также устанавливает для свойства $unserialized значение true:

<?php

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

/* typemap: [] (все значения по умолчанию) */
{ "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") }

add a note add a note

User Contributed Notes 1 note

up
1
Miguel
1 year ago
Make sure you include the field "__pclass" to the projection if you want the ODM to automatically call the bsonUnserialize of the class.

If you don't get that field in the query, the ODM will never know which class to call, so you'll have to specify it with the "typemap" variable.
To Top