====
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
:phpclass:`MongoDB\\Model\\BSONDocument` objects and BSON arrays as
:phpclass:`MongoDB\\Model\\BSONArray` objects, respectively.

BSON Classes
------------

.. phpclass:: MongoDB\\Model\\BSONArray

   This class extends PHP's :php:`ArrayObject <arrayobject>` class. It also
   implements PHP's :php:`JsonSerializable <jsonserializable>` interface and the
   driver's :php:`MongoDB\\BSON\\Serializable <mongodb-bson-serializable>` and
   :php:`MongoDB\\BSON\\Unserializable <mongodb-bson-unserializable>`
   interfaces.

   By default, the library will deserialize BSON arrays as instances of this
   class. During BSON and JSON serialization, instances of this class will
   serialize as an array type (:php:`array_values() <array_values>` is used
   internally to numerically reindex the array).

.. phpclass:: MongoDB\\Model\\BSONDocument

   This class extends PHP's :php:`ArrayObject <arrayobject>` class. It also
   implements PHP's :php:`JsonSerializable <jsonserializable>` interface and the
   driver's :php:`MongoDB\\BSON\\Serializable <mongodb-bson-serializable>` and
   :php:`MongoDB\\BSON\\Unserializable <mongodb-bson-unserializable>`
   interfaces.

   By default, the library will deserialize BSON documents as instances of this
   class. During BSON and JSON serialization, instances of this class will
   serialize as a document type (:php:`object casting
   <types.type-juggling#language.types.typecasting>` is used internally).

.. _php-type-map:

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 can
be used to specify a default type map to apply to any supporting methods and
selected classes (e.g. :phpmethod:`MongoDB\\Client::selectDatabase()`).

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',
   ]

The type map above will convert BSON documents and arrays to
:phpclass:`MongoDB\\Model\\BSONDocument` and
:phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. The ``root`` and
``document`` keys are used to distinguish the top-level BSON document from
embedded documents, respectively.

A type map may specify any class that implements
:php:`MongoDB\\BSON\\Unserializable <mongodb-bson-unserializable>` as well as
``"array"``, ``"stdClass``", and ``"object"`` (``"stdClass``" and ``"object"``
are aliases of one another).

.. seealso:: :php:`Deserialization from BSON <manual/en/mongodb.persistence.deserialization.php>` in the PHP manual

``Persistable`` Classes
-----------------------

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

The driver automatically handles serialization and deserialization for classes
implementing the :php:`Persistable <mongodb-bson-persistable>` interface without
requiring the use of the ``typeMap`` option. This is done by encoding the name
of the PHP class in a special property within the BSON document.

.. note::

   When deserializing a PHP variable from BSON, the encoded class name of a
   :php:`Persistable <mongodb-bson-persistable>` object will override any class
   specified in the type map, but it will not override ``"array"`` and
   ``"stdClass"`` or ``"object"``. This is discussed in the 
   :php:`persistence specification <mongodb.persistence>` but it bears
   repeating.

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;
           $this->createdAt = new MongoDB\BSON\UTCDateTime;
       }

       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 example 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)->test->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. It may not be used for BSON arrays.

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 numerically reindex the array.

The |php-library|'s :phpclass:`BSONDocument <MongoDB\\Model\\BSONDocument>` and
:phpclass:`BSONArray <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(
       'mongodb://127.0.0.1/',
       [],
       [
           'typeMap' => [
               'array' => 'array',
               'document' => 'array',
               'root' => 'array',
           ],
       ]
   );

   $document = $client->test->zips->findOne(['_id' => '94301']);

   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"
   }
