Skip to content

Commit 88731a5

Browse files
authored
PHPLIB-1137: Reject PackedArrays when expecting documents (#1117)
* Refactor data providers With this refactoring, data providers become more concise; each data entry also gets a distinct name making for easier identification of failing tests * PHPLIB-1137: Prohibit PackedArray objects where documents are required * Refactor validity check for bulk update argument * Fix invalid data providers * Ensure unique data set names in createOptionDataProvider * Move invalid update values data provider to operation tests * Introduce new exception factory for expected document type
1 parent 0d13981 commit 88731a5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+633
-1222
lines changed

psalm-baseline.xml

Lines changed: 28 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,6 @@
196196
</MixedAssignment>
197197
</file>
198198
<file src="src/Model/ChangeStreamIterator.php">
199-
<DocblockTypeContradiction occurrences="2">
200-
<code>! is_array($document) &amp;&amp; ! is_object($document)</code>
201-
<code>isset($initialResumeToken) &amp;&amp; ! is_array($initialResumeToken) &amp;&amp; ! is_object($initialResumeToken)</code>
202-
</DocblockTypeContradiction>
203199
<MixedArgument occurrences="2">
204200
<code>$reply-&gt;cursor-&gt;nextBatch</code>
205201
<code>$this-&gt;current()</code>
@@ -276,8 +272,9 @@
276272
</MixedAssignment>
277273
</file>
278274
<file src="src/Model/IndexInput.php">
279-
<MixedArgument occurrences="1">
275+
<MixedArgument occurrences="2">
280276
<code>$fieldName</code>
277+
<code>$index['key']</code>
281278
</MixedArgument>
282279
<MixedAssignment occurrences="2">
283280
<code>$fieldName</code>
@@ -328,11 +325,13 @@
328325
<code>$args[1]</code>
329326
<code>$args[2]</code>
330327
</MixedArgument>
331-
<MixedArrayAccess occurrences="28">
328+
<MixedArrayAccess occurrences="30">
332329
<code>$args[0]</code>
333330
<code>$args[0]</code>
334331
<code>$args[0]</code>
335332
<code>$args[0]</code>
333+
<code>$args[0]</code>
334+
<code>$args[1]</code>
336335
<code>$args[1]</code>
337336
<code>$args[1]</code>
338337
<code>$args[1]</code>
@@ -395,9 +394,6 @@
395394
</MixedOperand>
396395
</file>
397396
<file src="src/Operation/Count.php">
398-
<DocblockTypeContradiction occurrences="1">
399-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
400-
</DocblockTypeContradiction>
401397
<MixedAssignment occurrences="6">
402398
<code>$cmd[$option]</code>
403399
<code>$cmd['hint']</code>
@@ -410,11 +406,6 @@
410406
<code>isInTransaction</code>
411407
</MixedMethodCall>
412408
</file>
413-
<file src="src/Operation/CountDocuments.php">
414-
<DocblockTypeContradiction occurrences="1">
415-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
416-
</DocblockTypeContradiction>
417-
</file>
418409
<file src="src/Operation/CreateCollection.php">
419410
<MixedArgument occurrences="2">
420411
<code>$options['pipeline']</code>
@@ -427,7 +418,8 @@
427418
</MixedAssignment>
428419
</file>
429420
<file src="src/Operation/CreateEncryptedCollection.php">
430-
<MixedArgument occurrences="1">
421+
<MixedArgument occurrences="2">
422+
<code>$options['encryptedFields']</code>
431423
<code>$this-&gt;options['encryptedFields']</code>
432424
</MixedArgument>
433425
</file>
@@ -446,9 +438,6 @@
446438
</MixedMethodCall>
447439
</file>
448440
<file src="src/Operation/DatabaseCommand.php">
449-
<DocblockTypeContradiction occurrences="1">
450-
<code>! is_array($command) &amp;&amp; ! is_object($command)</code>
451-
</DocblockTypeContradiction>
452441
<MixedArgument occurrences="1">
453442
<code>$this-&gt;options['typeMap']</code>
454443
</MixedArgument>
@@ -458,9 +447,6 @@
458447
</MixedAssignment>
459448
</file>
460449
<file src="src/Operation/Delete.php">
461-
<DocblockTypeContradiction occurrences="1">
462-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
463-
</DocblockTypeContradiction>
464450
<MixedArgument occurrences="1">
465451
<code>$this-&gt;options['writeConcern']</code>
466452
</MixedArgument>
@@ -476,9 +462,6 @@
476462
</MixedMethodCall>
477463
</file>
478464
<file src="src/Operation/Distinct.php">
479-
<DocblockTypeContradiction occurrences="1">
480-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
481-
</DocblockTypeContradiction>
482465
<MixedArgument occurrences="1">
483466
<code>$this-&gt;options['typeMap']</code>
484467
</MixedArgument>
@@ -516,6 +499,11 @@
516499
<code>$options['writeConcern']</code>
517500
</MixedAssignment>
518501
</file>
502+
<file src="src/Operation/DropEncryptedCollection.php">
503+
<MixedArgument occurrences="1">
504+
<code>$options['encryptedFields']</code>
505+
</MixedArgument>
506+
</file>
519507
<file src="src/Operation/DropIndexes.php">
520508
<MixedArgument occurrences="1">
521509
<code>$this-&gt;options['typeMap']</code>
@@ -540,9 +528,6 @@
540528
</MixedAssignment>
541529
</file>
542530
<file src="src/Operation/Find.php">
543-
<DocblockTypeContradiction occurrences="1">
544-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
545-
</DocblockTypeContradiction>
546531
<MixedArgument occurrences="1">
547532
<code>$this-&gt;options['typeMap']</code>
548533
</MixedArgument>
@@ -583,32 +568,30 @@
583568
</MixedReturnStatement>
584569
</file>
585570
<file src="src/Operation/FindOneAndDelete.php">
586-
<DocblockTypeContradiction occurrences="1">
587-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
588-
</DocblockTypeContradiction>
571+
<MixedAssignment occurrences="1">
572+
<code>$options['fields']</code>
573+
</MixedAssignment>
589574
</file>
590575
<file src="src/Operation/FindOneAndReplace.php">
591-
<DocblockTypeContradiction occurrences="2">
592-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
593-
<code>! is_array($replacement) &amp;&amp; ! is_object($replacement)</code>
594-
</DocblockTypeContradiction>
576+
<MixedAssignment occurrences="1">
577+
<code>$options['fields']</code>
578+
</MixedAssignment>
595579
<MixedOperand occurrences="1">
596580
<code>$options['returnDocument']</code>
597581
</MixedOperand>
598582
</file>
599583
<file src="src/Operation/FindOneAndUpdate.php">
600-
<DocblockTypeContradiction occurrences="2">
601-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
584+
<DocblockTypeContradiction occurrences="1">
602585
<code>! is_array($update) &amp;&amp; ! is_object($update)</code>
603586
</DocblockTypeContradiction>
587+
<MixedAssignment occurrences="1">
588+
<code>$options['fields']</code>
589+
</MixedAssignment>
604590
<MixedOperand occurrences="1">
605591
<code>$options['returnDocument']</code>
606592
</MixedOperand>
607593
</file>
608594
<file src="src/Operation/InsertMany.php">
609-
<DocblockTypeContradiction occurrences="1">
610-
<code>! is_array($document) &amp;&amp; ! is_object($document)</code>
611-
</DocblockTypeContradiction>
612595
<MixedAssignment occurrences="4">
613596
<code>$insertedIds[$i]</code>
614597
<code>$options[$option]</code>
@@ -621,9 +604,6 @@
621604
</MixedMethodCall>
622605
</file>
623606
<file src="src/Operation/InsertOne.php">
624-
<DocblockTypeContradiction occurrences="1">
625-
<code>! is_array($document) &amp;&amp; ! is_object($document)</code>
626-
</DocblockTypeContradiction>
627607
<MixedAssignment occurrences="4">
628608
<code>$insertedId</code>
629609
<code>$options[$option]</code>
@@ -692,14 +672,8 @@
692672
<code>isInTransaction</code>
693673
</MixedMethodCall>
694674
</file>
695-
<file src="src/Operation/ReplaceOne.php">
696-
<DocblockTypeContradiction occurrences="1">
697-
<code>! is_array($replacement) &amp;&amp; ! is_object($replacement)</code>
698-
</DocblockTypeContradiction>
699-
</file>
700675
<file src="src/Operation/Update.php">
701-
<DocblockTypeContradiction occurrences="2">
702-
<code>! is_array($filter) &amp;&amp; ! is_object($filter)</code>
676+
<DocblockTypeContradiction occurrences="1">
703677
<code>! is_array($update) &amp;&amp; ! is_object($update)</code>
704678
</DocblockTypeContradiction>
705679
<MixedArgument occurrences="1">
@@ -767,11 +741,11 @@
767741
</MixedAssignment>
768742
</file>
769743
<file src="src/functions.php">
770-
<DocblockTypeContradiction occurrences="2">
771-
<code>! is_array($document) &amp;&amp; ! is_object($document)</code>
744+
<DocblockTypeContradiction occurrences="1">
772745
<code>is_array($document)</code>
773746
</DocblockTypeContradiction>
774-
<MixedArgument occurrences="1">
747+
<MixedArgument occurrences="2">
748+
<code>$stage</code>
775749
<code>$wireVersionForWriteStageOnSecondary</code>
776750
</MixedArgument>
777751
<MixedArgumentTypeCoercion occurrences="1">
@@ -784,8 +758,9 @@
784758
<code>$typeMap['fieldPaths'][$fieldPath]</code>
785759
<code>$typeMap['fieldPaths'][substr($fieldPath, 0, -2)]</code>
786760
</MixedArrayAssignment>
787-
<MixedAssignment occurrences="5">
761+
<MixedAssignment occurrences="6">
788762
<code>$element[$key]</code>
763+
<code>$stage</code>
789764
<code>$type</code>
790765
<code>$typeMap['fieldPaths'][$fieldPath . '.' . $existingFieldPath]</code>
791766
<code>$typeMap['fieldPaths'][$fieldPath]</code>

src/Command/ListCollections.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@
2525
use MongoDB\Model\CachingIterator;
2626
use MongoDB\Operation\Executable;
2727

28-
use function is_array;
2928
use function is_bool;
3029
use function is_integer;
31-
use function is_object;
30+
use function MongoDB\is_document;
3231

3332
/**
3433
* Wrapper for the listCollections command.
@@ -79,8 +78,8 @@ public function __construct(string $databaseName, array $options = [])
7978
throw InvalidArgumentException::invalidType('"authorizedCollections" option', $options['authorizedCollections'], 'boolean');
8079
}
8180

82-
if (isset($options['filter']) && ! is_array($options['filter']) && ! is_object($options['filter'])) {
83-
throw InvalidArgumentException::invalidType('"filter" option', $options['filter'], 'array or object');
81+
if (isset($options['filter']) && ! is_document($options['filter'])) {
82+
throw InvalidArgumentException::expectedDocumentType('"filter" option', $options['filter']);
8483
}
8584

8685
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {

src/Command/ListDatabases.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
use function is_array;
3030
use function is_bool;
3131
use function is_integer;
32-
use function is_object;
32+
use function MongoDB\is_document;
3333

3434
/**
3535
* Wrapper for the ListDatabases command.
@@ -76,8 +76,8 @@ public function __construct(array $options = [])
7676
throw InvalidArgumentException::invalidType('"authorizedDatabases" option', $options['authorizedDatabases'], 'boolean');
7777
}
7878

79-
if (isset($options['filter']) && ! is_array($options['filter']) && ! is_object($options['filter'])) {
80-
throw InvalidArgumentException::invalidType('"filter" option', $options['filter'], ['array', 'object']);
79+
if (isset($options['filter']) && ! is_document($options['filter'])) {
80+
throw InvalidArgumentException::expectedDocumentType('"filter" option', $options['filter']);
8181
}
8282

8383
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {

src/Exception/InvalidArgumentException.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@
2929

3030
class InvalidArgumentException extends DriverInvalidArgumentException implements Exception
3131
{
32+
/**
33+
* Thrown when an argument or option is expected to be a document.
34+
*
35+
* @param string $name Name of the argument or option
36+
* @param mixed $value Actual value (used to derive the type)
37+
*/
38+
public static function expectedDocumentType(string $name, $value): self
39+
{
40+
return new self(sprintf('Expected %s to have type "document" (array or object) but found "%s"', $name, get_debug_type($value)));
41+
}
42+
3243
/**
3344
* Thrown when an argument or option has an invalid type.
3445
*

src/GridFS/WritableStream.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@
2828
use function hash_final;
2929
use function hash_init;
3030
use function hash_update;
31-
use function is_array;
3231
use function is_bool;
3332
use function is_integer;
34-
use function is_object;
3533
use function is_string;
34+
use function MongoDB\is_document;
3635
use function MongoDB\is_string_array;
3736
use function sprintf;
3837
use function strlen;
@@ -130,8 +129,8 @@ public function __construct(CollectionWrapper $collectionWrapper, string $filena
130129
throw InvalidArgumentException::invalidType('"contentType" option', $options['contentType'], 'string');
131130
}
132131

133-
if (isset($options['metadata']) && ! is_array($options['metadata']) && ! is_object($options['metadata'])) {
134-
throw InvalidArgumentException::invalidType('"metadata" option', $options['metadata'], 'array or object');
132+
if (isset($options['metadata']) && ! is_document($options['metadata'])) {
133+
throw InvalidArgumentException::expectedDocumentType('"metadata" option', $options['metadata']);
135134
}
136135

137136
$this->chunkSize = $options['chunkSizeBytes'];

src/Model/ChangeStreamIterator.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use function is_object;
3737
use function MongoDB\Driver\Monitoring\addSubscriber;
3838
use function MongoDB\Driver\Monitoring\removeSubscriber;
39+
use function MongoDB\is_document;
3940

4041
/**
4142
* ChangeStreamIterator wraps a change stream's tailable cursor.
@@ -75,8 +76,8 @@ class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber
7576
*/
7677
public function __construct(Cursor $cursor, int $firstBatchSize, $initialResumeToken, ?object $postBatchResumeToken)
7778
{
78-
if (isset($initialResumeToken) && ! is_array($initialResumeToken) && ! is_object($initialResumeToken)) {
79-
throw InvalidArgumentException::invalidType('$initialResumeToken', $initialResumeToken, 'array or object');
79+
if (isset($initialResumeToken) && ! is_document($initialResumeToken)) {
80+
throw InvalidArgumentException::expectedDocumentType('$initialResumeToken', $initialResumeToken);
8081
}
8182

8283
parent::__construct($cursor);
@@ -234,8 +235,8 @@ public function valid(): bool
234235
*/
235236
private function extractResumeToken($document)
236237
{
237-
if (! is_array($document) && ! is_object($document)) {
238-
throw InvalidArgumentException::invalidType('$document', $document, 'array or object');
238+
if (! is_document($document)) {
239+
throw InvalidArgumentException::expectedDocumentType('$document', $document);
239240
}
240241

241242
if ($document instanceof Serializable) {

src/Model/IndexInput.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@
2020
use MongoDB\BSON\Serializable;
2121
use MongoDB\Exception\InvalidArgumentException;
2222

23-
use function is_array;
2423
use function is_float;
2524
use function is_int;
26-
use function is_object;
2725
use function is_string;
2826
use function MongoDB\document_to_array;
27+
use function MongoDB\is_document;
2928
use function sprintf;
3029

3130
/**
@@ -53,8 +52,8 @@ public function __construct(array $index)
5352
throw new InvalidArgumentException('Required "key" document is missing from index specification');
5453
}
5554

56-
if (! is_array($index['key']) && ! is_object($index['key'])) {
57-
throw InvalidArgumentException::invalidType('"key" option', $index['key'], 'array or object');
55+
if (! is_document($index['key'])) {
56+
throw InvalidArgumentException::expectedDocumentType('"key" option', $index['key']);
5857
}
5958

6059
foreach ($index['key'] as $fieldName => $order) {

src/Operation/Aggregate.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
use function is_object;
3939
use function is_string;
4040
use function MongoDB\create_field_path_type_map;
41+
use function MongoDB\is_document;
4142
use function MongoDB\is_last_pipeline_operator_write;
4243
use function MongoDB\is_pipeline;
4344

@@ -155,8 +156,8 @@ public function __construct(string $databaseName, ?string $collectionName, array
155156
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
156157
}
157158

158-
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
159-
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
159+
if (isset($options['collation']) && ! is_document($options['collation'])) {
160+
throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']);
160161
}
161162

162163
if (isset($options['explain']) && ! is_bool($options['explain'])) {
@@ -167,8 +168,8 @@ public function __construct(string $databaseName, ?string $collectionName, array
167168
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
168169
}
169170

170-
if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) {
171-
throw InvalidArgumentException::invalidType('"let" option', $options['let'], ['array', 'object']);
171+
if (isset($options['let']) && ! is_document($options['let'])) {
172+
throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']);
172173
}
173174

174175
if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) {

0 commit comments

Comments
 (0)