====
BSON
====

.. default-domain:: mongodb

.. contents:: On this page
   :local:
   :backlinks: none
   :depth: 1
   :class: singlecol

Overview
--------

MongoDB stores data records as BSON documents. BSON is a binary
representation of :term:`JSON` documents, though it contains more data
types than JSON. For the BSON spec, see `bsonspec.org <http://bsonspec.org/>`_.

By default, the |php-library| returns BSON documents as
``MongoDB\Model\BSONDocument`` objects and BSON arrays as
``MongoDB\Model\BSONArray`` objects. ``MongoDB\Model\BSONDocument`` and 
``MongoDB\Model\BSONArray`` extend PHP's
:php:`ArrayObject <arrayobject>` class and implement the MongoDB PHP
driver's :php:`MongoDB\\BSON\\Serializable <mongodb-bson-serializable>`
and :php:`MongoDB\\BSON\\Unserializable <mongodb-bson-unserializable>`
interfaces.

Type Maps
---------

Most methods that read data from MongoDB support a ``typeMap`` option,
which allows control over how BSON is converted to PHP. Additionally,
the :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and
:phpclass:`MongoDB\\Collection` classes accept a ``typeMap`` option,
which applies to any supporting methods and selected classes by default.

The :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and
:phpclass:`MongoDB\\Collection` classes use the following type map by
default:

.. code-block:: php

   [
       'array' => 'MongoDB\Model\BSONArray',
       'document' => 'MongoDB\Model\BSONDocument',
       'root' => 'MongoDB\Model\BSONDocument',
   ]

Serialization and deserialization of PHP variables into MongoDB
---------------------------------------------------------------

``Persistable`` Classes
~~~~~~~~~~~~~~~~~~~~~~~

The PHP driver's :php:`persistence <mongodb.persistence>` specification
specifies how classes implementing :php:`MongoDB\\BSON\\Persistable
<mongodb-bson-persistable>` are serialized and deserialized, and is
analogous to PHP's :php:`Serializable interface <class.serializable>`.

The PHP :php:`driver <mongodb>` automatically handles serialization and
deserialization for classes implementing ``MongoDB\BSON\Persistable``
without requiring the use of the ``typeMap`` option.

Consider the following class definition:

.. code-block:: php

   <?php

   class Person implements MongoDB\BSON\Persistable
   {
       private $id;
       private $name;
       private $createdAt;
       
       public function __construct($name)
       {
           $this->id = new MongoDB\BSON\ObjectID;
           $this->name = (string) $name;
       
           // Get current time in milliseconds since the epoch
           $msec = floor(microtime(true) * 1000);
           $this->createdAt = new MongoDB\BSON\UTCDateTime($msec);
       }
       
       function bsonSerialize()
       {
           return [
               '_id' => $this->id,
               'name' => $this->name,
               'createdAt' => $this->createdAt,
           ];
       }
       
       function bsonUnserialize(array $data)
       {
           $this->id = $data['_id'];
           $this->name = $data['name'];
           $this->createdAt = $data['createdAt'];
       }
   }

The following constructs a ``Person`` object, inserts it into the
database, and reads it back as an object of the same type:

.. code-block:: php

   <?php

   $collection = (new MongoDB\Client)->demo->persons;

   $result = $collection->insertOne(new Person('Bob'));

   $person = $collection->findOne(['_id' => $result->getInsertedId()]);

   var_dump($person);

The output would then resemble:

.. code-block:: none

   object(Person)#18 (3) {
     ["id":"Person":private]=>
     object(MongoDB\BSON\ObjectID)#15 (1) {
       ["oid"]=>
       string(24) "56fad2c36118fd2e9820cfc1"
     }
     ["name":"Person":private]=>
     string(3) "Bob"
     ["createdAt":"Person":private]=>
     object(MongoDB\BSON\UTCDateTime)#17 (1) {
       ["milliseconds"]=>
       int(1459278531218)
     }
   }

The same document in the MongoDB shell might display as:

.. code-block:: js

   {
     "_id" : ObjectId("56fad2c36118fd2e9820cfc1"),
     "__pclass" : BinData(128,"UGVyc29u"),
     "name" : "Bob",
     "createdAt" : ISODate("2016-03-29T19:08:51.218Z")
   }

.. note::

   :php:`MongoDB\\BSON\\Persistable <mongodb-bson-persistable>` may only be used
   for root and embedded BSON documents. BSON arrays are not supported.

Emulating the Legacy Driver
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The legacy :php:`mongo extension <mongo>` returned both BSON
documents and arrays as PHP arrays. While PHP arrays are convenient to
work with, this behavior was problematic:

-  Different BSON types could deserialize to the same PHP value (e.g.
   ``{"0": "foo"}`` and ``["foo"]``), which made it impossible to infer
   the original BSON type.

-  Numerically-indexed PHP arrays would be serialized as BSON documents
   if there was a gap in their key sequence. Such gaps were easily 
   caused by unsetting a key to remove an element and
   forgetting to reindex the array.

The |php-library|'s ``MongoDB\Model\BSONDocument`` and ``MongoDB\Model\BSONArray``
classes address these
concerns by preserving the BSON type information during serialization
and deserialization; however, some users may still prefer the legacy
behavior. If desired, you can use the ``typeMap`` option to have
the library return everything as a PHP array:

.. code-block:: php

   <?php

   $client = new MongoDB\Client(
       null,
       [],
       ['typeMap' => [
           'root' => 'array', 'document' => 'array', 'array' => 'array'
           ],
       ]
   );

   $document = $client->demo->zips->findOne(
       ['_id' => '94301'],
       ['typeMap' => [
            'root' => 'array', 'document' => 'array', 'array' => 'array'
            ],
       ]
   );

   var_dump($document);

The above example would output something similar to:

.. code-block:: php

   array(5) {
     ["_id"]=>
     string(5) "94301"
     ["city"]=>
     string(9) "PALO ALTO"
     ["loc"]=>
     array(2) {
       [0]=>
       float(-122.149685)
       [1]=>
       float(37.444324)
     }
     ["pop"]=>
     int(15965)
     ["state"]=>
     string(2) "CA"
   }
