|
| 1 | +# BSON Conversion |
| 2 | + |
| 3 | +## Deserialization |
| 4 | + |
| 5 | +By default, the library returns BSON documents and arrays as |
| 6 | +MongoDB\Model\BSONDocument and MongoDB\Model\BSONArray objects, respectively. |
| 7 | +Both of those classes extend PHP's [ArrayObject][arrayobject] class and |
| 8 | +implement the driver's [MongoDB\BSON\Serializable][serializable] and |
| 9 | +[MongoDB\BSON\Unserializable][unserializable] interfaces. |
| 10 | + |
| 11 | +[arrayobject]: http://php.net/arrayobject |
| 12 | +[serializable]: http://php.net/mongodb-bson-serializable |
| 13 | +[unserializable]: http://php.net/mongodb-bson-unserializable |
| 14 | + |
| 15 | +## Type Maps |
| 16 | + |
| 17 | +Most methods that read data from MongoDB support a "typeMap" option, which |
| 18 | +allows control over how BSON is converted to PHP. Additionally, the |
| 19 | +[MongoDB\Client][client], [MongoDB\Database][database], and |
| 20 | +[MongoDB\Collection][collection] classes accept a "typeMap" option, which will |
| 21 | +apply to any supporting methods and selected classes by default. |
| 22 | + |
| 23 | +[client]: ../classes/client.md |
| 24 | +[database]: ../classes/database.md |
| 25 | +[collection]: ../classes/collection.md |
| 26 | + |
| 27 | +The [MongoDB\Client][client], [MongoDB\Database][database], and |
| 28 | +[MongoDB\Collection][collection] classes use the following type map by default: |
| 29 | + |
| 30 | +```php |
| 31 | +[ |
| 32 | + 'array' => 'MongoDB\Model\BSONArray', |
| 33 | + 'document' => 'MongoDB\Model\BSONDocument', |
| 34 | + 'root' => 'MongoDB\Model\BSONDocument', |
| 35 | +] |
| 36 | +``` |
| 37 | + |
| 38 | +## Persistable Classes |
| 39 | + |
| 40 | +Classes implementing [MongoDB\BSON\Persistable][persistable] will be serialized |
| 41 | +and deserialized according to the [Persistence][persistence] specification. This |
| 42 | +behavior occurs by default in the [driver][ext-mongodb] and does not require use |
| 43 | +of the "typeMap" option. |
| 44 | + |
| 45 | +[persistable]: http://php.net/mongodb-bson-persistable |
| 46 | +[persistence]: http://php.net/manual/en/mongodb.persistence.php |
| 47 | +[ext-mongodb]: https://php.net/mongodb |
| 48 | + |
| 49 | +Given the following class definition: |
| 50 | + |
| 51 | +``` |
| 52 | +<?php |
| 53 | +
|
| 54 | +class Person implements MongoDB\BSON\Persistable |
| 55 | +{ |
| 56 | + private $id; |
| 57 | + private $name; |
| 58 | + private $createdAt; |
| 59 | +
|
| 60 | + public function __construct($name) |
| 61 | + { |
| 62 | + $this->id = new MongoDB\BSON\ObjectID; |
| 63 | + $this->name = (string) $name; |
| 64 | +
|
| 65 | + // Get current time in milliseconds since the epoch |
| 66 | + $msec = floor(microtime(true) * 1000); |
| 67 | + $this->createdAt = new MongoDB\BSON\UTCDateTime($msec); |
| 68 | + } |
| 69 | +
|
| 70 | + function bsonSerialize() |
| 71 | + { |
| 72 | + return [ |
| 73 | + '_id' => $this->id, |
| 74 | + 'name' => $this->name, |
| 75 | + 'createdAt' => $this->createdAt, |
| 76 | + ]; |
| 77 | + } |
| 78 | +
|
| 79 | + function bsonUnserialize(array $data) |
| 80 | + { |
| 81 | + $this->id = $data['_id']; |
| 82 | + $this->name = $data['name']; |
| 83 | + $this->createdAt = $data['createdAt']; |
| 84 | + } |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +The following example constructs a Person object, inserts it into the database, |
| 89 | +and reads it back as an object of the same type (without the use of the |
| 90 | +"typeMap" option): |
| 91 | + |
| 92 | +``` |
| 93 | +<?php |
| 94 | +
|
| 95 | +$collection = (new MongoDB\Client)->demo->persons; |
| 96 | +
|
| 97 | +$result = $collection->insertOne(new Person('Bob')); |
| 98 | +
|
| 99 | +$person = $collection->findOne(['_id' => $result->getInsertedId()]); |
| 100 | +
|
| 101 | +var_dump($person); |
| 102 | +``` |
| 103 | + |
| 104 | +The above example would output something similar to: |
| 105 | + |
| 106 | +``` |
| 107 | +object(Person)#18 (3) { |
| 108 | + ["id":"Person":private]=> |
| 109 | + object(MongoDB\BSON\ObjectID)#15 (1) { |
| 110 | + ["oid"]=> |
| 111 | + string(24) "56fad2c36118fd2e9820cfc1" |
| 112 | + } |
| 113 | + ["name":"Person":private]=> |
| 114 | + string(3) "Bob" |
| 115 | + ["createdAt":"Person":private]=> |
| 116 | + object(MongoDB\BSON\UTCDateTime)#17 (1) { |
| 117 | + ["milliseconds"]=> |
| 118 | + int(1459278531218) |
| 119 | + } |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +The same document in the MongoDB shell might display as: |
| 124 | + |
| 125 | +``` |
| 126 | +> db.persons.findOne() |
| 127 | +{ |
| 128 | + "_id" : ObjectId("56fad2c36118fd2e9820cfc1"), |
| 129 | + "__pclass" : BinData(128,"UGVyc29u"), |
| 130 | + "name" : "Bob", |
| 131 | + "createdAt" : ISODate("2016-03-29T19:08:51.218Z") |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +**Note:** [MongoDB\BSON\Persistable][persistable] may only be used for root and |
| 136 | +embedded BSON documents; BSON arrays are not supported. |
| 137 | + |
| 138 | +## Emulating the Legacy Driver |
| 139 | + |
| 140 | +The legacy [mongo extension][ext-mongo] returned both BSON documents and |
| 141 | +arrays as PHP arrays. While PHP arrays are convenient to work with, this |
| 142 | +behavior was problematic for several reasons: |
| 143 | + |
| 144 | +[ext-mongo]: http://php.net/mongo |
| 145 | + |
| 146 | + * Different BSON types could deserialize to the same PHP value (e.g. |
| 147 | + `{"0": "foo"}` and `["foo"]`), which made it impossible to infer the |
| 148 | + original BSON type. |
| 149 | + |
| 150 | + * Numerically indexed PHP arrays would be serialized as BSON documents if there |
| 151 | + was a gap in their key sequence. Such gaps were easily (and inadvertently) |
| 152 | + caused by unsetting a key to remove an element and forgetting to reindex the |
| 153 | + array. |
| 154 | + |
| 155 | +The libary's MongoDB\Model\BSONDocument and MongoDB\Model\BSONArray classes |
| 156 | +address these concerns by preserving the BSON type information during |
| 157 | +serialization and deserialization; however, some users may still prefer the |
| 158 | +legacy behavior. If desired, the following "typeMap" option can be used to have |
| 159 | +the library return everything as a PHP array: |
| 160 | + |
| 161 | +``` |
| 162 | +<?php |
| 163 | +
|
| 164 | +$client = new MongoDB\Client( |
| 165 | + null, |
| 166 | + [], |
| 167 | + ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']] |
| 168 | +); |
| 169 | +
|
| 170 | +$document = $client->demo->zips->findOne( |
| 171 | + ['_id' => '94301'], |
| 172 | + ['typeMap' => ['root' => 'array', 'document' => 'array', 'array' => 'array']] |
| 173 | +); |
| 174 | +
|
| 175 | +var_dump($document); |
| 176 | +``` |
| 177 | + |
| 178 | +The above example would output something similar to: |
| 179 | + |
| 180 | +``` |
| 181 | +array(5) { |
| 182 | + ["_id"]=> |
| 183 | + string(5) "94301" |
| 184 | + ["city"]=> |
| 185 | + string(9) "PALO ALTO" |
| 186 | + ["loc"]=> |
| 187 | + array(2) { |
| 188 | + [0]=> |
| 189 | + float(-122.149685) |
| 190 | + [1]=> |
| 191 | + float(37.444324) |
| 192 | + } |
| 193 | + ["pop"]=> |
| 194 | + int(15965) |
| 195 | + ["state"]=> |
| 196 | + string(2) "CA" |
| 197 | +} |
| 198 | +``` |
0 commit comments