Este documento explica cómo se convierten las estructuras compuestas (es decir, los documentos, los arrays y los objetos) entre los valores BSON y PHP.
Si un array es un array compacto — es decir, un array vacío o las claves comienzan en 0 y son secuenciales sin huecos : array BSON.
Si el array no es compacto — es decir, que tiene claves asociativas (cadenas), que las claves no comienzan en 0, o que hay huecos : objeto BSON.
Un documento de nivel superior (raíz), siempre serializado como documento BSON.
Estos ejemplos se serializan como array BSON :
[ 8, 5, 2, 3 ] => [ 8, 5, 2, 3 ] [ 0 => 4, 1 => 9 ] => [ 4, 9 ]
Estos ejemplos se serializan como objeto 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 }
Es de notar que los cinco ejemplos son extractos de un documento completo, y solo representan un valor dentro de un documento.
Si un objeto es de la clase stdClass, serializar como documento BSON.
Si un objeto es una clase soportada que implementa MongoDB\BSON\Type, entonces utilizar la lógica de serialización BSON para este tipo específico. Las instancias de MongoDB\BSON\Type (a excepción de MongoDB\BSON\Serializable) solo pueden ser serializadas como valor de campo de documento. Intentar serializar tal objeto como documento raíz lanzará una MongoDB\Driver\Exception\UnexpectedValueException.
Si un objeto es de una clase desconocida que implementa la interfaz MongoDB\BSON\Type, entonces se lanza una MongoDB\Driver\Exception\UnexpectedValueException.
Si un objeto es de otra clase, sin implementar una interfaz especial, serializar como documento BSON. Mantener solo las propiedades públicas, e ignorar las propiedades protegidas y privadas.
Si un objeto es de una clase que implementa MongoDB\BSON\Serializable, llamar MongoDB\BSON\Serializable::bsonSerialize() y utilizar el array o stdClass devuelto para serializar como documento BSON o array. El tipo BSON será determinado por las siguientes reglas :
Si un objeto es de una clase que implementa la interfaz
MongoDB\BSON\Persistable (lo que implica
MongoDB\BSON\Serializable), obtener las
propiedades de manera similar a los párrafos anteriores, pero
también añadir una propiedad
__pclass como valor binario, con un subtipo
0x80 y datos que llevan el nombre de la clase
completamente calificado del objeto que se serializa.
La propiedad __pclass se añade al array o al objeto devuelto por MongoDB\BSON\Serializable::bsonSerialize(), lo que significa que sobrescribirá cualquier clave/propiedad __pclass en el valor de retorno de MongoDB\BSON\Serializable::bsonSerialize(). Si se desea evitar este comportamiento y definir su propio valor __pclass, no se debe implementar MongoDB\BSON\Persistable y se debería implementar MongoDB\BSON\Serializable directamente.
<?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"} como clase raíz
// ["foo", "bar"] como valor anidado
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=="}}
?>Los documentos BSON pueden contener técnicamente claves duplicadas ya que los documentos se almacenan como una lista de pares clave-valor; sin embargo, las aplicaciones deben abstenerse de generar documentos con claves duplicadas ya que el comportamiento del servidor y del controlador puede ser indefinido. Dado que los objetos y arrays de PHP no pueden tener claves duplicadas, los datos también podrían perderse al decodificar un documento BSON con claves duplicadas.
La extensión mongodb deserializa los documentos BSON
y los arrays BSON como arrays PHP. Aunque los arrays PHP son
prácticos de usar, este comportamiento era problemático ya que diferentes
tipos BSON podían ser deserializados en el mismo valor PHP (por ejemplo
{"0": "foo"} y ["foo"]) y hacía
imposible inferir el tipo BSON original. Por defecto, la extensión
mongodb aborda esta preocupación asegurándose de que los
arrays BSON y los documentos BSON se conviertan en arrays y objetos PHP,
respectivamente.
Para los tipos compuestos, existen tres tipos de datos :
Además de los tres tipos colectivos, también es posible configurar campos específicos en su documento para mapear los tipos de datos
mencionados a continuación. Por ejemplo, el siguiente tipo de mapa le permite mapear cada documento incrustado en un array "addresses" a una clase Address y cada
campo "city" en estos documentos de dirección incrustados a una
clase City:
[
'fieldPaths' => [
'addresses.$' => 'MyProject\Address',
'addresses.$.city' => 'MyProject\City',
],
]
Cada uno de estos tres tipos de datos, así como los mapeos específicos de los campos, pueden ser mapeados contra diferentes tipos PHP. Los valores de mapeo posibles son:
Una propiedad __pclass solo se considera existente si una propiedad con ese nombre existe, y es un valor binario, y el subtipo del valor binario es 0x80. Si alguna de estas tres condiciones no se cumple, la propiedad __pclass no existe y debe ser tratada como cualquier otra propiedad normal.
se convierte en un objeto stdClass, con cada clave de documento BSON definida como una propiedad de stdClass pública."array""object" o "stdClass""bson"Nota: El valor
bsonsolo está disponible para los tres tipos raíz, y no en los mapeos específicos de los campos.
Los TypeMaps pueden ser definidos a través del método
MongoDB\Driver\Cursor::setTypeMap() en un objeto
MongoDB\Driver\Cursor, o el argumento
$typeMap de
MongoDB\BSON\toPHP(),
MongoDB\BSON\Document::toPHP(), y
MongoDB\BSON\PackedArray::toPHP(). Cada una de las tres
clases (raíz, documento, y
array) puede ser definida individualmente, además de los
tipos específicos de los campos.
Si el valor en el TypeMap es NULL, esto significa lo mismo que el valor por defecto para este elemento.
Estos ejemplos utilizan las siguientes clases:
El método MongoDB\BSON\Unserializable::bsonUnserialize()
de YourClass, OurClass, TheirClass itera sobre el array y define las
propiedades sin modificaciones. También añade la
propiedad $unserialized a true:
<?php
function bsonUnserialize( array $map )
{
foreach ( $map as $k => $value )
{
$this->$k = $value;
}
$this->unserialized = true;
}
/* typemap: [] (todos los valores por defecto) */
{ "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") }