## Sérialisation and déserialisation des variables PHP dans MongoDB

Ce document explique comment les structures composées (c'est-à-dire les documents, les tableaux et les objets) sont converties entre les valeurs BSON et PHP.

## Sérialisation en BSON

## Tableaux

Si un tableau est un tableau compact — c'est-à-dire un tableau vide ou les clés commencent à 0 et sont séquentielles sans trous : tableau BSON.

Si le tableau n'est pas compact — c'est-à-dire qu'il a des clés associatives (chaînes), que les clés ne commencent pas à 0, ou qu'il y a des trous : objet BSON.

Un document de niveau supérieur (racine), toujours sérialisé en tant que document BSON.

## Exemples

Ces exemples sérialisent en tant que tableau BSON :

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

Ces exemples sérialisent en tant qu'objet 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 }

Il est à noter que les cinq exemples sont des extraits d'un document complet, et ne représentent qu'une valeur à l'intérieur d'un document.

## Objets

Si un objet est de la classe stdClass, sérialiser en tant que document BSON.

Si un objet est une classe supportée qui implémente MongoDB\BSON\Type, alors utiliser la logique de sérialisation BSON pour ce type spécifique. Les instances de MongoDB\BSON\Type (à l'exclusion de MongoDB\BSON\Serializable) ne peuvent être sérialisées que comme valeur de champ de document. Tenter de sérialiser un tel objet en tant que document racine lèvera une MongoDB\Driver\Exception\UnexpectedValueException.

Si un objet est d'une classe inconnue implémentant l'interface MongoDB\BSON\Type, alors une MongoDB\Driver\Exception\UnexpectedValueException est lancée.

Si un objet est d'une classe autre, sans implémenter une interface spéciale, sérialiser en tant que document BSON. Garder seulement les propriétés public, et ignorer les propriétés protected et private.

Si un objet est d'une classe qui implémente MongoDB\BSON\Serializable, appeler MongoDB\BSON\Serializable::bsonSerialize() et utiliser le tableau ou stdClass retourné pour sérialiser en tant que document BSON ou tableau. Le type BSON sera déterminé par les règles suivantes :

1. Les documents racines doivent être sérialisés en tant que document BSON.

2. Les objets MongoDB\BSON\Persistable doivent être sérialisés en tant que document BSON.

3. Si MongoDB\BSON\Serializable::bsonSerialize() retourne un tableau compact, sérialiser en tant que tableau BSON.

4. Si MongoDB\BSON\Serializable::bsonSerialize() retourne un tableau non-compact ou stdClass, sérialiser en tant qu'objet BSON.

5. Si MongoDB\BSON\Serializable::bsonSerialize() ne retourne pas un tableau ou stdClass, lance une exception MongoDB\Driver\Exception\UnexpectedValueException.

Si un objet est d'une classe qui implémente l'interface MongoDB\BSON\Persistable (qui implique MongoDB\BSON\Serializable), obtenir les propriétés de manière similaire aux paragraphes précédents, mais aussi ajouter une propriété __pclass en tant que valeur binaire, avec un sous-type `0x80` et des données portant le nom de la classe entièrement qualifié de l'objet qui est sérialisé.

La propriété __pclass est ajoutée au tableau ou à l'objet retourné par MongoDB\BSON\Serializable::bsonSerialize(), ce qui signifie qu'elle écrasera toute clé/propriété __pclass dans la valeur de retour de MongoDB\BSON\Serializable::bsonSerialize(). Si vous voulez éviter ce comportement et définir votre propre valeur __pclass, vous ne devez pas implémenter MongoDB\BSON\Persistable et devriez plutôt implémenter MongoDB\BSON\Serializable directement.

## Exemples

`<?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"} en tant que classe racine
// ["foo", "bar"] en tant que valeur imbriquée
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=="}}
?>`

## Deserialization from BSON

Avertissement

Les documents BSON peuvent techniquement contenir des clés dupliquées car les documents sont stockés en tant qu'une liste de paire clé-valeur ; cependant, les applications devrait s'abstenir de générer des documents avec des clés dupliquées car le comportement du serveur et du pilote peut être indéfinie. Puisque les objets et tableaux PHP ne peuvent pas avoir de clés dupliquées, les données pourraient aussi être perdu lors du décodage d'un document BSON avec des clés dupliquées.

L'extension `mongodb` désérialise les documents BSON et les tableaux BSON en tant que tableaux PHP. Tant que les tableaux PHP sont pratiques à utiliser, ce comportement était problématique car différents types BSON pouvaient être désérialisés en la même valeur PHP (par exemple `{"0": "foo"}` et `["foo"]`) et rendait impossible d'inférer le type BSON original. Par défaut, l'extension `mongodb` adresse cette préoccupation en s'assurant que les tableaux BSON et les documents BSON sont convertis en tableaux et objets PHP, respectivement.

Pour les types composés, il existe trois types de données :

root

  réfère à un document BSON de niveau supérieur seulement

document

  réfère à des documents BSON imbriqués seulement

array

  réfère à un tableau BSON

A part les trois types collectifs, il est aussi possible de configurer des champs spécifiques dans votre document pour mapper les types de données mentionnés ci-dessous. Par exemple, le type de carte suivant vous permet de mapper chaque document intégré dans un tableau `"addresses"` à une classe Address et chaque champ `"city"` dans ces documents d'adresse intégrés à une classe City:

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

Chacun de ces trois types de données, ainsi que les mappages spécifiques aux champs, peuvent être mappés contre différents types PHP. Les valeurs de mappage possibles sont:

non défini ou NULL (par défaut)

  - Un tableau BSON sera désérialisé en un tableau PHP.

  - Un document BSON (racine ou imbriqué) sans propriété __pclass [1] devient un objet stdClass, avec chaque clé de document BSON définie comme une propriété de stdClass publique.

  - Un document BSON (racine ou imbriqué) avec une propriété __pclass devient un objet PHP de la classe nommée par la propriété __pclass.

    Si la classe nommée implémente l'interface MongoDB\BSON\Persistable, alors les propriétés du document BSON, y compris la propriété __pclass, sont envoyées sous forme de tableau associatif à la fonction MongoDB\BSON\Unserializable::bsonUnserialize() pour initialiser les propriétés de l'objet.

    Si la classe nommée n'existe pas ou n'implémente pas l'interface MongoDB\BSON\Persistable, stdClass sera utilisé et chaque clé de document BSON (y compris __pclass) sera définie comme une propriété publique de stdClass.

    La fonctionnalité __pclass repose sur le fait que la propriété soit partie d'un document MongoDB récupéré. Si vous utilisez une » projection lors de la recherche de documents, vous devez inclure le champ __pclass dans la projection pour que cette fonctionnalité fonctionne.

`"array"`

  Transforme un tableau BSON en un tableau PHP. Il n'y aura pas de traitement spécial d'une propriété __pclass [1] mais elle peut être définie comme un élément dans le tableau retourné si elle était présente dans le document BSON.

`"object"` ou `"stdClass"`

  Transforme un tableau BSON ou un document BSON en un objet stdClass. Il n'y aura pas de traitement spécial d'une propriété __pclass [1] mais elle peut être définie comme une propriété publique dans l'objet retourné si elle était présente dans le document BSON.

`"bson"`

  Transforme un tableau BSON en un MongoDB\BSON\PackedArray et un document BSON en un MongoDB\BSON\Document, indépendamment du fait que le document BSON ait une propriété __pclass [1].

  > Note: La valeur `bson` n'est disponible que pour les trois types racines, et non dans les mappages spécifiques aux champs.

toues les autres chaînes de caractères

  Définit le nom de la classe à laquelle le document BSON doit être désérialisé. Pour les documents BSON qui incluent des propriétés __pclass, cette classe prendra la priorité.

  Si la classe nommée n'existe pas ou n'implémente pas l'interface MongoDB\BSON\Unserializable, une exception MongoDB\Driver\Exception\InvalidArgumentException est lancée.

  Si l'objet BSON a une propriété __pclass et que cette classe existe et implémente MongoDB\BSON\Persistable, elle prendra le pas sur la classe fournie dans la carte de type.

  Les propriétés du document BSON, y compris la propriété __pclass, seront envoyées sous forme de tableau associatif à la fonction MongoDB\BSON\Unserializable::bsonUnserialize() pour initialiser les propriétés de l'objet.

## TypeMaps

Les TypeMaps peuvent être définis via la méthode MongoDB\Driver\Cursor::setTypeMap() sur un objet MongoDB\Driver\Cursor, ou l'argument `$typeMap` de MongoDB\BSON\toPHP(), MongoDB\BSON\Document::toPHP(), et MongoDB\BSON\PackedArray::toPHP(). Chacune des trois classes (racine, document, et array) peut être définie individuellement, en plus des types spécifiques aux champs.

Si la valeur dans le TypeMap est NULL, cela signifie la même chose que la valeur par défaut pour cet élément.

## Exemples

Ces exemples utilisent les classes suivantes:

MyClass

  qui n'implémente aucune interface

YourClass

  qui implémente MongoDB\BSON\Unserializable

OurClass

  qui implémente MongoDB\BSON\Persistable

TheirClass

  qui étends OurClass

La méthode MongoDB\BSON\Unserializable::bsonUnserialize() de YourClass, OurClass, TheirClass itère sur le tableau et définit les propriétés sans modifications. Elle ajoute aussi la propriété `$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") }