Skip to content

Commit 789368a

Browse files
authored
PHPLIB-1122: Support Document and PackedArray objects in public APIs (#1077)
Excluding tests, most changes were concentrated in functions.php. A document_to_array() function was introduced to make it easier to access document values as arrays. This is mainly used by other utility functions (e.g. checking for dollar prefixed keys) but also ended up being used in the Find operation for handling the "modifiers" option. Adds tests for using Document and PackedArray objects in operations. Beyond handling of document/array types, this also filled gaps in test coverage for: * Validation for IndexInput key/name options * Passing a MongoDB\Driver\Command directly to DatabaseCommand * is_last_pipeline_operator_write() * Use elseif for checking Serializable after Document or PackedArray Processing a Document and PackedArray will yield an array, so we can avoid a redundant Serializable check by using elseif. * Use array_key_first() and refactor is_pipeline() to use is_first_key_operator() This introduces a dependency on symfony/polyfill-php73 and bumps both polyfill packages to the latest minor version for consistency. The doc block for is_first_key_operator() was revised to note its additional purpose for validating pipeline stages. * Test is_first_key_operator() with empty and packed arrays * Note an edge case where is_pipeline() may throw InvalidArgumentException * Add type annotations to assist static analysis * Use psalm-var annotation and fix phpcs errors * Regenerate psalm baseline This suppresses a new error in BulkWrite pertaining to a variable previously checked by is_first_key_operator() and is_pipeline(). ``` ERROR: MixedArgument - src/Operation/BulkWrite.php:341:45 - Argument 2 of MongoDB\Driver\BulkWrite::update cannot be mixed, expecting array<array-key, mixed>|object (see https://psalm.dev/030) $bulk->update($args[0], $args[1], $args[2]); The type of $args[1] is sourced from here - vendor/vimeo/psalm/stubs/CoreGenericFunctions.phpstub:154:12 * @return (TArray is array<empty,empty> ? false : (TArray is non-empty-array ? TValue : TValue|false)) ``` Other changes to the baseline are removals for obsolete errors.
1 parent 8d985e6 commit 789368a

25 files changed

+1017
-277
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"ext-json": "*",
1515
"ext-mongodb": "^1.16.0",
1616
"jean85/pretty-package-versions": "^2.0.1",
17-
"symfony/polyfill-php80": "^1.19"
17+
"symfony/polyfill-php73": "^1.27",
18+
"symfony/polyfill-php80": "^1.27"
1819
},
1920
"require-dev": {
2021
"squizlabs/php_codesniffer": "^3.7",

psalm-baseline.xml

Lines changed: 6 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<files psalm-version="4.30.0@d0bc6e25d89f649e4f36a534f330f8bb4643dd69">
3-
<file src="examples/aggregate.php">
4-
<MixedInferredReturnType occurrences="1">
5-
<code>string</code>
6-
</MixedInferredReturnType>
7-
<MixedReturnStatement occurrences="1">
8-
<code>toRelaxedExtendedJSON(fromPHP($document))</code>
9-
</MixedReturnStatement>
10-
</file>
11-
<file src="examples/bulk.php">
12-
<MixedInferredReturnType occurrences="1">
13-
<code>string</code>
14-
</MixedInferredReturnType>
15-
<MixedReturnStatement occurrences="1">
16-
<code>toRelaxedExtendedJSON(fromPHP($document))</code>
17-
</MixedReturnStatement>
18-
</file>
19-
<file src="examples/changestream.php">
20-
<MixedInferredReturnType occurrences="1">
21-
<code>string</code>
22-
</MixedInferredReturnType>
23-
<MixedReturnStatement occurrences="1">
24-
<code>toRelaxedExtendedJSON(fromPHP($document))</code>
25-
</MixedReturnStatement>
26-
</file>
27-
<file src="examples/command_logger.php">
28-
<MixedInferredReturnType occurrences="1">
29-
<code>string</code>
30-
</MixedInferredReturnType>
31-
<MixedReturnStatement occurrences="1">
32-
<code>toRelaxedExtendedJSON(fromPHP($document))</code>
33-
</MixedReturnStatement>
34-
</file>
353
<file src="examples/typemap.php">
364
<PropertyNotSetInConstructor occurrences="5">
375
<code>$address</code>
@@ -41,14 +9,6 @@
419
<code>$type</code>
4210
</PropertyNotSetInConstructor>
4311
</file>
44-
<file src="examples/with_transaction.php">
45-
<MixedInferredReturnType occurrences="1">
46-
<code>string</code>
47-
</MixedInferredReturnType>
48-
<MixedReturnStatement occurrences="1">
49-
<code>toRelaxedExtendedJSON(fromPHP($document))</code>
50-
</MixedReturnStatement>
51-
</file>
5212
<file src="src/Client.php">
5313
<MixedArgument occurrences="1">
5414
<code>$driverOptions['driver'] ?? []</code>
@@ -57,16 +17,6 @@
5717
<code>$mergedDriver['platform']</code>
5818
</MixedAssignment>
5919
</file>
60-
<file src="src/Collection.php">
61-
<MixedArgument occurrences="3">
62-
<code>$encryptedFields['eccCollection'] ?? 'enxcol_.' . $this-&gt;collectionName . '.ecc'</code>
63-
<code>$encryptedFields['ecocCollection'] ?? 'enxcol_.' . $this-&gt;collectionName . '.ecoc'</code>
64-
<code>$encryptedFields['escCollection'] ?? 'enxcol_.' . $this-&gt;collectionName . '.esc'</code>
65-
</MixedArgument>
66-
<MixedAssignment occurrences="1">
67-
<code>$encryptedFields</code>
68-
</MixedAssignment>
69-
</file>
7020
<file src="src/Command/ListCollections.php">
7121
<MixedAssignment occurrences="2">
7222
<code>$cmd[$option]</code>
@@ -79,21 +29,6 @@
7929
<code>$options['session']</code>
8030
</MixedAssignment>
8131
</file>
82-
<file src="src/Database.php">
83-
<MixedArgument occurrences="6">
84-
<code>$encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc'</code>
85-
<code>$encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc'</code>
86-
<code>$encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc'</code>
87-
<code>$encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc'</code>
88-
<code>$encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc'</code>
89-
<code>$encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc'</code>
90-
</MixedArgument>
91-
<MixedAssignment occurrences="3">
92-
<code>$encryptedFields</code>
93-
<code>$encryptedFields</code>
94-
<code>$options['encryptedFields']</code>
95-
</MixedAssignment>
96-
</file>
9732
<file src="src/Exception/BadMethodCallException.php">
9833
<UnsafeInstantiation occurrences="2">
9934
<code>new static(sprintf('%s is immutable', $class))</code>
@@ -139,14 +74,10 @@
13974
<code>drop</code>
14075
<code>rename</code>
14176
</MissingReturnType>
142-
<MixedArgument occurrences="3">
143-
<code>$id</code>
77+
<MixedArgument occurrences="2">
14478
<code>$options['revision']</code>
14579
<code>$options['revision']</code>
14680
</MixedArgument>
147-
<MixedAssignment occurrences="1">
148-
<code>$id</code>
149-
</MixedAssignment>
15081
</file>
15182
<file src="src/GridFS/Exception/CorruptFileException.php">
15283
<UnsafeInstantiation occurrences="4">
@@ -157,24 +88,12 @@
15788
</UnsafeInstantiation>
15889
</file>
15990
<file src="src/GridFS/Exception/FileNotFoundException.php">
160-
<MixedArgument occurrences="1">
161-
<code>$json</code>
162-
</MixedArgument>
163-
<MixedAssignment occurrences="1">
164-
<code>$json</code>
165-
</MixedAssignment>
16691
<UnsafeInstantiation occurrences="2">
16792
<code>new static(sprintf('File "%s" not found in "%s"', $json, $namespace))</code>
16893
<code>new static(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace))</code>
16994
</UnsafeInstantiation>
17095
</file>
17196
<file src="src/GridFS/Exception/StreamException.php">
172-
<MixedArgument occurrences="1">
173-
<code>$idString</code>
174-
</MixedArgument>
175-
<MixedAssignment occurrences="1">
176-
<code>$idString</code>
177-
</MixedAssignment>
17897
<UnsafeInstantiation occurrences="3">
17998
<code>new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $filename))</code>
18099
<code>new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString))</code>
@@ -384,7 +303,7 @@
384303
<DocblockTypeContradiction occurrences="1">
385304
<code>is_array($operation)</code>
386305
</DocblockTypeContradiction>
387-
<MixedArgument occurrences="11">
306+
<MixedArgument occurrences="12">
388307
<code>$args</code>
389308
<code>$args</code>
390309
<code>$args</code>
@@ -395,6 +314,7 @@
395314
<code>$args[1]</code>
396315
<code>$args[1]</code>
397316
<code>$args[1]</code>
317+
<code>$args[1]</code>
398318
<code>$args[2]</code>
399319
</MixedArgument>
400320
<MixedArrayAccess occurrences="25">
@@ -629,10 +549,9 @@
629549
<code>$this-&gt;options['typeMap']</code>
630550
<code>$this-&gt;options['writeConcern']</code>
631551
</MixedArgument>
632-
<MixedAssignment occurrences="6">
552+
<MixedAssignment occurrences="5">
633553
<code>$cmd[$option]</code>
634554
<code>$cmd['new']</code>
635-
<code>$cmd['update']</code>
636555
<code>$cmd['upsert']</code>
637556
<code>$options['session']</code>
638557
<code>$options['writeConcern']</code>
@@ -834,11 +753,9 @@
834753
</MixedAssignment>
835754
</file>
836755
<file src="src/functions.php">
837-
<DocblockTypeContradiction occurrences="4">
756+
<DocblockTypeContradiction occurrences="2">
838757
<code>! is_array($document) &amp;&amp; ! is_object($document)</code>
839758
<code>is_array($document)</code>
840-
<code>is_array($document)</code>
841-
<code>is_array($out)</code>
842759
</DocblockTypeContradiction>
843760
<MixedArgument occurrences="1">
844761
<code>$wireVersionForWriteStageOnSecondary</code>
@@ -853,9 +770,8 @@
853770
<code>$typeMap['fieldPaths'][$fieldPath]</code>
854771
<code>$typeMap['fieldPaths'][substr($fieldPath, 0, -2)]</code>
855772
</MixedArrayAssignment>
856-
<MixedAssignment occurrences="7">
773+
<MixedAssignment occurrences="6">
857774
<code>$element[$key]</code>
858-
<code>$lastOp</code>
859775
<code>$type</code>
860776
<code>$type</code>
861777
<code>$typeMap['fieldPaths'][$fieldPath . '.' . $existingFieldPath]</code>

src/Operation/Find.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use function is_integer;
3333
use function is_object;
3434
use function is_string;
35+
use function MongoDB\document_to_array;
3536
use function trigger_error;
3637

3738
use const E_USER_DEPRECATED;
@@ -427,10 +428,10 @@ private function createQueryOptions(): array
427428
}
428429
}
429430

430-
$modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers'];
431-
432-
if (! empty($modifiers)) {
433-
$options['modifiers'] = $modifiers;
431+
if (! empty($this->options['modifiers'])) {
432+
/** @psalm-var array|object */
433+
$modifiers = $this->options['modifiers'];
434+
$options['modifiers'] = is_object($modifiers) ? document_to_array($modifiers) : $modifiers;
434435
}
435436

436437
return $options;

src/Operation/FindAndModify.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,9 @@ private function createCommandDocument(): array
293293
}
294294

295295
if (isset($this->options['update'])) {
296-
$cmd['update'] = is_pipeline($this->options['update'])
297-
? $this->options['update']
298-
: (object) $this->options['update'];
296+
/** @psalm-var array|object */
297+
$update = $this->options['update'];
298+
$cmd['update'] = is_pipeline($update) ? $update : (object) $update;
299299
}
300300

301301
foreach (['arrayFilters', 'bypassDocumentValidation', 'comment', 'hint', 'maxTimeMS'] as $option) {

0 commit comments

Comments
 (0)