18
18
namespace MongoDB ;
19
19
20
20
use Exception ;
21
+ use MongoDB \BSON \Document ;
22
+ use MongoDB \BSON \PackedArray ;
21
23
use MongoDB \BSON \Serializable ;
22
24
use MongoDB \Driver \Exception \RuntimeException as DriverRuntimeException ;
23
25
use MongoDB \Driver \Manager ;
35
37
use function assert ;
36
38
use function end ;
37
39
use function get_object_vars ;
38
- use function in_array ;
39
40
use function is_array ;
40
41
use function is_object ;
41
42
use function is_string ;
@@ -94,15 +95,34 @@ function apply_type_map_to_document($document, array $typeMap)
94
95
}
95
96
96
97
/**
97
- * Generate an index name from a key specification.
98
+ * Converts a document parameter to an array.
99
+ *
100
+ * This is used to facilitate unified access to document fields. It also handles
101
+ * Document, PackedArray, and Serializable objects.
102
+ *
103
+ * PackedArray is intentionally allowed because this function is not used for
104
+ * type checking. Prohibiting PackedArray would only require callers to check
105
+ * for a PackedArray in order to avoid an exception. Furthermore, this function
106
+ * does not distinguish between Serializable::bsonSerialize() return values that
107
+ * might encode as a BSON document or array.
98
108
*
99
109
* @internal
100
- * @param array|object $document Document containing fields mapped to values,
101
- * which denote order or an index type
102
- * @throws InvalidArgumentException
110
+ * @param array|object $document
111
+ * @throws InvalidArgumentException if $document is not an array or object
103
112
*/
104
- function generate_index_name ($ document ): string
113
+ function document_to_array ($ document ): array
105
114
{
115
+ if ($ document instanceof Document || $ document instanceof PackedArray) {
116
+ /* Nested documents and arrays are intentionally left as BSON. We avoid
117
+ * iterator_to_array() since Document and PackedArray iteration returns
118
+ * all values as MongoDB\BSON\Value instances. */
119
+ return $ document ->toPHP ([
120
+ 'array ' => 'bson ' ,
121
+ 'document ' => 'bson ' ,
122
+ 'root ' => 'array ' ,
123
+ ]);
124
+ }
125
+
106
126
if ($ document instanceof Serializable) {
107
127
$ document = $ document ->bsonSerialize ();
108
128
}
@@ -115,6 +135,21 @@ function generate_index_name($document): string
115
135
throw InvalidArgumentException::invalidType ('$document ' , $ document , 'array or object ' );
116
136
}
117
137
138
+ return $ document ;
139
+ }
140
+
141
+ /**
142
+ * Generate an index name from a key specification.
143
+ *
144
+ * @internal
145
+ * @param array|object $document Document containing fields mapped to values,
146
+ * which denote order or an index type
147
+ * @throws InvalidArgumentException if $document is not an array or object
148
+ */
149
+ function generate_index_name ($ document ): string
150
+ {
151
+ $ document = document_to_array ($ document );
152
+
118
153
$ name = '' ;
119
154
120
155
foreach ($ document as $ field => $ type ) {
@@ -174,25 +209,17 @@ function get_encrypted_fields_from_server(string $databaseName, string $collecti
174
209
/**
175
210
* Return whether the first key in the document starts with a "$" character.
176
211
*
177
- * This is used for differentiating update and replacement documents.
212
+ * This is used for differentiating update and replacement documents. Since true
213
+ * and false return values may be expected in different contexts, this function
214
+ * intentionally throws if $document has an unexpected type.
178
215
*
179
216
* @internal
180
217
* @param array|object $document Update or replacement document
181
- * @throws InvalidArgumentException
218
+ * @throws InvalidArgumentException if $document is not an array or object
182
219
*/
183
220
function is_first_key_operator ($ document ): bool
184
221
{
185
- if ($ document instanceof Serializable) {
186
- $ document = $ document ->bsonSerialize ();
187
- }
188
-
189
- if (is_object ($ document )) {
190
- $ document = get_object_vars ($ document );
191
- }
192
-
193
- if (! is_array ($ document )) {
194
- throw InvalidArgumentException::invalidType ('$document ' , $ document , 'array or object ' );
195
- }
222
+ $ document = document_to_array ($ document );
196
223
197
224
reset ($ document );
198
225
$ firstKey = (string ) key ($ document );
@@ -208,6 +235,21 @@ function is_first_key_operator($document): bool
208
235
*/
209
236
function is_pipeline ($ pipeline ): bool
210
237
{
238
+ if ($ pipeline instanceof PackedArray) {
239
+ /* Nested documents and arrays are intentionally left as BSON. We avoid
240
+ * iterator_to_array() since Document iteration returns all values as
241
+ * MongoDB\BSON\Value instances. */
242
+ $ pipeline = $ pipeline ->toPHP ([
243
+ 'array ' => 'bson ' ,
244
+ 'document ' => 'bson ' ,
245
+ 'root ' => 'array ' ,
246
+ ]);
247
+ }
248
+
249
+ if ($ pipeline instanceof Serializable) {
250
+ $ pipeline = $ pipeline ->bsonSerialize ();
251
+ }
252
+
211
253
if (! is_array ($ pipeline )) {
212
254
return false ;
213
255
}
@@ -228,7 +270,7 @@ function is_pipeline($pipeline): bool
228
270
}
229
271
230
272
$ expectedKey ++;
231
- $ stage = ( array ) $ stage ;
273
+ $ stage = document_to_array ( $ stage) ;
232
274
reset ($ stage );
233
275
$ key = key ($ stage );
234
276
@@ -272,9 +314,15 @@ function is_last_pipeline_operator_write(array $pipeline): bool
272
314
return false ;
273
315
}
274
316
275
- $ lastOp = (array ) $ lastOp ;
317
+ if (! is_array ($ lastOp ) && ! is_object ($ lastOp )) {
318
+ return false ;
319
+ }
320
+
321
+ $ lastOp = document_to_array ($ lastOp );
322
+
323
+ reset ($ lastOp );
276
324
277
- return in_array ( key ($ lastOp ), [ ' $out ' , '$merge ' ], true ) ;
325
+ return key ($ lastOp ) === '$merge ' || key ( $ lastOp ) === ' $out ' ;
278
326
}
279
327
280
328
/**
@@ -285,25 +333,14 @@ function is_last_pipeline_operator_write(array $pipeline): bool
285
333
* @internal
286
334
* @see https://mongodb.com/docs/manual/reference/command/mapReduce/#output-inline
287
335
* @param string|array|object $out Output specification
288
- * @throws InvalidArgumentException
289
336
*/
290
337
function is_mapreduce_output_inline ($ out ): bool
291
338
{
292
339
if (! is_array ($ out ) && ! is_object ($ out )) {
293
340
return false ;
294
341
}
295
342
296
- if ($ out instanceof Serializable) {
297
- $ out = $ out ->bsonSerialize ();
298
- }
299
-
300
- if (is_object ($ out )) {
301
- $ out = get_object_vars ($ out );
302
- }
303
-
304
- if (! is_array ($ out )) {
305
- throw InvalidArgumentException::invalidType ('$out ' , $ out , 'array or object ' );
306
- }
343
+ $ out = document_to_array ($ out );
307
344
308
345
reset ($ out );
309
346
0 commit comments