Statement on glibc/iconv Vulnerability

Deserialización desde BSON

Advertencia

BSON documents can technically contain duplicate keys because documents are stored as a list of key-value pairs; however, applications should refrain from generating documents with duplicate keys as server and driver behavior may be undefined. Since PHP objects and arrays cannot have duplicate keys, data could also be lost when decoding a BSON document with duplicate keys.

The legacy mongo extension deserialized both BSON documents and arrays as PHP arrays. While PHP arrays are convenient to work with, this behavior was problematic because different BSON types could deserialize to the same PHP value (e.g. {"0": "foo"} and ["foo"]) and make it impossible to infer the original BSON type. By default, the current driver addresses this concern by ensuring that BSON arrays and documents are converted to PHP arrays and objects, respectively.

Para tipos compuestos, existen tres tipos de datos:

root

se refiere al documento BSON de más alto nivel solamente

document

se refiere a docuemtnos BSON embebidos solamente

array

se refiere a un array BSON

Besides the three collective types, it is also possible to configure specific fields in your document to map to the data types mentioned below. As an example, the following type map allows you to map each embedded document within an "addresses" array to an Address class and each "city" field within those embedded address documents to a City class:

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

Cada uno de estos tres tipos de datos se pueden hacer corresponder con diferentes tipos de PHP. Los posibles valores de correspondencia son:

no establecido o NULL (es el predeterminado)

  • Un array BSON será deserializado como un array de PHP.

  • Un documento BSON (raíz o embebido) sin una propiedad __pclass [1] se convierte en un objeto stdClass de PHP, con cada clave de documento BSON establecida a propiedad stdClass pública.

  • Un documento BSON (raíz o embebido) con una propiedad __pclass [1] se convierte en un objeto de PHP del nombre de la clase definida por la propiedad __pclass.

    Si la clase con nombre implementa la intefaz MongoDB\BSON\Persistable, las propiedades del documento BSON, incluyendo la propiedad __pclass, son enviadas como un array asociativo a la función bsonUnserialize() para inicializar la propiedades del objeto.

    Si la clase con nombre no existe o no implementa la interfaz MongoDB\BSON\Persistable, se empleará stdClass y cada clave del documento BSON (incluyendo __pclass) será establecida a una propiedad stdClass pública.

"array"

Convierte un array BSON o documento BSON en un array de PHP. No habrá ningún tratamiento especial de una propiedad __pclass [1], aunque se podría establecer como un elemento del array devuelto si estaba presente el el documento BSON.

"object" o "stdClass"

Convierte un array BSON o documento BSON a un objeto stdClass. No habrá ningún tratamiento especial de una propiedad __pclass [1], aunque se podría establecer como propiedad pública del objeto devuelto si estaba presente en el documento BSON.

cualquier otro string

Define el nombre de la clase a la que debe ser deserializado el array BSON u objeto BSON. Para objetos BSON que incluyan propiedades __pclass, esta clase tomará prioridad.

Si la clase citada no existe, no es concreta (esto es, es abstracta o es una interfaz), o no implementa MongoDB\BSON\Unserializable, se lanzará una excepción MongoDB\Driver\Exception\InvalidArgumentException.

Si el objeto BSON posee una propiedad __pclass y esa clase existe e implementa MongoDB\BSON\Persistable, sustituirá a la clase proporcionada en el mapa de tipo.

Las propiedades del documento BSON, incluyendo la propiedad __pclass si existiera, será enviada como un array asociativo a la función bsonUnserialize() para inicializar las propiedades del objeto.

TypeMaps

TypeMaps se puede establecer a través del método MongoDB\Driver\Cursor::setTypeMap() en un objeto MongoDB\Driver\Cursor o del argumento $typeMap de MongoDB\BSON\toPHP(). Cada una de las tres clases (root, document and array) se pueden establecer individualmente.

Si el valor del mapa es NULL, significa lo mismo que el valor predeterminado para ese elemento.

Ejemplos

Estos ejemplos utilizan las siguientes clases:

MiClase

la cual no implementa ninguna interfaz

TuClase

que implementa MongoDB\BSON\Unserializable

NuestraClase

que implementa MongoDB\BSON\Persistable

SuClase

que extiende NuestraClase

El método MongoDB\BSON\Unserializable::bsonUnserialize() de TuClase, NuestraClase, SuClase recorre el array y establece las propiedades sin modificaciones. También establece la propiedad $unserialized a 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") }

add a note

User Contributed Notes 3 notes

up
2
Miguel
6 years 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.
up
1
hello at stefandjokic dot com
3 years ago
Minor correction to my previous comment:

Use 'fieldPaths' for specifying type of nested property.

Eg.

'typeMap' => [
'array' => 'array',
'fieldPaths' => [
'notifications.email.to' => 'array',
'notifications.data.params' => 'array',
],
],
up
0
hello at stefandjokic dot com
3 years ago
You can also specify the type map for nested document item, e.g.

<?php
.
.
.
// everything else is by default an object
'typeMap' => [
'customer.phones' => 'array',
],
To Top