diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 2c7fcb3b9..1c4e2a22a 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -71,5 +71,3 @@ include: - filename: .evergreen/config/generated/test/require-api-version.yml - filename: .evergreen/config/generated/test/csfle.yml - filename: .evergreen/config/generated/test-variant/modern-php-full.yml - - filename: .evergreen/config/generated/test-variant/phpc.yml - - filename: .evergreen/config/generated/test-variant/lowest.yml diff --git a/.evergreen/config/generate-config.php b/.evergreen/config/generate-config.php index 3bd1c710b..3629bf13f 100644 --- a/.evergreen/config/generate-config.php +++ b/.evergreen/config/generate-config.php @@ -55,8 +55,9 @@ // Test variants $allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'modern-php-full.yml', $supportedPhpVersions); -$allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'phpc.yml', [$latestPhpVersion]); -$allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'lowest.yml', [$lowestPhpVersion]); +// TODO: Re-enable when 2.0.0 is released +// $allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'phpc.yml', [$latestPhpVersion]); +// $allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'lowest.yml', [$lowestPhpVersion]); echo "Generated config. Use the following list to import files:\n"; echo implode("\n", array_map('getImportConfig', $allFiles)) . "\n"; diff --git a/.evergreen/config/generated/build/build-extension.yml b/.evergreen/config/generated/build/build-extension.yml index 22adcfc8a..02033b002 100644 --- a/.evergreen/config/generated/build/build-extension.yml +++ b/.evergreen/config/generated/build/build-extension.yml @@ -7,37 +7,41 @@ tasks: vars: PHP_VERSION: "8.3" - func: "compile extension" - - func: "upload extension" - - name: "build-php-8.3-lowest" - tags: ["build", "php8.3", "lowest", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.3" - - func: "compile extension" - vars: - EXTENSION_VERSION: "1.20.0" - - func: "upload extension" - - name: "build-php-8.3-next-stable" - tags: ["build", "php8.3", "next-stable", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.3" - - func: "compile extension" - vars: - EXTENSION_BRANCH: "v1.20" - - func: "upload extension" - - name: "build-php-8.3-next-minor" - tags: ["build", "php8.3", "next-minor"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.3" - - func: "compile extension" + # TODO: remove once 2.0.0 is released vars: - EXTENSION_BRANCH: "v1.x" + EXTENSION_BRANCH: "v2.x" - func: "upload extension" + # TODO: re-enable once 2.0.0 is released +# - name: "build-php-8.3-lowest" +# tags: ["build", "php8.3", "lowest", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.3" +# - func: "compile extension" +# vars: +# EXTENSION_VERSION: "2.0.0" +# - func: "upload extension" +# - name: "build-php-8.3-next-stable" +# tags: ["build", "php8.3", "next-stable", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.3" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.0" +# - func: "upload extension" +# - name: "build-php-8.3-next-minor" +# tags: ["build", "php8.3", "next-minor"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.3" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.x" +# - func: "upload extension" - name: "build-php-8.2" tags: ["build", "php8.2", "stable", "pr", "tag"] commands: @@ -45,37 +49,41 @@ tasks: vars: PHP_VERSION: "8.2" - func: "compile extension" - - func: "upload extension" - - name: "build-php-8.2-lowest" - tags: ["build", "php8.2", "lowest", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.2" - - func: "compile extension" - vars: - EXTENSION_VERSION: "1.20.0" - - func: "upload extension" - - name: "build-php-8.2-next-stable" - tags: ["build", "php8.2", "next-stable", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.2" - - func: "compile extension" + # TODO: remove once 2.0.0 is released vars: - EXTENSION_BRANCH: "v1.20" - - func: "upload extension" - - name: "build-php-8.2-next-minor" - tags: ["build", "php8.2", "next-minor"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.2" - - func: "compile extension" - vars: - EXTENSION_BRANCH: "v1.x" + EXTENSION_BRANCH: "v2.x" - func: "upload extension" + # TODO: re-enable once 2.0.0 is released +# - name: "build-php-8.2-lowest" +# tags: ["build", "php8.2", "lowest", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.2" +# - func: "compile extension" +# vars: +# EXTENSION_VERSION: "2.0.0" +# - func: "upload extension" +# - name: "build-php-8.2-next-stable" +# tags: ["build", "php8.2", "next-stable", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.2" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.0" +# - func: "upload extension" +# - name: "build-php-8.2-next-minor" +# tags: ["build", "php8.2", "next-minor"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.2" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.x" +# - func: "upload extension" - name: "build-php-8.1" tags: ["build", "php8.1", "stable", "pr", "tag"] commands: @@ -83,34 +91,38 @@ tasks: vars: PHP_VERSION: "8.1" - func: "compile extension" - - func: "upload extension" - - name: "build-php-8.1-lowest" - tags: ["build", "php8.1", "lowest", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.1" - - func: "compile extension" - vars: - EXTENSION_VERSION: "1.20.0" - - func: "upload extension" - - name: "build-php-8.1-next-stable" - tags: ["build", "php8.1", "next-stable", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.1" - - func: "compile extension" - vars: - EXTENSION_BRANCH: "v1.20" - - func: "upload extension" - - name: "build-php-8.1-next-minor" - tags: ["build", "php8.1", "next-minor"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "8.1" - - func: "compile extension" + # TODO: remove once 2.0.0 is released vars: - EXTENSION_BRANCH: "v1.x" + EXTENSION_BRANCH: "v2.x" - func: "upload extension" + # TODO: re-enable once 2.0.0 is released +# - name: "build-php-8.1-lowest" +# tags: ["build", "php8.1", "lowest", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.1" +# - func: "compile extension" +# vars: +# EXTENSION_VERSION: "2.0.0" +# - func: "upload extension" +# - name: "build-php-8.1-next-stable" +# tags: ["build", "php8.1", "next-stable", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.1" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.0" +# - func: "upload extension" +# - name: "build-php-8.1-next-minor" +# tags: ["build", "php8.1", "next-minor"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "8.1" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.x" +# - func: "upload extension" diff --git a/.evergreen/config/generated/test-variant/lowest.yml b/.evergreen/config/generated/test-variant/lowest.yml deleted file mode 100644 index ce3b5fc69..000000000 --- a/.evergreen/config/generated/test-variant/lowest.yml +++ /dev/null @@ -1,16 +0,0 @@ -# This file is generated automatically - please edit the "templates/test-variant/lowest.yml" template file instead. -buildvariants: - - name: test-rhel80-php-8.1-local-lowest - tags: ["test", "rhel", "x64", "php8.1", "pr", "tag"] - display_name: "Test: RHEL 8.0, PHP 8.1, Lowest Dependencies" - run_on: rhel80-small - expansions: - FETCH_BUILD_VARIANT: "build-rhel80" - FETCH_BUILD_TASK: "build-php-8.1-lowest" - PHP_VERSION: "8.1" - DEPENDENCIES: "lowest" - depends_on: - - variant: "build-rhel80" - name: "build-php-8.1-lowest" - tasks: - - ".replicaset .local .4.0 !.csfle" diff --git a/.evergreen/config/generated/test-variant/phpc.yml b/.evergreen/config/generated/test-variant/phpc.yml deleted file mode 100644 index e0eb428ce..000000000 --- a/.evergreen/config/generated/test-variant/phpc.yml +++ /dev/null @@ -1,42 +0,0 @@ -# This file is generated automatically - please edit the "templates/test-variant/phpc.yml" template file instead. -buildvariants: - # Variants with different PHPC versions - - name: test-debian12-php-8.3-phpc-next-stable - tags: ["test", "debian", "x64", "php8.3", "pr", "tag"] - display_name: "Test: Debian 12, PHP 8.3, PHPC next-stable" - run_on: debian12-small - expansions: - FETCH_BUILD_VARIANT: "build-debian12" - FETCH_BUILD_TASK: "build-php-8.3-next-stable" - PHP_VERSION: "8.3" - depends_on: - - variant: "build-debian12" - name: "build-php-8.3-next-stable" - tasks: - - ".standalone .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - ".replicaset .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - ".sharded .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - ".loadbalanced .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - "test_serverless_task_group" - - "test_serverless_proxy_task_group" - - "test-atlas-data-lake" - - - name: test-debian12-php-8.3-phpc-next-minor - tags: ["test", "debian", "x64", "php8.3"] - display_name: "Test: Debian 12, PHP 8.3, PHPC next-minor" - run_on: debian12-small - expansions: - FETCH_BUILD_VARIANT: "build-debian12" - FETCH_BUILD_TASK: "build-php-8.3-next-minor" - PHP_VERSION: "8.3" - depends_on: - - variant: "build-debian12" - name: "build-php-8.3-next-minor" - tasks: - - ".standalone .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - ".replicaset .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - ".sharded .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - ".loadbalanced .local !.csfle !.4.0 !.4.2 !.4.4 !.5.0 !.6.0" - - "test_serverless_task_group" - - "test_serverless_proxy_task_group" - - "test-atlas-data-lake" diff --git a/.evergreen/config/templates/build/build-extension.yml b/.evergreen/config/templates/build/build-extension.yml index 869c0a9e4..1599967ab 100644 --- a/.evergreen/config/templates/build/build-extension.yml +++ b/.evergreen/config/templates/build/build-extension.yml @@ -5,34 +5,38 @@ vars: PHP_VERSION: "%phpVersion%" - func: "compile extension" - - func: "upload extension" - - name: "build-php-%phpVersion%-lowest" - tags: ["build", "php%phpVersion%", "lowest", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "%phpVersion%" - - func: "compile extension" - vars: - EXTENSION_VERSION: "1.20.0" - - func: "upload extension" - - name: "build-php-%phpVersion%-next-stable" - tags: ["build", "php%phpVersion%", "next-stable", "pr", "tag"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "%phpVersion%" - - func: "compile extension" - vars: - EXTENSION_BRANCH: "v1.20" - - func: "upload extension" - - name: "build-php-%phpVersion%-next-minor" - tags: ["build", "php%phpVersion%", "next-minor"] - commands: - - func: "locate PHP binaries" - vars: - PHP_VERSION: "%phpVersion%" - - func: "compile extension" + # TODO: remove once 2.0.0 is released vars: - EXTENSION_BRANCH: "v1.x" + EXTENSION_BRANCH: "v2.x" - func: "upload extension" + # TODO: re-enable once 2.0.0 is released +# - name: "build-php-%phpVersion%-lowest" +# tags: ["build", "php%phpVersion%", "lowest", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "%phpVersion%" +# - func: "compile extension" +# vars: +# EXTENSION_VERSION: "2.0.0" +# - func: "upload extension" +# - name: "build-php-%phpVersion%-next-stable" +# tags: ["build", "php%phpVersion%", "next-stable", "pr", "tag"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "%phpVersion%" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.0" +# - func: "upload extension" +# - name: "build-php-%phpVersion%-next-minor" +# tags: ["build", "php%phpVersion%", "next-minor"] +# commands: +# - func: "locate PHP binaries" +# vars: +# PHP_VERSION: "%phpVersion%" +# - func: "compile extension" +# vars: +# EXTENSION_BRANCH: "v2.x" +# - func: "upload extension" diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 462e4d67e..b03f7779f 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -13,7 +13,9 @@ on: env: PHP_VERSION: "8.2" - DRIVER_VERSION: "stable" + # TODO: change to "stable" once 2.0.0 is released + # DRIVER_VERSION: "stable" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" jobs: phpcs: diff --git a/.github/workflows/generator.yml b/.github/workflows/generator.yml index 2427a41eb..711befabd 100644 --- a/.github/workflows/generator.yml +++ b/.github/workflows/generator.yml @@ -13,9 +13,9 @@ on: env: PHP_VERSION: "8.2" - # TODO: change to "stable" once 1.20.0 is released + # TODO: change to "stable" once 2.0.0 is released # DRIVER_VERSION: "stable" - DRIVER_VERSION: "mongodb/mongo-php-driver@v1.20" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" jobs: psalm: diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 0103bdcd1..82919098f 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -19,7 +19,9 @@ on: env: PHP_VERSION: "8.2" - DRIVER_VERSION: "stable" + # TODO: change to "stable" once 2.0.0 is released + # DRIVER_VERSION: "stable" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" jobs: psalm: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e198604e3..e9cf73916 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,9 @@ on: - "feature/*" env: - DRIVER_VERSION: "stable" + # TODO: change to "stable" once 2.0.0 is released + # DRIVER_VERSION: "stable" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" jobs: phpunit: diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md new file mode 100644 index 000000000..516b57929 --- /dev/null +++ b/UPGRADE-2.0.md @@ -0,0 +1,83 @@ +UPGRADE FROM 1.x to 2.0 +======================== + + * Classes in the namespace `MongoDB\Operation\` are `final`. + * All methods in interfaces and classes now define a return type. + * The `MongoDB\ChangeStream::CURSOR_NOT_FOUND` constant is now private. + * The `MongoDB\Operation\Watch::FULL_DOCUMENT_DEFAULT` constant has been + removed. + * The `getNamespace` and `isGeoHaystack` methods have been removed from the + `MongoDB\Model\IndexInfo` class. + * The `maxScan`, `modifiers`, `oplogReplay`, and `snapshot` options for `find` + and `findOne` operations have been removed. + * The `MongoDB\Collection::mapReduce` method has been removed. Use + [aggregation pipeline](https://www.mongodb.com/docs/manual/reference/map-reduce-to-aggregation-pipeline/) + instead. + * The following classes and interfaces have been removed without replacement: + * `MongoDB\MapReduceResult` + * `MongoDB\Model\CollectionInfoCommandIterator` + * `MongoDB\Model\CollectionInfoIterator` + * `MongoDB\Model\DatabaseInfoIterator` + * `MongoDB\Model\DatabaseInfoLegacyIterator` + * `MongoDB\Model\IndexInfoIterator` + * `MongoDB\Model\IndexInfoIteratorIterator` + * `MongoDB\Operation\Executable` + * The `flags` and `autoIndexId` options for + `MongoDB\Database::createCollection()` have been removed. Additionally, the + `USE_POWER_OF_2_SIZES` and `NO_PADDING` constants in + `MongoDB\Operation\CreateCollection` have been removed. + +Operations with no result +------------------------- + +The following operations no longer return the raw command result. The return +type changed to `void`. In case of an error, an exception is thrown. + + * `MongoDB\Client`: `dropDatabase` + * `MongoDB\Collection`: `drop`, `dropIndex`, `dropIndexes`, `dropSearchIndex`, `rename` + * `MongoDB\Database`: `createCollection`, `drop`, `dropCollection`, `renameCollection` + * `MongoDB\Database::createEncryptedCollection()` returns the list of encrypted fields + +If you still need to access the raw command result, you can use a +[`CommandSubscriber`](https://www.php.net/manual/en/class.mongodb-driver-monitoring-commandsubscriber.php). + +GridFS +------ + + * The `md5` is no longer calculated when a file is uploaded to GridFS. + Applications that require a file digest should implement it outside GridFS + and store in metadata. + + ```php + $hash = hash_file('sha256', $filename); + $bucket->openUploadStream($fileId, ['metadata' => ['hash' => $hash]]); + ``` + + * The fields `contentType` and `aliases` are no longer stored in the `files` + collection. Applications that require this information should store it in + metadata. + + **Before:** + ```php + $bucket->openUploadStream($fileId, ['contentType' => 'image/png']); + ``` + + **After:** + ```php + $bucket->openUploadStream($fileId, ['metadata' => ['contentType' => 'image/png']]); + ``` + +UnsupportedException method removals +------------------------------------ + +The following methods have been removed from the +`MongoDB\Exception\UnsupportedException` class: + * `allowDiskUseNotSupported` + * `arrayFiltersNotSupported` + * `collationNotSupported` + * `explainNotSupported` + * `readConcernNotSupported` + * `writeConcernNotSupported` + +The remaining methods have been marked as internal and may be removed in a +future minor version. Only the class itself is covered by the BC promise. diff --git a/composer.json b/composer.json index 5a2c10def..cc7fabf52 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,8 @@ ], "require": { "php": "^8.1", - "ext-hash": "*", "ext-json": "*", - "ext-mongodb": "^1.20.0", + "ext-mongodb": "^2.0", "composer-runtime-api": "^2.0", "psr/log": "^1.1.4|^2|^3", "symfony/polyfill-php80": "^1.27", diff --git a/phpcs.xml.dist b/phpcs.xml.dist index de542ac64..3c2b9441b 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -133,14 +133,6 @@ - - - - - src - - - diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 039b43007..6521b8544 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -5,11 +5,7 @@ name]]> - - - - @@ -23,6 +19,9 @@ + + + @@ -180,14 +179,6 @@ count($queriesOrArrayOfQueries[0]) > 0]]> - - - - - - - - @@ -324,6 +315,7 @@ getArrayCopy()]]> + getArrayCopy()]]> @@ -331,6 +323,7 @@ + @@ -339,6 +332,15 @@ + + + + + ]]> + + + ]]> + cursor->nextBatch]]> @@ -354,39 +356,6 @@ current()]]> - - - - - - - - - - - - - - - - - - - - - - databases)]]> - - - - databases)]]> - - - - - - - index]]> @@ -550,9 +519,6 @@ - - options['typeMap']]]> - @@ -624,9 +590,6 @@ - - options['typeMap']]]> - @@ -637,9 +600,6 @@ - - options['typeMap']]]> - @@ -652,9 +612,6 @@ - - options['typeMap']]]> - @@ -684,11 +641,7 @@ options['codec']]]> options['typeMap']]]> - - - - @@ -724,18 +677,6 @@ value ?? null) : null]]> - - - - - - - - - - - - @@ -785,44 +726,12 @@ - - - listCollections->execute($server), $this->databaseName)]]> - - - - - listDatabases->execute($server))]]> - - - - - databaseName . '.' . $this->collectionName)]]> - - - - - result->collection]]> - result->db]]> - options['typeMap']]]> - - - - - - - - - - - - options['typeMap']]]> @@ -834,9 +743,6 @@ - - options['typeMap']]]> - diff --git a/psalm.xml.dist b/psalm.xml.dist index 83d825d51..9aa4d0a7e 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -21,6 +21,9 @@ + + + diff --git a/src/BulkWriteResult.php b/src/BulkWriteResult.php index 4fb556e0b..43c60ea2d 100644 --- a/src/BulkWriteResult.php +++ b/src/BulkWriteResult.php @@ -17,19 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a bulk write operation. */ class BulkWriteResult { - private bool $isAcknowledged; - public function __construct(private WriteResult $writeResult, private array $insertedIds) { - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -38,16 +35,11 @@ public function __construct(private WriteResult $writeResult, private array $ins * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getDeletedCount() + public function getDeletedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getDeletedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getDeletedCount(); } /** @@ -56,16 +48,11 @@ public function getDeletedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getInsertedCount() + public function getInsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getInsertedCount(); } /** @@ -76,10 +63,8 @@ public function getInsertedCount() * the driver did not generate an ID), the index will contain its "_id" * field value. Any driver-generated ID will be a MongoDB\BSON\ObjectId * instance. - * - * @return array */ - public function getInsertedIds() + public function getInsertedIds(): array { return $this->insertedIds; } @@ -90,16 +75,11 @@ public function getInsertedIds() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getMatchedCount() + public function getMatchedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getMatchedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getMatchedCount(); } /** @@ -111,16 +91,11 @@ public function getMatchedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getModifiedCount() + public function getModifiedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getModifiedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getModifiedCount(); } /** @@ -129,16 +104,11 @@ public function getModifiedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedCount() + public function getUpsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getUpsertedCount(); } /** @@ -152,16 +122,11 @@ public function getUpsertedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return array - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedIds() + public function getUpsertedIds(): array { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedIds(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getUpsertedIds(); } /** @@ -169,11 +134,9 @@ public function getUpsertedIds() * * If the update was not acknowledged, other fields from the WriteResult * (e.g. matchedCount) will be undefined. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { - return $this->isAcknowledged; + return $this->writeResult->isAcknowledged(); } } diff --git a/src/ChangeStream.php b/src/ChangeStream.php index 55f25b031..c48cd1483 100644 --- a/src/ChangeStream.php +++ b/src/ChangeStream.php @@ -21,22 +21,16 @@ use MongoDB\BSON\Document; use MongoDB\BSON\Int64; use MongoDB\Codec\DocumentCodec; -use MongoDB\Driver\CursorId; use MongoDB\Driver\Exception\ConnectionException; use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Exception\ServerException; use MongoDB\Exception\BadMethodCallException; use MongoDB\Exception\ResumeTokenException; use MongoDB\Model\ChangeStreamIterator; -use ReturnTypeWillChange; use function assert; use function call_user_func; use function in_array; -use function sprintf; -use function trigger_error; - -use const E_USER_DEPRECATED; /** * Iterator for a change stream. @@ -49,11 +43,7 @@ */ class ChangeStream implements Iterator { - /** - * @deprecated 1.4 - * @todo make this constant private in 2.0 (see: PHPLIB-360) - */ - public const CURSOR_NOT_FOUND = 43; + private const CURSOR_NOT_FOUND = 43; private const RESUMABLE_ERROR_CODES = [ 6, // HostUnreachable @@ -88,12 +78,8 @@ class ChangeStream implements Iterator */ private bool $hasAdvanced = false; - /** - * @see https://php.net/iterator.current - * @return array|object|null - */ - #[ReturnTypeWillChange] - public function current() + /** @see https://php.net/iterator.current */ + public function current(): array|object|null { $value = $this->iterator->current(); @@ -106,26 +92,9 @@ public function current() return $this->codec->decode($value); } - /** - * @return CursorId|Int64 - * @psalm-return ($asInt64 is true ? Int64 : CursorId) - */ - #[ReturnTypeWillChange] - public function getCursorId(bool $asInt64 = false) + public function getCursorId(): Int64 { - if (! $asInt64) { - @trigger_error( - sprintf( - 'The method "%s" will no longer return a "%s" instance in the future. Pass "true" as argument to change to the new behavior and receive a "%s" instance instead.', - __METHOD__, - CursorId::class, - Int64::class, - ), - E_USER_DEPRECATED, - ); - } - - return $this->iterator->getInnerIterator()->getId($asInt64); + return $this->iterator->getInnerIterator()->getId(); } /** @@ -134,20 +103,14 @@ public function getCursorId(bool $asInt64 = false) * Null may be returned if no change documents have been iterated and the * server did not include a postBatchResumeToken in its aggregate or getMore * command response. - * - * @return array|object|null */ - public function getResumeToken() + public function getResumeToken(): array|object|null { return $this->iterator->getResumeToken(); } - /** - * @see https://php.net/iterator.key - * @return int|null - */ - #[ReturnTypeWillChange] - public function key() + /** @see https://php.net/iterator.key */ + public function key(): ?int { if ($this->valid()) { return $this->key; @@ -158,11 +121,9 @@ public function key() /** * @see https://php.net/iterator.next - * @return void * @throws ResumeTokenException */ - #[ReturnTypeWillChange] - public function next() + public function next(): void { try { $this->iterator->next(); @@ -174,11 +135,9 @@ public function next() /** * @see https://php.net/iterator.rewind - * @return void * @throws ResumeTokenException */ - #[ReturnTypeWillChange] - public function rewind() + public function rewind(): void { try { $this->iterator->rewind(); @@ -191,12 +150,8 @@ public function rewind() } } - /** - * @see https://php.net/iterator.valid - * @return boolean - */ - #[ReturnTypeWillChange] - public function valid() + /** @see https://php.net/iterator.valid */ + public function valid(): bool { return $this->iterator->valid(); } @@ -255,7 +210,8 @@ private function onIteration(bool $incrementKey): void * have been received in the last response. Therefore, we can unset the * resumeCallable. This will free any reference to Watch as well as the * only reference to any implicit session created therein. */ - if ((string) $this->getCursorId(true) === '0') { + // Use a type-unsafe comparison to compare with Int64 instances + if ($this->getCursorId() == 0) { $this->resumeCallable = null; } diff --git a/src/Client.php b/src/Client.php index f21d09d72..43856439b 100644 --- a/src/Client.php +++ b/src/Client.php @@ -49,10 +49,6 @@ use function array_diff_key; use function is_array; use function is_string; -use function sprintf; -use function trigger_error; - -use const E_USER_DEPRECATED; class Client { @@ -147,9 +143,8 @@ public function __construct(?string $uri = null, array $uriOptions = [], array $ * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'manager' => $this->manager, @@ -170,19 +165,16 @@ public function __debugInfo() * @see https://php.net/oop5.overloading#object.get * @see https://php.net/types.string#language.types.string.parsing.complex * @param string $databaseName Name of the database to select - * @return Database */ - public function __get(string $databaseName) + public function __get(string $databaseName): Database { return $this->selectDatabase($databaseName); } /** * Return the connection string (i.e. URI). - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->uri; } @@ -201,10 +193,8 @@ final public function addSubscriber(Subscriber $subscriber): void * Returns a ClientEncryption instance for explicit encryption and decryption * * @param array $options Encryption options - * - * @return ClientEncryption */ - public function createClientEncryption(array $options) + public function createClientEncryption(array $options): ClientEncryption { if (isset($options['keyVaultClient'])) { if ($options['keyVaultClient'] instanceof self) { @@ -223,19 +213,12 @@ public function createClientEncryption(array $options) * @see DropDatabase::__construct() for supported options * @param string $databaseName Database name * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropDatabase(string $databaseName, array $options = []) + public function dropDatabase(string $databaseName, array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } else { - @trigger_error(sprintf('The function %s() will return nothing in mongodb/mongodb v2.0, the "typeMap" option is deprecated', __FUNCTION__), E_USER_DEPRECATED); - } - $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { @@ -244,15 +227,13 @@ public function dropDatabase(string $databaseName, array $options = []) $operation = new DropDatabase($databaseName, $options); - return $operation->execute($server); + $operation->execute($server); } /** * Return the Manager. - * - * @return Manager */ - public function getManager() + public function getManager(): Manager { return $this->manager; } @@ -261,29 +242,24 @@ public function getManager() * Return the read concern for this client. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this client. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this client. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -292,9 +268,8 @@ public function getTypeMap() * Return the write concern for this client. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -303,6 +278,7 @@ public function getWriteConcern() * List database names. * * @see ListDatabaseNames::__construct() for supported options + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -324,7 +300,7 @@ public function listDatabaseNames(array $options = []): Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listDatabases(array $options = []) + public function listDatabases(array $options = []): Iterator { $operation = new ListDatabases($options); $server = select_server($this->manager, $options); @@ -349,10 +325,9 @@ final public function removeSubscriber(Subscriber $subscriber): void * @param string $databaseName Name of the database containing the collection * @param string $collectionName Name of the collection to select * @param array $options Collection constructor options - * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectCollection(string $databaseName, string $collectionName, array $options = []) + public function selectCollection(string $databaseName, string $collectionName, array $options = []): Collection { $options += ['typeMap' => $this->typeMap, 'builderEncoder' => $this->builderEncoder]; @@ -365,10 +340,9 @@ public function selectCollection(string $databaseName, string $collectionName, a * @see Database::__construct() for supported options * @param string $databaseName Name of the database to select * @param array $options Database constructor options - * @return Database * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectDatabase(string $databaseName, array $options = []) + public function selectDatabase(string $databaseName, array $options = []): Database { $options += ['typeMap' => $this->typeMap, 'builderEncoder' => $this->builderEncoder]; @@ -380,9 +354,8 @@ public function selectDatabase(string $databaseName, array $options = []) * * @see https://php.net/manual/en/mongodb-driver-manager.startsession.php * @param array $options Session options - * @return Session */ - public function startSession(array $options = []) + public function startSession(array $options = []): Session { return $this->manager->startSession($options); } @@ -393,10 +366,9 @@ public function startSession(array $options = []) * @see Watch::__construct() for supported options * @param array $pipeline Aggregation pipeline * @param array $options Command options - * @return ChangeStream * @throws InvalidArgumentException for parameter/option parsing errors */ - public function watch(array $pipeline = [], array $options = []) + public function watch(array $pipeline = [], array $options = []): ChangeStream { if (is_builder_pipeline($pipeline)) { $pipeline = new Pipeline(...$pipeline); diff --git a/src/Codec/DecodeIfSupported.php b/src/Codec/DecodeIfSupported.php index 7fd768011..ea8dde15c 100644 --- a/src/Codec/DecodeIfSupported.php +++ b/src/Codec/DecodeIfSupported.php @@ -30,17 +30,13 @@ abstract public function canDecode(mixed $value): bool; /** * @psalm-param BSONType $value - * @return mixed * @psalm-return NativeType * @throws UnsupportedValueException if the decoder does not support the value */ - abstract public function decode(mixed $value); + abstract public function decode(mixed $value): mixed; - /** - * @return mixed - * @psalm-return ($value is BSONType ? NativeType : $value) - */ - public function decodeIfSupported(mixed $value) + /** @psalm-return ($value is BSONType ? NativeType : $value) */ + public function decodeIfSupported(mixed $value): mixed { return $this->canDecode($value) ? $this->decode($value) : $value; } diff --git a/src/Codec/Decoder.php b/src/Codec/Decoder.php index 432fb2dd7..37ff9b263 100644 --- a/src/Codec/Decoder.php +++ b/src/Codec/Decoder.php @@ -37,11 +37,10 @@ public function canDecode(mixed $value): bool; * should throw an exception. * * @psalm-param BSONType $value - * @return mixed * @psalm-return NativeType * @throws UnsupportedValueException if the decoder does not support the value */ - public function decode(mixed $value); + public function decode(mixed $value): mixed; /** * Decodes a given value if supported, otherwise returns the value as-is. @@ -49,8 +48,7 @@ public function decode(mixed $value); * The DecodeIfSupported trait provides a default implementation of this * method. * - * @return mixed * @psalm-return ($value is BSONType ? NativeType : $value) */ - public function decodeIfSupported(mixed $value); + public function decodeIfSupported(mixed $value): mixed; } diff --git a/src/Codec/EncodeIfSupported.php b/src/Codec/EncodeIfSupported.php index 33823cfd6..2ce1fcf53 100644 --- a/src/Codec/EncodeIfSupported.php +++ b/src/Codec/EncodeIfSupported.php @@ -30,17 +30,13 @@ abstract public function canEncode(mixed $value): bool; /** * @psalm-param NativeType $value - * @return mixed * @psalm-return BSONType * @throws UnsupportedValueException if the encoder does not support the value */ - abstract public function encode(mixed $value); + abstract public function encode(mixed $value): mixed; - /** - * @return mixed - * @psalm-return ($value is NativeType ? BSONType : $value) - */ - public function encodeIfSupported(mixed $value) + /** @psalm-return ($value is NativeType ? BSONType : $value) */ + public function encodeIfSupported(mixed $value): mixed { return $this->canEncode($value) ? $this->encode($value) : $value; } diff --git a/src/Codec/Encoder.php b/src/Codec/Encoder.php index 0cd0d58cb..c8ee0917b 100644 --- a/src/Codec/Encoder.php +++ b/src/Codec/Encoder.php @@ -37,11 +37,10 @@ public function canEncode(mixed $value): bool; * should throw an exception. * * @psalm-param NativeType $value - * @return mixed * @psalm-return BSONType * @throws UnsupportedValueException if the encoder does not support the value */ - public function encode(mixed $value); + public function encode(mixed $value): mixed; /** * Encodes a given value if supported, otherwise returns the value as-is. @@ -49,8 +48,7 @@ public function encode(mixed $value); * The EncodeIfSupported trait provides a default implementation of this * method. * - * @return mixed * @psalm-return ($value is NativeType ? BSONType : $value) */ - public function encodeIfSupported(mixed $value); + public function encodeIfSupported(mixed $value): mixed; } diff --git a/src/Collection.php b/src/Collection.php index a4ff6326f..055ade77a 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -20,7 +20,6 @@ use Countable; use Iterator; use MongoDB\BSON\Document; -use MongoDB\BSON\JavascriptInterface; use MongoDB\BSON\PackedArray; use MongoDB\Builder\BuilderEncoder; use MongoDB\Builder\Pipeline; @@ -63,7 +62,6 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListIndexes; use MongoDB\Operation\ListSearchIndexes; -use MongoDB\Operation\MapReduce; use MongoDB\Operation\RenameCollection; use MongoDB\Operation\ReplaceOne; use MongoDB\Operation\UpdateMany; @@ -77,11 +75,7 @@ use function array_key_exists; use function current; use function is_array; -use function sprintf; use function strlen; -use function trigger_error; - -use const E_USER_DEPRECATED; class Collection { @@ -185,9 +179,8 @@ public function __construct(private Manager $manager, private string $databaseNa * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'builderEncoder' => $this->builderEncoder, @@ -206,9 +199,8 @@ public function __debugInfo() * Return the collection namespace (e.g. "db.collection"). * * @see https://mongodb.com/docs/manual/core/databases-and-collections/ - * @return string */ - public function __toString() + public function __toString(): string { return $this->databaseName . '.' . $this->collectionName; } @@ -219,13 +211,12 @@ public function __toString() * @see Aggregate::__construct() for supported options * @param array $pipeline Aggregation pipeline * @param array $options Command options - * @return CursorInterface&Iterator * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function aggregate(array $pipeline, array $options = []) + public function aggregate(array $pipeline, array $options = []): CursorInterface { if (is_builder_pipeline($pipeline)) { $pipeline = new Pipeline(...$pipeline); @@ -265,12 +256,11 @@ public function aggregate(array $pipeline, array $options = []) * @see BulkWrite::__construct() for supported options * @param array[] $operations List of write operations * @param array $options Command options - * @return BulkWriteResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function bulkWrite(array $operations, array $options = []) + public function bulkWrite(array $operations, array $options = []): BulkWriteResult { $options = $this->inheritBuilderEncoder($options); $options = $this->inheritWriteOptions($options); @@ -287,7 +277,6 @@ public function bulkWrite(array $operations, array $options = []) * @see Count::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors @@ -295,7 +284,7 @@ public function bulkWrite(array $operations, array $options = []) * * @deprecated 1.4 */ - public function count(array|object $filter = [], array $options = []) + public function count(array|object $filter = [], array $options = []): int { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritReadOptions($options); @@ -311,13 +300,12 @@ public function count(array|object $filter = [], array $options = []) * @see CountDocuments::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function countDocuments(array|object $filter = [], array $options = []) + public function countDocuments(array|object $filter = [], array $options = []): int { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritReadOptions($options); @@ -340,7 +328,7 @@ public function countDocuments(array|object $filter = [], array $options = []) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function createIndex(array|object $key, array $options = []) + public function createIndex(array|object $key, array $options = []): string { $operationOptionKeys = ['comment' => 1, 'commitQuorum' => 1, 'maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1]; $indexOptions = array_diff_key($options, $operationOptionKeys); @@ -376,7 +364,7 @@ public function createIndex(array|object $key, array $options = []) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function createIndexes(array $indexes, array $options = []) + public function createIndexes(array $indexes, array $options = []): array { $options = $this->inheritWriteOptions($options); @@ -450,12 +438,11 @@ public function createSearchIndexes(array $indexes, array $options = []): array * @see https://mongodb.com/docs/manual/reference/command/delete/ * @param array|object $filter Query by which to delete documents * @param array $options Command options - * @return DeleteResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function deleteMany(array|object $filter, array $options = []) + public function deleteMany(array|object $filter, array $options = []): DeleteResult { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritWriteOptions($options); @@ -472,12 +459,11 @@ public function deleteMany(array|object $filter, array $options = []) * @see https://mongodb.com/docs/manual/reference/command/delete/ * @param array|object $filter Query by which to delete documents * @param array $options Command options - * @return DeleteResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function deleteOne(array|object $filter, array $options = []) + public function deleteOne(array|object $filter, array $options = []): DeleteResult { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritWriteOptions($options); @@ -494,13 +480,12 @@ public function deleteOne(array|object $filter, array $options = []) * @param string $fieldName Field for which to return distinct values * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return array * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function distinct(string $fieldName, array|object $filter = [], array $options = []) + public function distinct(string $fieldName, array|object $filter = [], array $options = []): array { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritReadOptions($options); @@ -516,15 +501,13 @@ public function distinct(string $fieldName, array|object $filter = [], array $op * * @see DropCollection::__construct() for supported options * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function drop(array $options = []) + public function drop(array $options = []): void { $options = $this->inheritWriteOptions($options); - $options = $this->inheritTypeMap($options, __FUNCTION__); $server = select_server_for_write($this->manager, $options); @@ -537,7 +520,7 @@ public function drop(array $options = []) ? new DropEncryptedCollection($this->databaseName, $this->collectionName, $options) : new DropCollection($this->databaseName, $this->collectionName, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -546,12 +529,11 @@ public function drop(array $options = []) * @see DropIndexes::__construct() for supported options * @param string|IndexInfo $indexName Index name or model object * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropIndex(string|IndexInfo $indexName, array $options = []) + public function dropIndex(string|IndexInfo $indexName, array $options = []): void { $indexName = (string) $indexName; @@ -560,11 +542,10 @@ public function dropIndex(string|IndexInfo $indexName, array $options = []) } $options = $this->inheritWriteOptions($options); - $options = $this->inheritTypeMap($options, __FUNCTION__); $operation = new DropIndexes($this->databaseName, $this->collectionName, $indexName, $options); - return $operation->execute(select_server_for_write($this->manager, $options)); + $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -572,19 +553,17 @@ public function dropIndex(string|IndexInfo $indexName, array $options = []) * * @see DropIndexes::__construct() for supported options * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropIndexes(array $options = []) + public function dropIndexes(array $options = []): void { $options = $this->inheritWriteOptions($options); - $options = $this->inheritTypeMap($options, __FUNCTION__); $operation = new DropIndexes($this->databaseName, $this->collectionName, '*', $options); - return $operation->execute(select_server_for_write($this->manager, $options)); + $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -610,13 +589,12 @@ public function dropSearchIndex(string $name, array $options = []): void * * @see EstimatedDocumentCount::__construct() for supported options * @param array $options Command options - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function estimatedDocumentCount(array $options = []) + public function estimatedDocumentCount(array $options = []): int { $options = $this->inheritReadOptions($options); @@ -632,12 +610,11 @@ public function estimatedDocumentCount(array $options = []) * @see https://mongodb.com/docs/manual/reference/command/explain/ * @param Explainable $explainable Command on which to run explain * @param array $options Additional options - * @return array|object * @throws UnsupportedException if explainable or options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function explain(Explainable $explainable, array $options = []) + public function explain(Explainable $explainable, array $options = []): array|object { $options = $this->inheritReadPreference($options); $options = $this->inheritTypeMap($options); @@ -654,12 +631,11 @@ public function explain(Explainable $explainable, array $options = []) * @see https://mongodb.com/docs/manual/crud/#read-operations * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return CursorInterface&Iterator * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function find(array|object $filter = [], array $options = []) + public function find(array|object $filter = [], array $options = []): CursorInterface { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritReadOptions($options); @@ -677,12 +653,11 @@ public function find(array|object $filter = [], array $options = []) * @see https://mongodb.com/docs/manual/crud/#read-operations * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return array|object|null * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOne(array|object $filter = [], array $options = []) + public function findOne(array|object $filter = [], array $options = []): array|object|null { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritReadOptions($options); @@ -702,13 +677,12 @@ public function findOne(array|object $filter = [], array $options = []) * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOneAndDelete(array|object $filter, array $options = []) + public function findOneAndDelete(array|object $filter, array $options = []): array|object|null { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritWriteOptions($options); @@ -733,13 +707,12 @@ public function findOneAndDelete(array|object $filter, array $options = []) * @param array|object $filter Query by which to filter documents * @param array|object $replacement Replacement document * @param array $options Command options - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOneAndReplace(array|object $filter, array|object $replacement, array $options = []) + public function findOneAndReplace(array|object $filter, array|object $replacement, array $options = []): array|object|null { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritWriteOptions($options); @@ -764,13 +737,12 @@ public function findOneAndReplace(array|object $filter, array|object $replacemen * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOneAndUpdate(array|object $filter, array|object $update, array $options = []) + public function findOneAndUpdate(array|object $filter, array|object $update, array $options = []): array|object|null { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritWriteOptions($options); @@ -783,30 +755,24 @@ public function findOneAndUpdate(array|object $filter, array|object $update, arr /** * Return the collection name. - * - * @return string */ - public function getCollectionName() + public function getCollectionName(): string { return $this->collectionName; } /** * Return the database name. - * - * @return string */ - public function getDatabaseName() + public function getDatabaseName(): string { return $this->databaseName; } /** * Return the Manager. - * - * @return Manager */ - public function getManager() + public function getManager(): Manager { return $this->manager; } @@ -815,9 +781,8 @@ public function getManager() * Return the collection namespace. * * @see https://mongodb.com/docs/manual/reference/glossary/#term-namespace - * @return string */ - public function getNamespace() + public function getNamespace(): string { return $this->databaseName . '.' . $this->collectionName; } @@ -826,29 +791,24 @@ public function getNamespace() * Return the read concern for this collection. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this collection. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this collection. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -857,9 +817,8 @@ public function getTypeMap() * Return the write concern for this collection. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -871,11 +830,10 @@ public function getWriteConcern() * @see https://mongodb.com/docs/manual/reference/command/insert/ * @param list $documents The documents to insert * @param array $options Command options - * @return InsertManyResult * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function insertMany(array $documents, array $options = []) + public function insertMany(array $documents, array $options = []): InsertManyResult { $options = $this->inheritWriteOptions($options); $options = $this->inheritCodec($options); @@ -892,11 +850,10 @@ public function insertMany(array $documents, array $options = []) * @see https://mongodb.com/docs/manual/reference/command/insert/ * @param array|object $document The document to insert * @param array $options Command options - * @return InsertOneResult * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function insertOne(array|object $document, array $options = []) + public function insertOne(array|object $document, array $options = []): InsertOneResult { $options = $this->inheritWriteOptions($options); $options = $this->inheritCodec($options); @@ -914,7 +871,7 @@ public function insertOne(array|object $document, array $options = []) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listIndexes(array $options = []) + public function listIndexes(array $options = []): Iterator { $operation = new ListIndexes($this->databaseName, $this->collectionName, $options); @@ -941,49 +898,6 @@ public function listSearchIndexes(array $options = []): Iterator return $operation->execute($server); } - /** - * Executes a map-reduce aggregation on the collection. - * - * @see MapReduce::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/mapReduce/ - * @param JavascriptInterface $map Map function - * @param JavascriptInterface $reduce Reduce function - * @param string|array|object $out Output specification - * @param array $options Command options - * @return MapReduceResult - * @throws UnsupportedException if options are not supported by the selected server - * @throws InvalidArgumentException for parameter/option parsing errors - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - * @throws UnexpectedValueException if the command response was malformed - */ - public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, string|array|object $out, array $options = []) - { - @trigger_error(sprintf('The %s method is deprecated and will be removed in a version 2.0.', __METHOD__), E_USER_DEPRECATED); - - $hasOutputCollection = ! is_mapreduce_output_inline($out); - - // Check if the out option is inline because we will want to coerce a primary read preference if not - if ($hasOutputCollection) { - $options['readPreference'] = new ReadPreference(ReadPreference::PRIMARY); - } else { - $options = $this->inheritReadPreference($options); - } - - /* A "majority" read concern is not compatible with inline output, so - * avoid providing the Collection's read concern if it would conflict. - */ - if (! $hasOutputCollection || $this->readConcern->getLevel() !== ReadConcern::MAJORITY) { - $options = $this->inheritReadConcern($options); - } - - $options = $this->inheritWriteOptions($options); - $options = $this->inheritTypeMap($options); - - $operation = new MapReduce($this->databaseName, $this->collectionName, $map, $reduce, $out, $options); - - return $operation->execute(select_server_for_write($this->manager, $options)); - } - /** * Renames the collection. * @@ -991,23 +905,21 @@ public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, * @param string $toCollectionName New name of the collection * @param string|null $toDatabaseName New database name of the collection. Defaults to the original database. * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = []) + public function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = []): void { if (! isset($toDatabaseName)) { $toDatabaseName = $this->databaseName; } $options = $this->inheritWriteOptions($options); - $options = $this->inheritTypeMap($options); $operation = new RenameCollection($this->databaseName, $this->collectionName, $toDatabaseName, $toCollectionName, $options); - return $operation->execute(select_server_for_write($this->manager, $options)); + $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -1018,12 +930,11 @@ public function rename(string $toCollectionName, ?string $toDatabaseName = null, * @param array|object $filter Query by which to filter documents * @param array|object $replacement Replacement document * @param array $options Command options - * @return UpdateResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function replaceOne(array|object $filter, array|object $replacement, array $options = []) + public function replaceOne(array|object $filter, array|object $replacement, array $options = []): UpdateResult { $filter = $this->builderEncoder->encodeIfSupported($filter); $options = $this->inheritWriteOptions($options); @@ -1042,12 +953,11 @@ public function replaceOne(array|object $filter, array|object $replacement, arra * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched documents * @param array $options Command options - * @return UpdateResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function updateMany(array|object $filter, array|object $update, array $options = []) + public function updateMany(array|object $filter, array|object $update, array $options = []): UpdateResult { $filter = $this->builderEncoder->encodeIfSupported($filter); $update = $this->builderEncoder->encodeIfSupported($update); @@ -1066,12 +976,11 @@ public function updateMany(array|object $filter, array|object $update, array $op * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options - * @return UpdateResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function updateOne(array|object $filter, array|object $update, array $options = []) + public function updateOne(array|object $filter, array|object $update, array $options = []): UpdateResult { $filter = $this->builderEncoder->encodeIfSupported($filter); $update = $this->builderEncoder->encodeIfSupported($update); @@ -1107,10 +1016,9 @@ public function updateSearchIndex(string $name, array|object $definition, array * @see Watch::__construct() for supported options * @param array $pipeline Aggregation pipeline * @param array $options Command options - * @return ChangeStream * @throws InvalidArgumentException for parameter/option parsing errors */ - public function watch(array $pipeline = [], array $options = []) + public function watch(array $pipeline = [], array $options = []): ChangeStream { if (is_builder_pipeline($pipeline)) { $pipeline = new Pipeline(...$pipeline); @@ -1131,10 +1039,9 @@ public function watch(array $pipeline = [], array $options = []) * * @see Collection::__construct() for supported options * @param array $options Collection constructor options - * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function withOptions(array $options = []) + public function withOptions(array $options = []): Collection { $options += [ 'builderEncoder' => $this->builderEncoder, @@ -1214,12 +1121,8 @@ private function inheritReadPreference(array $options): array return $options; } - private function inheritTypeMap(array $options, ?string $deprecatedFunction = null): array + private function inheritTypeMap(array $options): array { - if ($deprecatedFunction !== null && isset($options['typeMap'])) { - @trigger_error(sprintf('The function %s() will return nothing in mongodb/mongodb v2.0, the "typeMap" option is deprecated', $deprecatedFunction), E_USER_DEPRECATED); - } - // Only inherit the type map if no codec is used if (! isset($options['typeMap']) && ! isset($options['codec'])) { $options['typeMap'] = $this->typeMap; diff --git a/src/Command/ListCollections.php b/src/Command/ListCollections.php index 454e145f3..d42250cd3 100644 --- a/src/Command/ListCollections.php +++ b/src/Command/ListCollections.php @@ -18,13 +18,11 @@ namespace MongoDB\Command; use MongoDB\Driver\Command; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\CachingIterator; -use MongoDB\Operation\Executable; use function is_bool; use function is_integer; @@ -36,7 +34,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ -class ListCollections implements Executable +final class ListCollections { /** * Constructs a listCollections command. @@ -93,17 +91,16 @@ public function __construct(private string $databaseName, private array $options /** * Execute the operation. * - * @return CachingIterator - * @see Executable::execute() + * @return CursorInterface * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server): CachingIterator + public function execute(Server $server): CursorInterface { - /** @var Cursor $cursor */ + /** @var CursorInterface $cursor */ $cursor = $server->executeReadCommand($this->databaseName, $this->createCommand(), $this->createOptions()); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - return new CachingIterator($cursor); + return $cursor; } /** diff --git a/src/Command/ListDatabases.php b/src/Command/ListDatabases.php index 5386559db..31ca5f741 100644 --- a/src/Command/ListDatabases.php +++ b/src/Command/ListDatabases.php @@ -23,7 +23,6 @@ use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; -use MongoDB\Operation\Executable; use function current; use function is_array; @@ -37,7 +36,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/listDatabases/ */ -class ListDatabases implements Executable +final class ListDatabases { /** * Constructs a listDatabases command. @@ -93,7 +92,6 @@ public function __construct(private array $options = []) /** * Execute the operation. * - * @see Executable::execute() * @return array An array of database info structures * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -102,6 +100,7 @@ public function execute(Server $server): array { $cursor = $server->executeReadCommand('admin', $this->createCommand(), $this->createOptions()); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + $result = current($cursor->toArray()); if (! isset($result['databases']) || ! is_array($result['databases'])) { diff --git a/src/Database.php b/src/Database.php index 0561ea943..08817dbba 100644 --- a/src/Database.php +++ b/src/Database.php @@ -24,7 +24,7 @@ use MongoDB\Builder\Pipeline; use MongoDB\Codec\Encoder; use MongoDB\Driver\ClientEncryption; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Manager; use MongoDB\Driver\ReadConcern; @@ -52,14 +52,9 @@ use MongoDB\Operation\Watch; use stdClass; use Throwable; -use Traversable; use function is_array; -use function sprintf; use function strlen; -use function trigger_error; - -use const E_USER_DEPRECATED; class Database { @@ -149,9 +144,8 @@ public function __construct(private Manager $manager, private string $databaseNa * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'builderEncoder' => $this->builderEncoder, @@ -174,19 +168,16 @@ public function __debugInfo() * @see https://php.net/oop5.overloading#object.get * @see https://php.net/types.string#language.types.string.parsing.complex * @param string $collectionName Name of the collection to select - * @return Collection */ - public function __get(string $collectionName) + public function __get(string $collectionName): Collection { return $this->selectCollection($collectionName); } /** * Return the database name. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->databaseName; } @@ -199,13 +190,12 @@ public function __toString() * @see Aggregate::__construct() for supported options * @param array $pipeline Aggregation pipeline * @param array $options Command options - * @return Traversable * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function aggregate(array $pipeline, array $options = []) + public function aggregate(array $pipeline, array $options = []): CursorInterface { if (is_builder_pipeline($pipeline)) { $pipeline = new Pipeline(...$pipeline); @@ -255,11 +245,10 @@ public function aggregate(array $pipeline, array $options = []) * @see DatabaseCommand::__construct() for supported options * @param array|object $command Command document * @param array $options Options for command execution - * @return Cursor * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function command(array|object $command, array $options = []) + public function command(array|object $command, array $options = []): CursorInterface { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; @@ -281,19 +270,12 @@ public function command(array|object $command, array $options = []) * @see CreateCollection::__construct() for supported options * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#create-collection-helper * @see https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/manage-collections/ - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function createCollection(string $collectionName, array $options = []) + public function createCollection(string $collectionName, array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } else { - @trigger_error(sprintf('The function %s() will return nothing in mongodb/mongodb v2.0, the "typeMap" option is deprecated', __FUNCTION__), E_USER_DEPRECATED); - } - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -308,7 +290,7 @@ public function createCollection(string $collectionName, array $options = []) $server = select_server_for_write($this->manager, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -326,19 +308,13 @@ public function createCollection(string $collectionName, array $options = []) * getPrevious() and getEncryptedFields() methods, respectively. * * @see CreateCollection::__construct() for supported options - * @return array A tuple containing the command result document from creating the collection and the modified "encryptedFields" option + * @return array The modified "encryptedFields" option * @throws InvalidArgumentException for parameter/option parsing errors * @throws CreateEncryptedCollectionException for any errors creating data keys or creating the collection * @throws UnsupportedException if Queryable Encryption is not supported by the selected server */ public function createEncryptedCollection(string $collectionName, ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, array $options): array { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } else { - @trigger_error(sprintf('The function %s() will return nothing in mongodb/mongodb v2.0, the "typeMap" option is deprecated', __FUNCTION__), E_USER_DEPRECATED); - } - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } @@ -347,10 +323,10 @@ public function createEncryptedCollection(string $collectionName, ClientEncrypti $server = select_server_for_write($this->manager, $options); try { - $operation->createDataKeys($clientEncryption, $kmsProvider, $masterKey, $encryptedFields); - $result = $operation->execute($server); + $encryptedFields = $operation->createDataKeys($clientEncryption, $kmsProvider, $masterKey); + $operation->execute($server); - return [$result, $encryptedFields]; + return $encryptedFields; } catch (Throwable $e) { throw new CreateEncryptedCollectionException($e, $encryptedFields ?? []); } @@ -361,19 +337,12 @@ public function createEncryptedCollection(string $collectionName, ClientEncrypti * * @see DropDatabase::__construct() for supported options * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function drop(array $options = []) + public function drop(array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } else { - @trigger_error(sprintf('The function %s() will return nothing in mongodb/mongodb v2.0, the "typeMap" option is deprecated', __FUNCTION__), E_USER_DEPRECATED); - } - $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { @@ -382,7 +351,7 @@ public function drop(array $options = []) $operation = new DropDatabase($this->databaseName, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -391,19 +360,12 @@ public function drop(array $options = []) * @see DropCollection::__construct() for supported options * @param string $collectionName Collection name * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropCollection(string $collectionName, array $options = []) + public function dropCollection(string $collectionName, array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } else { - @trigger_error(sprintf('The function %s() will return nothing in mongodb/mongodb v2.0, the "typeMap" option is deprecated', __FUNCTION__), E_USER_DEPRECATED); - } - $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { @@ -419,25 +381,21 @@ public function dropCollection(string $collectionName, array $options = []) ? new DropEncryptedCollection($this->databaseName, $collectionName, $options) : new DropCollection($this->databaseName, $collectionName, $options); - return $operation->execute($server); + $operation->execute($server); } /** * Returns the database name. - * - * @return string */ - public function getDatabaseName() + public function getDatabaseName(): string { return $this->databaseName; } /** * Return the Manager. - * - * @return Manager */ - public function getManager() + public function getManager(): Manager { return $this->manager; } @@ -446,29 +404,24 @@ public function getManager() * Return the read concern for this database. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this database. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this database. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -477,9 +430,8 @@ public function getTypeMap() * Return the write concern for this database. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -488,6 +440,7 @@ public function getWriteConcern() * Returns the names of all collections in this database * * @see ListCollectionNames::__construct() for supported options + * @return Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ @@ -507,7 +460,7 @@ public function listCollectionNames(array $options = []): Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listCollections(array $options = []) + public function listCollections(array $options = []): Iterator { $operation = new ListCollections($this->databaseName, $options); $server = select_server($this->manager, $options); @@ -522,11 +475,10 @@ public function listCollections(array $options = []) * @param string $collectionName Collection or view to modify * @param array $collectionOptions Collection or view options to assign * @param array $options Command options - * @return array|object * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function modifyCollection(string $collectionName, array $collectionOptions, array $options = []) + public function modifyCollection(string $collectionName, array $collectionOptions, array $options = []): array|object { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; @@ -551,21 +503,16 @@ public function modifyCollection(string $collectionName, array $collectionOption * @param string $toCollectionName New name of the collection * @param string|null $toDatabaseName New database name of the collection. Defaults to the original database. * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = []) + public function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = []): void { if (! isset($toDatabaseName)) { $toDatabaseName = $this->databaseName; } - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { @@ -574,7 +521,7 @@ public function renameCollection(string $fromCollectionName, string $toCollectio $operation = new RenameCollection($this->databaseName, $fromCollectionName, $toDatabaseName, $toCollectionName, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -583,10 +530,9 @@ public function renameCollection(string $fromCollectionName, string $toCollectio * @see Collection::__construct() for supported options * @param string $collectionName Name of the collection to select * @param array $options Collection constructor options - * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectCollection(string $collectionName, array $options = []) + public function selectCollection(string $collectionName, array $options = []): Collection { $options += [ 'builderEncoder' => $this->builderEncoder, @@ -604,10 +550,9 @@ public function selectCollection(string $collectionName, array $options = []) * * @see Bucket::__construct() for supported options * @param array $options Bucket constructor options - * @return Bucket * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectGridFSBucket(array $options = []) + public function selectGridFSBucket(array $options = []): Bucket { $options += [ 'readConcern' => $this->readConcern, @@ -625,10 +570,9 @@ public function selectGridFSBucket(array $options = []) * @see Watch::__construct() for supported options * @param array $pipeline Aggregation pipeline * @param array $options Command options - * @return ChangeStream * @throws InvalidArgumentException for parameter/option parsing errors */ - public function watch(array $pipeline = [], array $options = []) + public function watch(array $pipeline = [], array $options = []): ChangeStream { if (is_builder_pipeline($pipeline)) { $pipeline = new Pipeline(...$pipeline); @@ -660,10 +604,9 @@ public function watch(array $pipeline = [], array $options = []) * * @see Database::__construct() for supported options * @param array $options Database constructor options - * @return Database * @throws InvalidArgumentException for parameter/option parsing errors */ - public function withOptions(array $options = []) + public function withOptions(array $options = []): Database { $options += [ 'readConcern' => $this->readConcern, diff --git a/src/DeleteResult.php b/src/DeleteResult.php index 56f3f072d..23b416d0a 100644 --- a/src/DeleteResult.php +++ b/src/DeleteResult.php @@ -17,19 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a delete operation. */ class DeleteResult { - private bool $isAcknowledged; - public function __construct(private WriteResult $writeResult) { - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -38,16 +35,11 @@ public function __construct(private WriteResult $writeResult) * This method should only be called if the write was acknowledged. * * @see DeleteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getDeletedCount() + public function getDeletedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getDeletedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getDeletedCount(); } /** @@ -55,11 +47,9 @@ public function getDeletedCount() * * If the delete was not acknowledged, other fields from the WriteResult * (e.g. deletedCount) will be undefined. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { - return $this->isAcknowledged; + return $this->writeResult->isAcknowledged(); } } diff --git a/src/Exception/BadMethodCallException.php b/src/Exception/BadMethodCallException.php index e1070982a..00cece346 100644 --- a/src/Exception/BadMethodCallException.php +++ b/src/Exception/BadMethodCallException.php @@ -27,21 +27,10 @@ class BadMethodCallException extends BaseBadMethodCallException implements Excep * Thrown when a mutable method is invoked on an immutable object. * * @param string $class Class name - * @return self + * @internal */ - public static function classIsImmutable(string $class) + public static function classIsImmutable(string $class): self { return new self(sprintf('%s is immutable', $class)); } - - /** - * Thrown when accessing a result field on an unacknowledged write result. - * - * @param string $method Method name - * @return self - */ - public static function unacknowledgedWriteResultAccess(string $method) - { - return new self(sprintf('%s should not be called for an unacknowledged write result', $method)); - } } diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index 115f6a5c7..2a9bc30b9 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -29,6 +29,7 @@ class InvalidArgumentException extends DriverInvalidArgumentException implements Exception { + /** @internal */ public static function cannotCombineCodecAndTypeMap(): self { return new self('Cannot provide both "codec" and "typeMap" options'); @@ -39,6 +40,7 @@ public static function cannotCombineCodecAndTypeMap(): self * * @param string $name Name of the argument or option * @param mixed $value Actual value (used to derive the type) + * @internal */ public static function expectedDocumentType(string $name, mixed $value): self { @@ -51,9 +53,9 @@ public static function expectedDocumentType(string $name, mixed $value): self * @param string $name Name of the argument or option * @param mixed $value Actual value (used to derive the type) * @param string|list $expectedType Expected type as a string or an array containing one or more strings - * @return self + * @internal */ - public static function invalidType(string $name, mixed $value, string|array $expectedType) + public static function invalidType(string $name, mixed $value, string|array $expectedType): self { if (is_array($expectedType)) { $expectedType = self::expectedTypesToString($expectedType); diff --git a/src/Exception/ResumeTokenException.php b/src/Exception/ResumeTokenException.php index fc8808271..9117f700d 100644 --- a/src/Exception/ResumeTokenException.php +++ b/src/Exception/ResumeTokenException.php @@ -26,9 +26,9 @@ class ResumeTokenException extends RuntimeException * Thrown when a resume token has an invalid type. * * @param mixed $value Actual value (used to derive the type) - * @return self + * @internal */ - public static function invalidType(mixed $value) + public static function invalidType(mixed $value): self { return new self(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value))); } @@ -36,9 +36,9 @@ public static function invalidType(mixed $value) /** * Thrown when a resume token is not found in a change document. * - * @return self + * @internal */ - public static function notFound() + public static function notFound(): self { return new self('Resume token not found in change document'); } diff --git a/src/Exception/UnsupportedException.php b/src/Exception/UnsupportedException.php index 2bf910a09..1a5273715 100644 --- a/src/Exception/UnsupportedException.php +++ b/src/Exception/UnsupportedException.php @@ -19,109 +19,43 @@ class UnsupportedException extends RuntimeException { - /** - * Thrown when a command's allowDiskUse option is not supported by a server. - * - * @return self - */ - public static function allowDiskUseNotSupported() - { - return new self('The "allowDiskUse" option is not supported by the server executing this operation'); - } - - /** - * Thrown when array filters are not supported by a server. - * - * @deprecated 1.12 - * @todo Remove this in 2.0 (see: PHPLIB-797) - * - * @return self - */ - public static function arrayFiltersNotSupported() - { - return new self('Array filters are not supported by the server executing this operation'); - } - - /** - * Thrown when collations are not supported by a server. - * - * @deprecated 1.12 - * @todo Remove this in 2.0 (see: PHPLIB-797) - * - * @return self - */ - public static function collationNotSupported() - { - return new self('Collations are not supported by the server executing this operation'); - } - /** * Thrown when the commitQuorum option for createIndexes is not supported * by a server. * - * @return self + * @internal */ - public static function commitQuorumNotSupported() + public static function commitQuorumNotSupported(): self { return new self('The "commitQuorum" option is not supported by the server executing this operation'); } - /** - * Thrown when explain is not supported by a server. - * - * @return self - */ - public static function explainNotSupported() - { - return new self('Explain is not supported by the server executing this operation'); - } - /** * Thrown when a command's hint option is not supported by a server. * - * @return self + * @internal */ - public static function hintNotSupported() + public static function hintNotSupported(): self { return new self('Hint is not supported by the server executing this operation'); } - /** - * Thrown when a command's readConcern option is not supported by a server. - * - * @return self - */ - public static function readConcernNotSupported() - { - return new self('Read concern is not supported by the server executing this command'); - } - /** * Thrown when a readConcern is used with a read operation in a transaction. * - * @return self + * @internal */ - public static function readConcernNotSupportedInTransaction() + public static function readConcernNotSupportedInTransaction(): self { return new self('The "readConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.'); } - /** - * Thrown when a command's writeConcern option is not supported by a server. - * - * @return self - */ - public static function writeConcernNotSupported() - { - return new self('Write concern is not supported by the server executing this command'); - } - /** * Thrown when a writeConcern is used with a write operation in a transaction. * - * @return self + * @internal */ - public static function writeConcernNotSupportedInTransaction() + public static function writeConcernNotSupportedInTransaction(): self { return new self('The "writeConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.'); } diff --git a/src/Exception/UnsupportedValueException.php b/src/Exception/UnsupportedValueException.php index d744baa13..62e089e5e 100644 --- a/src/Exception/UnsupportedValueException.php +++ b/src/Exception/UnsupportedValueException.php @@ -24,8 +24,7 @@ class UnsupportedValueException extends InvalidArgumentException implements Exception { - /** @return mixed */ - public function getValue() + public function getValue(): mixed { return $this->value; } diff --git a/src/GridFS/Bucket.php b/src/GridFS/Bucket.php index 56a4e26e2..234af8954 100644 --- a/src/GridFS/Bucket.php +++ b/src/GridFS/Bucket.php @@ -17,7 +17,6 @@ namespace MongoDB\GridFS; -use Iterator; use MongoDB\BSON\Document; use MongoDB\Codec\DocumentCodec; use MongoDB\Collection; @@ -45,7 +44,6 @@ use function get_resource_type; use function in_array; use function is_array; -use function is_bool; use function is_integer; use function is_object; use function is_resource; @@ -59,11 +57,8 @@ use function stream_copy_to_stream; use function stream_get_meta_data; use function stream_get_wrappers; -use function trigger_error; use function urlencode; -use const E_USER_DEPRECATED; - /** * Bucket provides a public API for interacting with the GridFS files and chunks * collections. @@ -88,8 +83,6 @@ class Bucket private string $bucketName; - private bool $disableMD5; - private int $chunkSizeBytes; private ReadConcern $readConcern; @@ -111,9 +104,6 @@ class Bucket * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to * 261120 (i.e. 255 KiB). * - * * disableMD5 (boolean): When true, no MD5 sum will be generated for - * each stored file. Defaults to "false". - * * * readConcern (MongoDB\Driver\ReadConcern): Read concern. * * * readPreference (MongoDB\Driver\ReadPreference): Read preference. @@ -129,14 +119,9 @@ class Bucket */ public function __construct(private Manager $manager, private string $databaseName, array $options = []) { - if (isset($options['disableMD5']) && $options['disableMD5'] === false) { - @trigger_error('Setting GridFS "disableMD5" option to "false" is deprecated since mongodb/mongodb 1.18 and will not be supported in version 2.0.', E_USER_DEPRECATED); - } - $options += [ 'bucketName' => self::DEFAULT_BUCKET_NAME, 'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES, - 'disableMD5' => false, ]; if (! is_string($options['bucketName'])) { @@ -155,10 +140,6 @@ public function __construct(private Manager $manager, private string $databaseNa throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); } - if (! is_bool($options['disableMD5'])) { - throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean'); - } - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); } @@ -182,7 +163,6 @@ public function __construct(private Manager $manager, private string $databaseNa $this->bucketName = $options['bucketName']; $this->chunkSizeBytes = $options['chunkSizeBytes']; $this->codec = $options['codec'] ?? null; - $this->disableMD5 = $options['disableMD5']; $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); $this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP; @@ -203,15 +183,13 @@ public function __construct(private Manager $manager, private string $databaseNa * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'bucketName' => $this->bucketName, 'codec' => $this->codec, 'databaseName' => $this->databaseName, - 'disableMD5' => $this->disableMD5, 'manager' => $this->manager, 'chunkSizeBytes' => $this->chunkSizeBytes, 'readConcern' => $this->readConcern, @@ -228,11 +206,10 @@ public function __debugInfo() * attempt to delete orphaned chunks. * * @param mixed $id File ID - * @return void * @throws FileNotFoundException if no file could be selected * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function delete(mixed $id) + public function delete(mixed $id): void { $file = $this->collectionWrapper->findFileById($id); $this->collectionWrapper->deleteFileAndChunksById($id); @@ -247,13 +224,12 @@ public function delete(mixed $id) * * @param mixed $id File ID * @param resource $destination Writable Stream - * @return void * @throws FileNotFoundException if no file could be selected * @throws InvalidArgumentException if $destination is not a stream * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function downloadToStream(mixed $id, $destination) + public function downloadToStream(mixed $id, $destination): void { if (! is_resource($destination) || get_resource_type($destination) != 'stream') { throw InvalidArgumentException::invalidType('$destination', $destination, 'resource'); @@ -287,13 +263,12 @@ public function downloadToStream(mixed $id, $destination) * @param string $filename Filename * @param resource $destination Writable Stream * @param array $options Download options - * @return void * @throws FileNotFoundException if no file could be selected * @throws InvalidArgumentException if $destination is not a stream * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function downloadToStreamByName(string $filename, $destination, array $options = []) + public function downloadToStreamByName(string $filename, $destination, array $options = []): void { if (! is_resource($destination) || get_resource_type($destination) != 'stream') { throw InvalidArgumentException::invalidType('$destination', $destination, 'resource'); @@ -309,10 +284,9 @@ public function downloadToStreamByName(string $filename, $destination, array $op * Drops the files and chunks collections associated with this GridFS * bucket. * - * @return void * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function drop() + public function drop(): void { $this->collectionWrapper->dropCollections(); } @@ -324,12 +298,11 @@ public function drop() * @see Find::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return CursorInterface&Iterator * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function find(array|object $filter = [], array $options = []) + public function find(array|object $filter = [], array $options = []): CursorInterface { if ($this->codec && ! array_key_exists('codec', $options)) { $options['codec'] = $this->codec; @@ -345,12 +318,11 @@ public function find(array|object $filter = [], array $options = []) * @see FindOne::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return array|object|null * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOne(array|object $filter = [], array $options = []) + public function findOne(array|object $filter = [], array $options = []): array|object|null { if ($this->codec && ! array_key_exists('codec', $options)) { $options['codec'] = $this->codec; @@ -361,40 +333,32 @@ public function findOne(array|object $filter = [], array $options = []) /** * Return the bucket name. - * - * @return string */ - public function getBucketName() + public function getBucketName(): string { return $this->bucketName; } /** * Return the chunks collection. - * - * @return Collection */ - public function getChunksCollection() + public function getChunksCollection(): Collection { return $this->collectionWrapper->getChunksCollection(); } /** * Return the chunk size in bytes. - * - * @return integer */ - public function getChunkSizeBytes() + public function getChunkSizeBytes(): int { return $this->chunkSizeBytes; } /** * Return the database name. - * - * @return string */ - public function getDatabaseName() + public function getDatabaseName(): string { return $this->databaseName; } @@ -403,11 +367,10 @@ public function getDatabaseName() * Gets the file document of the GridFS file associated with a stream. * * @param resource $stream GridFS stream - * @return array|object * @throws InvalidArgumentException if $stream is not a GridFS stream * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function getFileDocumentForStream($stream) + public function getFileDocumentForStream($stream): array|object { $file = $this->getRawFileDocumentForStream($stream); @@ -423,12 +386,11 @@ public function getFileDocumentForStream($stream) * Gets the file document's ID of the GridFS file associated with a stream. * * @param resource $stream GridFS stream - * @return mixed * @throws CorruptFileException if the file "_id" field does not exist * @throws InvalidArgumentException if $stream is not a GridFS stream * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function getFileIdForStream($stream) + public function getFileIdForStream($stream): mixed { $file = $this->getRawFileDocumentForStream($stream); @@ -448,10 +410,8 @@ public function getFileIdForStream($stream) /** * Return the files collection. - * - * @return Collection */ - public function getFilesCollection() + public function getFilesCollection(): Collection { return $this->collectionWrapper->getFilesCollection(); } @@ -460,29 +420,24 @@ public function getFilesCollection() * Return the read concern for this GridFS bucket. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this GridFS bucket. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this GridFS bucket. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -491,9 +446,8 @@ public function getTypeMap() * Return the write concern for this GridFS bucket. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -565,9 +519,6 @@ public function openDownloadStreamByName(string $filename, array $options = []) * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to the * bucket's chunk size. * - * * disableMD5 (boolean): When true, no MD5 sum will be generated for - * the stored file. Defaults to "false". - * * * metadata (document): User data for the "metadata" field of the files * collection document. * @@ -579,7 +530,6 @@ public function openUploadStream(string $filename, array $options = []) { $options += [ 'chunkSizeBytes' => $this->chunkSizeBytes, - 'disableMD5' => $this->disableMD5, ]; $path = $this->createPathForUpload(); @@ -622,11 +572,10 @@ public function registerGlobalStreamWrapperAlias(string $alias): void * * @param mixed $id File ID * @param string $newFilename New filename - * @return void * @throws FileNotFoundException if no file could be selected * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function rename(mixed $id, string $newFilename) + public function rename(mixed $id, string $newFilename): void { $updateResult = $this->collectionWrapper->updateFilenameForId($id, $newFilename); @@ -634,16 +583,9 @@ public function rename(mixed $id, string $newFilename) return; } - /* If the update resulted in no modification, it's possible that the - * file did not exist, in which case we must raise an error. Checking - * the write result's matched count will be most efficient, but fall - * back to a findOne operation if necessary (i.e. legacy writes). - */ - $found = $updateResult->getMatchedCount() !== null - ? $updateResult->getMatchedCount() === 1 - : $this->collectionWrapper->findFileById($id) !== null; - - if (! $found) { + // If the update resulted in no modification, it's possible that the + // file did not exist, in which case we must raise an error. + if ($updateResult->getMatchedCount() !== 1) { throw FileNotFoundException::byId($id, $this->getFilesNamespace()); } } @@ -658,9 +600,6 @@ public function rename(mixed $id, string $newFilename) * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to the * bucket's chunk size. * - * * disableMD5 (boolean): When true, no MD5 sum will be generated for - * the stored file. Defaults to "false". - * * * metadata (document): User data for the "metadata" field of the files * collection document. * @@ -672,7 +611,7 @@ public function rename(mixed $id, string $newFilename) * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function uploadFromStream(string $filename, $source, array $options = []) + public function uploadFromStream(string $filename, $source, array $options = []): mixed { if (! is_resource($source) || get_resource_type($source) != 'stream') { throw InvalidArgumentException::invalidType('$source', $source, 'resource'); @@ -792,9 +731,9 @@ private function registerStreamWrapper(): void * * @see StreamWrapper::setContextResolver() * - * @param string $path The full url provided to fopen(). It contains the filename. - * gridfs://database_name/collection_name.files/file_name - * @param array{revision?: int, chunkSizeBytes?: int, disableMD5?: bool} $context The options provided to fopen() + * @param string $path The full url provided to fopen(). It contains the filename. + * gridfs://database_name/collection_name.files/file_name + * @param array{revision?: int, chunkSizeBytes?: int} $context The options provided to fopen() * * @return array{collectionWrapper: CollectionWrapper, file: object}|array{collectionWrapper: CollectionWrapper, filename: string, options: array} * @@ -825,7 +764,6 @@ private function resolveStreamContext(string $path, string $mode, array $context 'filename' => $filename, 'options' => $context + [ 'chunkSizeBytes' => $this->chunkSizeBytes, - 'disableMD5' => $this->disableMD5, ], ]; } diff --git a/src/GridFS/CollectionWrapper.php b/src/GridFS/CollectionWrapper.php index 0c6b9b96c..5941992ef 100644 --- a/src/GridFS/CollectionWrapper.php +++ b/src/GridFS/CollectionWrapper.php @@ -18,7 +18,6 @@ namespace MongoDB\GridFS; use ArrayIterator; -use Iterator; use MongoDB\Collection; use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Manager; @@ -39,7 +38,7 @@ * * @internal */ -class CollectionWrapper +final class CollectionWrapper { private Collection $chunksCollection; @@ -74,7 +73,7 @@ public function deleteChunksByFilesId(mixed $id): void /** * Delete all GridFS files and chunks for a given filename. */ - public function deleteFileAndChunksByFilename(string $filename): ?int + public function deleteFileAndChunksByFilename(string $filename): int { /** @var iterable $files */ $files = $this->findFiles(['filename' => $filename], [ @@ -117,9 +116,8 @@ public function dropCollections(): void * * @param mixed $id File ID * @param integer $fromChunk Starting chunk (inclusive) - * @return CursorInterface&Iterator */ - public function findChunksByFileId(mixed $id, int $fromChunk = 0) + public function findChunksByFileId(mixed $id, int $fromChunk = 0): CursorInterface { return $this->chunksCollection->find( [ @@ -194,9 +192,8 @@ public function findFileById(mixed $id): ?object * @see Find::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return CursorInterface&Iterator */ - public function findFiles(array|object $filter, array $options = []) + public function findFiles(array|object $filter, array $options = []): CursorInterface { return $this->filesCollection->find($filter, $options); } @@ -206,9 +203,8 @@ public function findFiles(array|object $filter, array $options = []) * * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return array|object|null */ - public function findOneFile(array|object $filter, array $options = []) + public function findOneFile(array|object $filter, array $options = []): array|object|null { return $this->filesCollection->findOne($filter, $options); } @@ -266,7 +262,7 @@ public function insertFile(array|object $file): void /** * Updates the filename field in the file document for all the files with a given filename. */ - public function updateFilenameForFilename(string $filename, string $newFilename): ?int + public function updateFilenameForFilename(string $filename, string $newFilename): int { return $this->filesCollection->updateMany( ['filename' => $filename], diff --git a/src/GridFS/Exception/CorruptFileException.php b/src/GridFS/Exception/CorruptFileException.php index db1622908..b9feef87d 100644 --- a/src/GridFS/Exception/CorruptFileException.php +++ b/src/GridFS/Exception/CorruptFileException.php @@ -25,6 +25,8 @@ class CorruptFileException extends RuntimeException { /** * Thrown when a chunk doesn't contain valid data. + * + * @internal */ public static function invalidChunkData(int $chunkIndex): self { @@ -35,9 +37,9 @@ public static function invalidChunkData(int $chunkIndex): self * Thrown when a chunk is not found for an expected index. * * @param integer $expectedIndex Expected index number - * @return self + * @internal */ - public static function missingChunk(int $expectedIndex) + public static function missingChunk(int $expectedIndex): self { return new self(sprintf('Chunk not found for index "%d"', $expectedIndex)); } @@ -47,9 +49,9 @@ public static function missingChunk(int $expectedIndex) * * @param integer $index Actual index number (i.e. "n" field) * @param integer $expectedIndex Expected index number - * @return self + * @internal */ - public static function unexpectedIndex(int $index, int $expectedIndex) + public static function unexpectedIndex(int $index, int $expectedIndex): self { return new self(sprintf('Expected chunk to have index "%d" but found "%d"', $expectedIndex, $index)); } @@ -59,9 +61,9 @@ public static function unexpectedIndex(int $index, int $expectedIndex) * * @param integer $size Actual size (i.e. "data" field length) * @param integer $expectedSize Expected size - * @return self + * @internal */ - public static function unexpectedSize(int $size, int $expectedSize) + public static function unexpectedSize(int $size, int $expectedSize): self { return new self(sprintf('Expected chunk to have size "%d" but found "%d"', $expectedSize, $size)); } diff --git a/src/GridFS/Exception/FileNotFoundException.php b/src/GridFS/Exception/FileNotFoundException.php index 99c6d854a..4cf153c08 100644 --- a/src/GridFS/Exception/FileNotFoundException.php +++ b/src/GridFS/Exception/FileNotFoundException.php @@ -28,9 +28,9 @@ class FileNotFoundException extends RuntimeException * Thrown when a file cannot be found by its filename. * * @param string $filename Filename - * @return self + * @internal */ - public static function byFilename(string $filename) + public static function byFilename(string $filename): self { return new self(sprintf('File with name "%s" not found', $filename)); } @@ -41,9 +41,9 @@ public static function byFilename(string $filename) * @param string $filename Filename * @param integer $revision Revision * @param string $namespace Namespace for the files collection - * @return self + * @internal */ - public static function byFilenameAndRevision(string $filename, int $revision, string $namespace) + public static function byFilenameAndRevision(string $filename, int $revision, string $namespace): self { return new self(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace)); } @@ -53,9 +53,9 @@ public static function byFilenameAndRevision(string $filename, int $revision, st * * @param mixed $id File ID * @param string $namespace Namespace for the files collection - * @return self + * @internal */ - public static function byId(mixed $id, string $namespace) + public static function byId(mixed $id, string $namespace): self { $json = Document::fromPHP(['_id' => $id])->toRelaxedExtendedJSON(); diff --git a/src/GridFS/Exception/StreamException.php b/src/GridFS/Exception/StreamException.php index 880182588..3fd75ca8c 100644 --- a/src/GridFS/Exception/StreamException.php +++ b/src/GridFS/Exception/StreamException.php @@ -13,6 +13,7 @@ class StreamException extends RuntimeException /** * @param resource $source * @param resource $destination + * @internal */ public static function downloadFromFilenameFailed(string $filename, $source, $destination): self { @@ -25,6 +26,7 @@ public static function downloadFromFilenameFailed(string $filename, $source, $de /** * @param resource $source * @param resource $destination + * @internal */ public static function downloadFromIdFailed(mixed $id, $source, $destination): self { @@ -35,7 +37,10 @@ public static function downloadFromIdFailed(mixed $id, $source, $destination): s return new self(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString)); } - /** @param resource $source */ + /** + * @param resource $source + * @internal + */ public static function uploadFailed(string $filename, $source, string $destinationUri): self { $sourceMetadata = stream_get_meta_data($source); diff --git a/src/GridFS/ReadableStream.php b/src/GridFS/ReadableStream.php index 980e3e19f..4e9b1ec72 100644 --- a/src/GridFS/ReadableStream.php +++ b/src/GridFS/ReadableStream.php @@ -17,7 +17,6 @@ namespace MongoDB\GridFS; -use Iterator; use MongoDB\BSON\Binary; use MongoDB\Driver\CursorInterface; use MongoDB\Exception\InvalidArgumentException; @@ -38,7 +37,7 @@ * * @internal */ -class ReadableStream +final class ReadableStream { private ?string $buffer = null; @@ -48,8 +47,7 @@ class ReadableStream private int $chunkOffset = 0; - /** @var (CursorInterface&Iterator)|null */ - private ?Iterator $chunksIterator = null; + private ?CursorInterface $chunksIterator = null; private int $expectedLastChunkSize = 0; diff --git a/src/GridFS/StreamWrapper.php b/src/GridFS/StreamWrapper.php index cc1d89fc9..e4f57fa85 100644 --- a/src/GridFS/StreamWrapper.php +++ b/src/GridFS/StreamWrapper.php @@ -49,7 +49,7 @@ * @see Bucket::openDownloadStream() * @psalm-type ContextOptions = array{collectionWrapper: CollectionWrapper, file: object}|array{collectionWrapper: CollectionWrapper, filename: string, options: array} */ -class StreamWrapper +final class StreamWrapper { /** @var resource|null Stream context (set by PHP) */ public $context; @@ -89,7 +89,7 @@ public static function register(string $protocol = 'gridfs'): void stream_wrapper_unregister($protocol); } - stream_wrapper_register($protocol, static::class, STREAM_IS_URL); + stream_wrapper_register($protocol, self::class, STREAM_IS_URL); } /** @@ -315,8 +315,7 @@ public function unlink(string $path): bool return true; } - /** @return false|array */ - public function url_stat(string $path, int $flags) + public function url_stat(string $path, int $flags): false|array { assert($this->stream === null); diff --git a/src/GridFS/WritableStream.php b/src/GridFS/WritableStream.php index 65b35de90..e7a4e0e63 100644 --- a/src/GridFS/WritableStream.php +++ b/src/GridFS/WritableStream.php @@ -17,7 +17,6 @@ namespace MongoDB\GridFS; -use HashContext; use MongoDB\BSON\Binary; use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; @@ -25,14 +24,8 @@ use MongoDB\Exception\InvalidArgumentException; use function array_intersect_key; -use function hash_final; -use function hash_init; -use function hash_update; -use function is_bool; use function is_integer; -use function is_string; use function MongoDB\is_document; -use function MongoDB\is_string_array; use function sprintf; use function strlen; use function substr; @@ -42,7 +35,7 @@ * * @internal */ -class WritableStream +final class WritableStream { private const DEFAULT_CHUNK_SIZE_BYTES = 261120; @@ -52,12 +45,8 @@ class WritableStream private int $chunkSize; - private bool $disableMD5; - private array $file; - private ?HashContext $hashCtx = null; - private bool $isClosed = false; private int $length = 0; @@ -69,19 +58,9 @@ class WritableStream * * * _id (mixed): File document identifier. Defaults to a new ObjectId. * - * * aliases (array of strings): DEPRECATED An array of aliases. - * Applications wishing to store aliases should add an aliases field to - * the metadata document instead. - * * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to * 261120 (i.e. 255 KiB). * - * * disableMD5 (boolean): When true, no MD5 sum will be generated. - * Defaults to "false". - * - * * contentType (string): DEPRECATED content type to be stored with the - * file. This information should now be added to the metadata. - * * * metadata (document): User data for the "metadata" field of the files * collection document. * @@ -95,13 +74,8 @@ public function __construct(private CollectionWrapper $collectionWrapper, string $options += [ '_id' => new ObjectId(), 'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES, - 'disableMD5' => false, ]; - if (isset($options['aliases']) && ! is_string_array($options['aliases'])) { - throw InvalidArgumentException::invalidType('"aliases" option', $options['aliases'], 'array of strings'); - } - if (! is_integer($options['chunkSizeBytes'])) { throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer'); } @@ -110,32 +84,18 @@ public function __construct(private CollectionWrapper $collectionWrapper, string throw new InvalidArgumentException(sprintf('Expected "chunkSizeBytes" option to be >= 1, %d given', $options['chunkSizeBytes'])); } - if (! is_bool($options['disableMD5'])) { - throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean'); - } - - if (isset($options['contentType']) && ! is_string($options['contentType'])) { - throw InvalidArgumentException::invalidType('"contentType" option', $options['contentType'], 'string'); - } - if (isset($options['metadata']) && ! is_document($options['metadata'])) { throw InvalidArgumentException::expectedDocumentType('"metadata" option', $options['metadata']); } $this->chunkSize = $options['chunkSizeBytes']; - $this->disableMD5 = $options['disableMD5']; - - if (! $this->disableMD5) { - $this->hashCtx = hash_init('md5'); - } - $this->file = [ '_id' => $options['_id'], 'chunkSize' => $this->chunkSize, 'filename' => $filename, 'length' => null, 'uploadDate' => null, - ] + array_intersect_key($options, ['aliases' => 1, 'contentType' => 1, 'metadata' => 1]); + ] + array_intersect_key($options, ['metadata' => 1]); } /** @@ -248,10 +208,6 @@ private function fileCollectionInsert(): void $this->file['length'] = $this->length; $this->file['uploadDate'] = new UTCDateTime(); - if (! $this->disableMD5 && $this->hashCtx) { - $this->file['md5'] = hash_final($this->hashCtx); - } - try { $this->collectionWrapper->insertFile($this->file); } catch (DriverRuntimeException $e) { @@ -276,10 +232,6 @@ private function insertChunkFromBuffer(): void 'data' => new Binary($data), ]; - if (! $this->disableMD5 && $this->hashCtx) { - hash_update($this->hashCtx, $data); - } - try { $this->collectionWrapper->insertChunk($chunk); } catch (DriverRuntimeException $e) { diff --git a/src/InsertManyResult.php b/src/InsertManyResult.php index 6ad2a9595..2f27bd24e 100644 --- a/src/InsertManyResult.php +++ b/src/InsertManyResult.php @@ -17,19 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a multi-document insert operation. */ class InsertManyResult { - private bool $isAcknowledged; - public function __construct(private WriteResult $writeResult, private array $insertedIds) { - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -38,16 +35,11 @@ public function __construct(private WriteResult $writeResult, private array $ins * This method should only be called if the write was acknowledged. * * @see InsertManyResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getInsertedCount() + public function getInsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getInsertedCount(); } /** @@ -58,10 +50,8 @@ public function getInsertedCount() * the driver did not generate an ID), the index will contain its "_id" * field value. Any driver-generated ID will be a MongoDB\BSON\ObjectId * instance. - * - * @return array */ - public function getInsertedIds() + public function getInsertedIds(): array { return $this->insertedIds; } @@ -71,10 +61,8 @@ public function getInsertedIds() * * If the insert was not acknowledged, other fields from the WriteResult * (e.g. insertedCount) will be undefined. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { return $this->writeResult->isAcknowledged(); } diff --git a/src/InsertOneResult.php b/src/InsertOneResult.php index 9c72775c8..47e1d26bc 100644 --- a/src/InsertOneResult.php +++ b/src/InsertOneResult.php @@ -17,19 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a single-document insert operation. */ class InsertOneResult { - private bool $isAcknowledged; - public function __construct(private WriteResult $writeResult, private mixed $insertedId) { - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -38,16 +35,11 @@ public function __construct(private WriteResult $writeResult, private mixed $ins * This method should only be called if the write was acknowledged. * * @see InsertOneResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getInsertedCount() + public function getInsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getInsertedCount(); } /** @@ -56,10 +48,8 @@ public function getInsertedCount() * If the document had an ID prior to inserting (i.e. the driver did not * need to generate an ID), this will contain its "_id". Any * driver-generated ID will be a MongoDB\BSON\ObjectId instance. - * - * @return mixed */ - public function getInsertedId() + public function getInsertedId(): mixed { return $this->insertedId; } @@ -73,10 +63,8 @@ public function getInsertedId() * If the insert was not acknowledged, other fields from the WriteResult * (e.g. insertedCount) will be undefined and their getter methods should * not be invoked. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { return $this->writeResult->isAcknowledged(); } diff --git a/src/MapReduceResult.php b/src/MapReduceResult.php deleted file mode 100644 index 00a75116a..000000000 --- a/src/MapReduceResult.php +++ /dev/null @@ -1,111 +0,0 @@ - - * @psalm-type MapReduceCallable = callable(): Traversable - */ -class MapReduceResult implements IteratorAggregate -{ - /** - * @var callable - * @psalm-var MapReduceCallable - */ - private $getIterator; - - private int $executionTimeMS; - - private array $counts; - - private array $timing; - - /** - * Returns various count statistics from the mapReduce command. - * - * @return array - */ - public function getCounts() - { - return $this->counts; - } - - /** - * Return the command execution time in milliseconds. - * - * @return integer - */ - public function getExecutionTimeMS() - { - return $this->executionTimeMS; - } - - /** - * Return the mapReduce results as a Traversable. - * - * @see https://php.net/iteratoraggregate.getiterator - * @return Traversable - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return call_user_func($this->getIterator); - } - - /** - * Returns various timing statistics from the mapReduce command. - * - * Note: timing statistics are only available if the mapReduce command's - * "verbose" option was true; otherwise, an empty array will be returned. - * - * @return array - */ - public function getTiming() - { - return $this->timing; - } - - /** - * @internal - * @param callable $getIterator Callback that returns a Traversable for mapReduce results - * @param stdClass $result Result document from the mapReduce command - * @psalm-param MapReduceCallable $getIterator - */ - public function __construct(callable $getIterator, stdClass $result) - { - $this->getIterator = $getIterator; - $this->executionTimeMS = isset($result->timeMillis) ? (integer) $result->timeMillis : 0; - $this->counts = isset($result->counts) ? (array) $result->counts : []; - $this->timing = isset($result->timing) ? (array) $result->timing : []; - } -} diff --git a/src/Model/BSONArray.php b/src/Model/BSONArray.php index f055f4cb7..040350ce4 100644 --- a/src/Model/BSONArray.php +++ b/src/Model/BSONArray.php @@ -21,7 +21,6 @@ use JsonSerializable; use MongoDB\BSON\Serializable; use MongoDB\BSON\Unserializable; -use ReturnTypeWillChange; use function array_values; use function MongoDB\recursive_copy; @@ -51,9 +50,8 @@ public function __clone() * * @see https://php.net/oop5.magic#object.set-state * @see https://php.net/var-export - * @return self */ - public static function __set_state(array $properties) + public static function __set_state(array $properties): self { $array = new self(); $array->exchangeArray($properties); @@ -68,10 +66,8 @@ public static function __set_state(array $properties) * as a BSON array. * * @see https://php.net/mongodb-bson-serializable.bsonserialize - * @return array */ - #[ReturnTypeWillChange] - public function bsonSerialize() + public function bsonSerialize(): array { return array_values($this->getArrayCopy()); } @@ -82,8 +78,7 @@ public function bsonSerialize() * @see https://php.net/mongodb-bson-unserializable.bsonunserialize * @param array $data Array data */ - #[ReturnTypeWillChange] - public function bsonUnserialize(array $data) + public function bsonUnserialize(array $data): void { parent::__construct($data); } @@ -95,10 +90,8 @@ public function bsonUnserialize(array $data) * as a JSON array. * * @see https://php.net/jsonserializable.jsonserialize - * @return array */ - #[ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): array { return array_values($this->getArrayCopy()); } diff --git a/src/Model/BSONDocument.php b/src/Model/BSONDocument.php index b9a9c0d80..5df92c0be 100644 --- a/src/Model/BSONDocument.php +++ b/src/Model/BSONDocument.php @@ -22,7 +22,6 @@ use JsonSerializable; use MongoDB\BSON\Serializable; use MongoDB\BSON\Unserializable; -use ReturnTypeWillChange; use stdClass; use function MongoDB\recursive_copy; @@ -65,9 +64,8 @@ public function __construct(array $input = [], int $flags = ArrayObject::ARRAY_A * * @see https://php.net/oop5.magic#object.set-state * @see https://php.net/var-export - * @return self */ - public static function __set_state(array $properties) + public static function __set_state(array $properties): self { $document = new self(); $document->exchangeArray($properties); @@ -79,10 +77,8 @@ public static function __set_state(array $properties) * Serialize the document to BSON. * * @see https://php.net/mongodb-bson-serializable.bsonserialize - * @return stdClass */ - #[ReturnTypeWillChange] - public function bsonSerialize() + public function bsonSerialize(): stdClass { return (object) $this->getArrayCopy(); } @@ -93,8 +89,7 @@ public function bsonSerialize() * @see https://php.net/mongodb-bson-unserializable.bsonunserialize * @param array $data Array data */ - #[ReturnTypeWillChange] - public function bsonUnserialize(array $data) + public function bsonUnserialize(array $data): void { parent::__construct($data, ArrayObject::ARRAY_AS_PROPS); } @@ -103,10 +98,8 @@ public function bsonUnserialize(array $data) * Serialize the array to JSON. * * @see https://php.net/jsonserializable.jsonserialize - * @return object */ - #[ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): stdClass { return (object) $this->getArrayCopy(); } diff --git a/src/Model/BSONIterator.php b/src/Model/BSONIterator.php index 2ca257c7f..e90901173 100644 --- a/src/Model/BSONIterator.php +++ b/src/Model/BSONIterator.php @@ -21,7 +21,6 @@ use MongoDB\BSON\Document; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; -use ReturnTypeWillChange; use function assert; use function is_array; @@ -50,44 +49,28 @@ class BSONIterator implements Iterator private int $position = 0; - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() + /** @see https://php.net/iterator.current */ + public function current(): mixed { return $this->current; } - /** - * @see https://php.net/iterator.key - * @return int - */ - #[ReturnTypeWillChange] - public function key() + /** @see https://php.net/iterator.key */ + public function key(): int { return $this->key; } - /** - * @see https://php.net/iterator.next - * @return void - */ - #[ReturnTypeWillChange] - public function next() + /** @see https://php.net/iterator.next */ + public function next(): void { $this->key++; $this->current = null; $this->advance(); } - /** - * @see https://php.net/iterator.rewind - * @return void - */ - #[ReturnTypeWillChange] - public function rewind() + /** @see https://php.net/iterator.rewind */ + public function rewind(): void { $this->key = 0; $this->position = 0; @@ -96,7 +79,6 @@ public function rewind() } /** @see https://php.net/iterator.valid */ - #[ReturnTypeWillChange] public function valid(): bool { return $this->current !== null; diff --git a/src/Model/CachingIterator.php b/src/Model/CachingIterator.php index 15ba7ca1a..64c61708f 100644 --- a/src/Model/CachingIterator.php +++ b/src/Model/CachingIterator.php @@ -20,7 +20,6 @@ use Countable; use Iterator; use IteratorIterator; -use ReturnTypeWillChange; use Traversable; use function count; @@ -40,7 +39,7 @@ * @template TValue * @template-implements Iterator */ -class CachingIterator implements Countable, Iterator +final class CachingIterator implements Countable, Iterator { private const FIELD_KEY = 0; private const FIELD_VALUE = 1; @@ -79,12 +78,8 @@ public function count(): int return count($this->items); } - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() + /** @see https://php.net/iterator.current */ + public function current(): mixed { $currentItem = current($this->items); @@ -93,11 +88,9 @@ public function current() /** * @see https://php.net/iterator.key - * @return mixed * @psalm-return TKey|null */ - #[ReturnTypeWillChange] - public function key() + public function key(): mixed { $currentItem = current($this->items); diff --git a/src/Model/CallbackIterator.php b/src/Model/CallbackIterator.php index ea429ccbc..4a8e5dda9 100644 --- a/src/Model/CallbackIterator.php +++ b/src/Model/CallbackIterator.php @@ -19,7 +19,6 @@ use Iterator; use IteratorIterator; -use ReturnTypeWillChange; use Traversable; use function call_user_func; @@ -29,12 +28,12 @@ * * @internal * - * @template TKey + * @template TKey of array-key * @template TValue * @template TCallbackValue * @template-implements Iterator */ -class CallbackIterator implements Iterator +final class CallbackIterator implements Iterator { /** @var callable(TValue, TKey): TCallbackValue */ private $callback; @@ -56,8 +55,7 @@ public function __construct(Traversable $traversable, callable $callback) * @see https://php.net/iterator.current * @return TCallbackValue */ - #[ReturnTypeWillChange] - public function current() + public function current(): mixed { return call_user_func($this->callback, $this->iterator->current(), $this->iterator->key()); } @@ -66,8 +64,7 @@ public function current() * @see https://php.net/iterator.key * @return TKey */ - #[ReturnTypeWillChange] - public function key() + public function key(): mixed { return $this->iterator->key(); } diff --git a/src/Model/ChangeStreamIterator.php b/src/Model/ChangeStreamIterator.php index aa635dda9..4a4f36b51 100644 --- a/src/Model/ChangeStreamIterator.php +++ b/src/Model/ChangeStreamIterator.php @@ -17,7 +17,6 @@ namespace MongoDB\Model; -use Iterator; use IteratorIterator; use MongoDB\BSON\Document; use MongoDB\BSON\Serializable; @@ -30,7 +29,6 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\ResumeTokenException; use MongoDB\Exception\UnexpectedValueException; -use ReturnTypeWillChange; use function assert; use function count; @@ -49,9 +47,9 @@ * * @internal * @template TValue of array|object - * @template-extends IteratorIterator&Iterator> + * @template-extends IteratorIterator> */ -class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber +final class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber { private int $batchPosition = 0; @@ -67,28 +65,25 @@ class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber /** * @see https://php.net/iteratoriterator.current - * @return array|object|null * @psalm-return TValue|null */ - #[ReturnTypeWillChange] - public function current() + public function current(): array|object|null { return $this->valid() ? parent::current() : null; } /** - * Necessary to let psalm know that we're always expecting a cursor as inner - * iterator. This could be side-stepped due to the class not being final, - * but it's very much an invalid use-case. This method can be dropped in 2.0 - * once the class is final. + * This method is necessary as psalm does not properly set the return type + * of IteratorIterator::getInnerIterator to the templated iterator * - * @return CursorInterface&Iterator + * @see https://github.com/vimeo/psalm/pull/11100. + * + * @return CursorInterface */ - final public function getInnerIterator(): Iterator + public function getInnerIterator(): CursorInterface { $cursor = parent::getInnerIterator(); assert($cursor instanceof CursorInterface); - assert($cursor instanceof Iterator); return $cursor; } @@ -99,10 +94,8 @@ final public function getInnerIterator(): Iterator * Null may be returned if no change documents have been iterated and the * server did not include a postBatchResumeToken in its aggregate or getMore * command response. - * - * @return array|object|null */ - public function getResumeToken() + public function getResumeToken(): array|object|null { return $this->resumeToken; } @@ -115,12 +108,8 @@ public function getServer(): Server return $this->server; } - /** - * @see https://php.net/iteratoriterator.key - * @return int|null - */ - #[ReturnTypeWillChange] - public function key() + /** @see https://php.net/iteratoriterator.key */ + public function key(): ?int { return $this->valid() ? parent::key() : null; } @@ -173,18 +162,10 @@ public function valid(): bool /** * @internal - * @psalm-param CursorInterface&Iterator $cursor + * @psalm-param CursorInterface $cursor */ public function __construct(CursorInterface $cursor, int $firstBatchSize, array|object|null $initialResumeToken, private ?object $postBatchResumeToken = null) { - if (! $cursor instanceof Iterator) { - throw InvalidArgumentException::invalidType( - '$cursor', - $cursor, - CursorInterface::class . '&' . Iterator::class, - ); - } - if (isset($initialResumeToken) && ! is_document($initialResumeToken)) { throw InvalidArgumentException::expectedDocumentType('$initialResumeToken', $initialResumeToken); } @@ -238,11 +219,10 @@ final public function commandSucceeded(CommandSucceededEvent $event): void * Extracts the resume token (i.e. "_id" field) from a change document. * * @param array|object $document Change document - * @return array|object * @throws InvalidArgumentException * @throws ResumeTokenException if the resume token is not found or invalid */ - private function extractResumeToken(array|object $document) + private function extractResumeToken(array|object $document): array|object { if (! is_document($document)) { throw InvalidArgumentException::expectedDocumentType('$document', $document); diff --git a/src/Model/CodecCursor.php b/src/Model/CodecCursor.php index 642f3c3c1..d7a3776b2 100644 --- a/src/Model/CodecCursor.php +++ b/src/Model/CodecCursor.php @@ -17,30 +17,24 @@ namespace MongoDB\Model; -use Iterator; use MongoDB\BSON\Document; use MongoDB\BSON\Int64; use MongoDB\Codec\DocumentCodec; -use MongoDB\Driver\Cursor; -use MongoDB\Driver\CursorId; use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Server; -use ReturnTypeWillChange; use function assert; use function iterator_to_array; use function sprintf; use function trigger_error; -use const E_USER_DEPRECATED; use const E_USER_WARNING; /** * @template TValue of object - * @template-implements CursorInterface - * @template-implements Iterator + * @template-implements CursorInterface */ -class CodecCursor implements CursorInterface, Iterator +class CodecCursor implements CursorInterface { private const TYPEMAP = ['root' => 'bson']; @@ -64,33 +58,16 @@ public function current(): ?object * @param DocumentCodec $codec * @return self */ - public static function fromCursor(Cursor $cursor, DocumentCodec $codec): self + public static function fromCursor(CursorInterface $cursor, DocumentCodec $codec): self { $cursor->setTypeMap(self::TYPEMAP); return new self($cursor, $codec); } - /** - * @return CursorId|Int64 - * @psalm-return ($asInt64 is true ? Int64 : CursorId) - */ - #[ReturnTypeWillChange] - public function getId(bool $asInt64 = false) + public function getId(): Int64 { - if (! $asInt64) { - @trigger_error( - sprintf( - 'The method "%s" will no longer return a "%s" instance in the future. Pass "true" as argument to change to the new behavior and receive a "%s" instance instead.', - __METHOD__, - CursorId::class, - Int64::class, - ), - E_USER_DEPRECATED, - ); - } - - return $this->cursor->getId($asInt64); + return $this->cursor->getId(); } public function getServer(): Server @@ -138,7 +115,7 @@ public function valid(): bool } /** @param DocumentCodec $codec */ - private function __construct(private Cursor $cursor, private DocumentCodec $codec) + private function __construct(private CursorInterface $cursor, private DocumentCodec $codec) { } } diff --git a/src/Model/CollectionInfo.php b/src/Model/CollectionInfo.php index 2850a4be4..717e19d84 100644 --- a/src/Model/CollectionInfo.php +++ b/src/Model/CollectionInfo.php @@ -19,7 +19,6 @@ use ArrayAccess; use MongoDB\Exception\BadMethodCallException; -use ReturnTypeWillChange; use function array_key_exists; @@ -45,9 +44,8 @@ public function __construct(private array $info) * Return the collection info as an array. * * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return $this->info; } @@ -56,10 +54,8 @@ public function __debugInfo() * Return the maximum number of documents to keep in the capped collection. * * @deprecated 1.0 Deprecated in favor of using getOptions - * - * @return integer|null */ - public function getCappedMax() + public function getCappedMax(): ?int { /* The MongoDB server might return this number as an integer or float */ return isset($this->info['options']['max']) ? (integer) $this->info['options']['max'] : null; @@ -69,10 +65,8 @@ public function getCappedMax() * Return the maximum size (in bytes) of the capped collection. * * @deprecated 1.0 Deprecated in favor of using getOptions - * - * @return integer|null */ - public function getCappedSize() + public function getCappedSize(): ?int { /* The MongoDB server might return this number as an integer or float */ return isset($this->info['options']['size']) ? (integer) $this->info['options']['size'] : null; @@ -100,9 +94,8 @@ public function getInfo(): array * Return the collection name. * * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - * @return string */ - public function getName() + public function getName(): string { return (string) $this->info['name']; } @@ -111,9 +104,8 @@ public function getName() * Return the collection options. * * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - * @return array */ - public function getOptions() + public function getOptions(): array { return (array) ($this->info['options'] ?? []); } @@ -132,10 +124,8 @@ public function getType(): string * Return whether the collection is a capped collection. * * @deprecated 1.0 Deprecated in favor of using getOptions - * - * @return boolean */ - public function isCapped() + public function isCapped(): bool { return ! empty($this->info['options']['capped']); } @@ -144,11 +134,9 @@ public function isCapped() * Check whether a field exists in the collection information. * * @see https://php.net/arrayaccess.offsetexists - * @return boolean * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetExists(mixed $offset) + public function offsetExists(mixed $offset): bool { return array_key_exists($offset, $this->info); } @@ -157,11 +145,9 @@ public function offsetExists(mixed $offset) * Return the field's value from the collection information. * * @see https://php.net/arrayaccess.offsetget - * @return mixed * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetGet(mixed $offset) + public function offsetGet(mixed $offset): mixed { return $this->info[$offset]; } @@ -171,10 +157,8 @@ public function offsetGet(mixed $offset) * * @see https://php.net/arrayaccess.offsetset * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetSet(mixed $offset, mixed $value) + public function offsetSet(mixed $offset, mixed $value): void { throw BadMethodCallException::classIsImmutable(self::class); } @@ -184,10 +168,8 @@ public function offsetSet(mixed $offset, mixed $value) * * @see https://php.net/arrayaccess.offsetunset * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetUnset(mixed $offset) + public function offsetUnset(mixed $offset): void { throw BadMethodCallException::classIsImmutable(self::class); } diff --git a/src/Model/CollectionInfoCommandIterator.php b/src/Model/CollectionInfoCommandIterator.php deleted file mode 100644 index 543d8fc38..000000000 --- a/src/Model/CollectionInfoCommandIterator.php +++ /dev/null @@ -1,60 +0,0 @@ -> - */ -class CollectionInfoCommandIterator extends IteratorIterator implements CollectionInfoIterator -{ - /** @param Traversable $iterator */ - public function __construct(Traversable $iterator, private ?string $databaseName = null) - { - parent::__construct($iterator); - } - - /** - * Return the current element as a CollectionInfo instance. - * - * @see CollectionInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): CollectionInfo - { - $info = parent::current(); - - if ($this->databaseName !== null && isset($info['idIndex']) && ! isset($info['idIndex']['ns'])) { - $info['idIndex']['ns'] = $this->databaseName . '.' . $info['name']; - } - - return new CollectionInfo($info); - } -} diff --git a/src/Model/CollectionInfoIterator.php b/src/Model/CollectionInfoIterator.php deleted file mode 100644 index e7ebf4984..000000000 --- a/src/Model/CollectionInfoIterator.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -interface CollectionInfoIterator extends Iterator -{ - /** - * Return the current element as a CollectionInfo instance. - * - * @return CollectionInfo - */ - #[ReturnTypeWillChange] - public function current(); -} diff --git a/src/Model/DatabaseInfo.php b/src/Model/DatabaseInfo.php index 2803532f8..2a9d01204 100644 --- a/src/Model/DatabaseInfo.php +++ b/src/Model/DatabaseInfo.php @@ -19,7 +19,6 @@ use ArrayAccess; use MongoDB\Exception\BadMethodCallException; -use ReturnTypeWillChange; use function array_key_exists; @@ -44,29 +43,24 @@ public function __construct(private array $info) * Return the database info as an array. * * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return $this->info; } /** * Return the database name. - * - * @return string */ - public function getName() + public function getName(): string { return (string) $this->info['name']; } /** * Return the databases size on disk (in bytes). - * - * @return integer */ - public function getSizeOnDisk() + public function getSizeOnDisk(): int { /* The MongoDB server might return this number as an integer or float */ return (integer) $this->info['sizeOnDisk']; @@ -74,10 +68,8 @@ public function getSizeOnDisk() /** * Return whether the database is empty. - * - * @return boolean */ - public function isEmpty() + public function isEmpty(): bool { return (boolean) $this->info['empty']; } @@ -86,11 +78,9 @@ public function isEmpty() * Check whether a field exists in the database information. * * @see https://php.net/arrayaccess.offsetexists - * @return boolean * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetExists(mixed $offset) + public function offsetExists(mixed $offset): bool { return array_key_exists($offset, $this->info); } @@ -99,11 +89,9 @@ public function offsetExists(mixed $offset) * Return the field's value from the database information. * * @see https://php.net/arrayaccess.offsetget - * @return mixed * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetGet(mixed $offset) + public function offsetGet(mixed $offset): mixed { return $this->info[$offset]; } @@ -113,10 +101,8 @@ public function offsetGet(mixed $offset) * * @see https://php.net/arrayaccess.offsetset * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetSet(mixed $offset, mixed $value) + public function offsetSet(mixed $offset, mixed $value): void { throw BadMethodCallException::classIsImmutable(self::class); } @@ -126,10 +112,8 @@ public function offsetSet(mixed $offset, mixed $value) * * @see https://php.net/arrayaccess.offsetunset * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetUnset(mixed $offset) + public function offsetUnset(mixed $offset): void { throw BadMethodCallException::classIsImmutable(self::class); } diff --git a/src/Model/DatabaseInfoIterator.php b/src/Model/DatabaseInfoIterator.php deleted file mode 100644 index 72199bd32..000000000 --- a/src/Model/DatabaseInfoIterator.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -interface DatabaseInfoIterator extends Iterator -{ - /** - * Return the current element as a DatabaseInfo instance. - * - * @return DatabaseInfo - */ - #[ReturnTypeWillChange] - public function current(); -} diff --git a/src/Model/DatabaseInfoLegacyIterator.php b/src/Model/DatabaseInfoLegacyIterator.php deleted file mode 100644 index 1348b0c57..000000000 --- a/src/Model/DatabaseInfoLegacyIterator.php +++ /dev/null @@ -1,92 +0,0 @@ -databases)); - } - - /** - * Return the key of the current element. - * - * @see https://php.net/iterator.key - */ - public function key(): int - { - return key($this->databases); - } - - /** - * Move forward to next element. - * - * @see https://php.net/iterator.next - */ - public function next(): void - { - next($this->databases); - } - - /** - * Rewind the Iterator to the first element. - * - * @see https://php.net/iterator.rewind - */ - public function rewind(): void - { - reset($this->databases); - } - - /** - * Checks if current position is valid. - * - * @see https://php.net/iterator.valid - */ - public function valid(): bool - { - return key($this->databases) !== null; - } -} diff --git a/src/Model/IndexInfo.php b/src/Model/IndexInfo.php index 062c40856..ce6e04be5 100644 --- a/src/Model/IndexInfo.php +++ b/src/Model/IndexInfo.php @@ -19,13 +19,9 @@ use ArrayAccess; use MongoDB\Exception\BadMethodCallException; -use ReturnTypeWillChange; use function array_key_exists; use function array_search; -use function trigger_error; - -use const E_USER_DEPRECATED; /** * Index information model class. @@ -53,107 +49,66 @@ public function __construct(private array $info) * Return the collection info as an array. * * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return $this->info; } /** * Return the index name to allow casting IndexInfo to string. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->getName(); } /** * Return the index key. - * - * @return array */ - public function getKey() + public function getKey(): array { return (array) $this->info['key']; } /** * Return the index name. - * - * @return string */ - public function getName() + public function getName(): string { return (string) $this->info['name']; } - /** - * Return the index namespace (e.g. "db.collection"). - * - * @deprecated - * - * @return string - */ - public function getNamespace() - { - @trigger_error('MongoDB 4.4 drops support for the namespace in indexes, the method "IndexInfo::getNamespace()" will be removed in version 2.0', E_USER_DEPRECATED); - - return (string) $this->info['ns']; - } - /** * Return the index version. - * - * @return integer */ - public function getVersion() + public function getVersion(): int { return (integer) $this->info['v']; } /** * Return whether or not this index is of type 2dsphere. - * - * @return boolean */ - public function is2dSphere() + public function is2dSphere(): bool { return array_search('2dsphere', $this->getKey(), true) !== false; } - /** - * Return whether or not this index is of type geoHaystack. - * - * @return boolean - * @deprecated Since 1.16: MongoDB 5.0 removes support for geoHaystack indexes. - */ - public function isGeoHaystack() - { - @trigger_error('MongoDB 5.0 removes support for "geoHaystack" indexes, the method "IndexInfo::isGeoHaystack()" will be removed in version 2.0', E_USER_DEPRECATED); - - return array_search('geoHaystack', $this->getKey(), true) !== false; - } - /** * Return whether this is a sparse index. * * @see https://mongodb.com/docs/manual/core/index-sparse/ - * @return boolean */ - public function isSparse() + public function isSparse(): bool { return ! empty($this->info['sparse']); } /** * Return whether or not this index is of type text. - * - * @return boolean */ - public function isText() + public function isText(): bool { return array_search('text', $this->getKey(), true) !== false; } @@ -162,9 +117,8 @@ public function isText() * Return whether this is a TTL index. * * @see https://mongodb.com/docs/manual/core/index-ttl/ - * @return boolean */ - public function isTtl() + public function isTtl(): bool { return array_key_exists('expireAfterSeconds', $this->info); } @@ -173,9 +127,8 @@ public function isTtl() * Return whether this is a unique index. * * @see https://mongodb.com/docs/manual/core/index-unique/ - * @return boolean */ - public function isUnique() + public function isUnique(): bool { return ! empty($this->info['unique']); } @@ -184,11 +137,9 @@ public function isUnique() * Check whether a field exists in the index information. * * @see https://php.net/arrayaccess.offsetexists - * @return boolean * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetExists(mixed $offset) + public function offsetExists(mixed $offset): bool { return array_key_exists($offset, $this->info); } @@ -202,11 +153,9 @@ public function offsetExists(mixed $offset) * * @see https://php.net/arrayaccess.offsetget * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst#getting-full-index-information - * @return mixed * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetGet(mixed $offset) + public function offsetGet(mixed $offset): mixed { return $this->info[$offset]; } @@ -216,10 +165,8 @@ public function offsetGet(mixed $offset) * * @see https://php.net/arrayaccess.offsetset * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetSet(mixed $offset, mixed $value) + public function offsetSet(mixed $offset, mixed $value): void { throw BadMethodCallException::classIsImmutable(self::class); } @@ -229,10 +176,8 @@ public function offsetSet(mixed $offset, mixed $value) * * @see https://php.net/arrayaccess.offsetunset * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetUnset(mixed $offset) + public function offsetUnset(mixed $offset): void { throw BadMethodCallException::classIsImmutable(self::class); } diff --git a/src/Model/IndexInfoIterator.php b/src/Model/IndexInfoIterator.php deleted file mode 100644 index b7929c20f..000000000 --- a/src/Model/IndexInfoIterator.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -interface IndexInfoIterator extends Iterator -{ - /** - * Return the current element as a IndexInfo instance. - * - * @return IndexInfo - */ - #[ReturnTypeWillChange] - public function current(); -} diff --git a/src/Model/IndexInfoIteratorIterator.php b/src/Model/IndexInfoIteratorIterator.php deleted file mode 100644 index 0bcc4bd85..000000000 --- a/src/Model/IndexInfoIteratorIterator.php +++ /dev/null @@ -1,64 +0,0 @@ -> - */ -class IndexInfoIteratorIterator extends IteratorIterator implements IndexInfoIterator -{ - /** @param Traversable $iterator */ - public function __construct(Traversable $iterator, private ?string $ns = null) - { - parent::__construct($iterator); - } - - /** - * Return the current element as an IndexInfo instance. - * - * @see IndexInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): IndexInfo - { - $info = parent::current(); - - if (! array_key_exists('ns', $info) && $this->ns !== null) { - $info['ns'] = $this->ns; - } - - return new IndexInfo($info); - } -} diff --git a/src/Model/IndexInput.php b/src/Model/IndexInput.php index 657c2b6da..133451eeb 100644 --- a/src/Model/IndexInput.php +++ b/src/Model/IndexInput.php @@ -38,7 +38,7 @@ * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst * @see https://mongodb.com/docs/manual/reference/method/db.collection.createIndex/ */ -class IndexInput implements Serializable +final class IndexInput implements Serializable { /** * @param array $index Index specification diff --git a/src/Model/SearchIndexInput.php b/src/Model/SearchIndexInput.php index bad880a7f..b27f4f3be 100644 --- a/src/Model/SearchIndexInput.php +++ b/src/Model/SearchIndexInput.php @@ -34,7 +34,7 @@ * @see https://github.com/mongodb/specifications/blob/master/source/index-management/index-management.rst#search-indexes * @see https://mongodb.com/docs/manual/reference/method/db.collection.createSearchIndex/ */ -class SearchIndexInput implements Serializable +final class SearchIndexInput implements Serializable { /** * @param array{definition: array|object, name?: string, type?: string} $index Search index specification diff --git a/src/Operation/Aggregate.php b/src/Operation/Aggregate.php index 3543fc73c..0588df188 100644 --- a/src/Operation/Aggregate.php +++ b/src/Operation/Aggregate.php @@ -17,10 +17,8 @@ namespace MongoDB\Operation; -use Iterator; use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\Command; -use MongoDB\Driver\Cursor; use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\ReadConcern; @@ -48,10 +46,8 @@ * * @see \MongoDB\Collection::aggregate() * @see https://mongodb.com/docs/manual/reference/command/aggregate/ - * - * @final extending this class will not be supported in v2.0.0 */ -class Aggregate implements Executable, Explainable +final class Aggregate implements Explainable { private bool $isWrite; @@ -216,13 +212,11 @@ public function __construct(private string $databaseName, private ?string $colle /** * Execute the operation. * - * @see Executable::execute() - * @return CursorInterface&Iterator * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): CursorInterface { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction) { @@ -257,9 +251,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { $cmd = $this->createCommandDocument(); @@ -322,7 +315,7 @@ private function createCommandOptions(): array * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php * @see https://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php */ - private function executeCommand(Server $server, Command $command): Cursor + private function executeCommand(Server $server, Command $command): CursorInterface { $options = []; diff --git a/src/Operation/BulkWrite.php b/src/Operation/BulkWrite.php index a5367bf2c..411c9d24d 100644 --- a/src/Operation/BulkWrite.php +++ b/src/Operation/BulkWrite.php @@ -45,10 +45,8 @@ * Operation for executing multiple write operations. * * @see \MongoDB\Collection::bulkWrite() - * - * @final extending this class will not be supported in v2.0.0 */ -class BulkWrite implements Executable +final class BulkWrite { public const DELETE_MANY = 'deleteMany'; public const DELETE_ONE = 'deleteOne'; @@ -187,12 +185,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return BulkWriteResult * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): BulkWriteResult { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { diff --git a/src/Operation/Count.php b/src/Operation/Count.php index 9f9225942..d79d49447 100644 --- a/src/Operation/Count.php +++ b/src/Operation/Count.php @@ -40,10 +40,8 @@ * * @see \MongoDB\Collection::count() * @see https://mongodb.com/docs/manual/reference/command/count/ - * - * @final extending this class will not be supported in v2.0.0 */ -class Count implements Executable, Explainable +final class Count implements Explainable { /** * Constructs a count command. @@ -126,13 +124,11 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): int { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { @@ -154,9 +150,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { $cmd = $this->createCommandDocument(); diff --git a/src/Operation/CountDocuments.php b/src/Operation/CountDocuments.php index 954ea7466..7d00e8bed 100644 --- a/src/Operation/CountDocuments.php +++ b/src/Operation/CountDocuments.php @@ -36,10 +36,8 @@ * * @see \MongoDB\Collection::countDocuments() * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#countdocuments - * - * @final extending this class will not be supported in v2.0.0 */ -class CountDocuments implements Executable +final class CountDocuments { private array $aggregateOptions; @@ -105,13 +103,11 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): int { $cursor = $this->aggregate->execute($server); diff --git a/src/Operation/CreateCollection.php b/src/Operation/CreateCollection.php index e0f267af3..45a4925d3 100644 --- a/src/Operation/CreateCollection.php +++ b/src/Operation/CreateCollection.php @@ -24,46 +24,26 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; -use function current; use function is_array; use function is_bool; use function is_integer; use function is_string; use function MongoDB\is_document; use function MongoDB\is_pipeline; -use function trigger_error; - -use const E_USER_DEPRECATED; /** * Operation for the create command. * * @see \MongoDB\Database::createCollection() * @see https://mongodb.com/docs/manual/reference/command/create/ - * - * @final extending this class will not be supported in v2.0.0 */ -class CreateCollection implements Executable +final class CreateCollection { - /** @deprecated 1.21 */ - public const USE_POWER_OF_2_SIZES = 1; - - /** @deprecated 1.21 */ - public const NO_PADDING = 2; - /** * Constructs a create command. * * Supported options: * - * * autoIndexId (boolean): Specify false to disable the automatic creation - * of an index on the _id field. For replica sets, this option cannot be - * false. The default is true. - * - * This option has been deprecated since MongoDB 3.2. As of MongoDB 4.0, - * this option cannot be false when creating a replicated collection - * (i.e. a collection outside of the local database in any mongod mode). - * * * capped (boolean): Specify true to create a capped collection. If set, * the size option must also be specified. The default is false. * @@ -89,11 +69,6 @@ class CreateCollection implements Executable * * This is not supported for servers versions < 5.0. * - * * flags (integer): Options for the MMAPv1 storage engine only. Must be a - * bitwise combination CreateCollection::USE_POWER_OF_2_SIZES and - * CreateCollection::NO_PADDING. The default is - * CreateCollection::USE_POWER_OF_2_SIZES. - * * * indexOptionDefaults (document): Default configuration for indexes when * creating the collection. * @@ -117,9 +92,6 @@ class CreateCollection implements Executable * * This is not supported for servers versions < 5.0. * - * * typeMap (array): Type map for BSON deserialization. This will only be - * used for the returned command result document. - * * * validationAction (string): Validation action. * * * validationLevel (string): Validation level. @@ -140,10 +112,6 @@ class CreateCollection implements Executable */ public function __construct(private string $databaseName, private string $collectionName, private array $options = []) { - if (isset($this->options['autoIndexId']) && ! is_bool($this->options['autoIndexId'])) { - throw InvalidArgumentException::invalidType('"autoIndexId" option', $this->options['autoIndexId'], 'boolean'); - } - if (isset($this->options['capped']) && ! is_bool($this->options['capped'])) { throw InvalidArgumentException::invalidType('"capped" option', $this->options['capped'], 'boolean'); } @@ -168,10 +136,6 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::invalidType('"expireAfterSeconds" option', $this->options['expireAfterSeconds'], 'integer'); } - if (isset($this->options['flags']) && ! is_integer($this->options['flags'])) { - throw InvalidArgumentException::invalidType('"flags" option', $this->options['flags'], 'integer'); - } - if (isset($this->options['indexOptionDefaults']) && ! is_document($this->options['indexOptionDefaults'])) { throw InvalidArgumentException::expectedDocumentType('"indexOptionDefaults" option', $this->options['indexOptionDefaults']); } @@ -204,10 +168,6 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::expectedDocumentType('"timeseries" option', $this->options['timeseries']); } - if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); - } - if (isset($this->options['validationAction']) && ! is_string($this->options['validationAction'])) { throw InvalidArgumentException::invalidType('"validationAction" option', $this->options['validationAction'], 'string'); } @@ -232,14 +192,6 @@ public function __construct(private string $databaseName, private string $collec unset($this->options['writeConcern']); } - if (isset($this->options['autoIndexId'])) { - trigger_error('The "autoIndexId" option is deprecated and will be removed in version 2.0', E_USER_DEPRECATED); - } - - if (isset($this->options['flags'])) { - trigger_error('The "flags" option is deprecated and will be removed in version 2.0', E_USER_DEPRECATED); - } - if (isset($this->options['pipeline']) && ! is_pipeline($this->options['pipeline'], true /* allowEmpty */)) { throw new InvalidArgumentException('"pipeline" option is not a valid aggregation pipeline'); } @@ -248,19 +200,11 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } /** @@ -270,7 +214,7 @@ private function createCommand(): Command { $cmd = ['create' => $this->collectionName]; - foreach (['autoIndexId', 'capped', 'comment', 'expireAfterSeconds', 'flags', 'max', 'maxTimeMS', 'pipeline', 'size', 'validationAction', 'validationLevel', 'viewOn'] as $option) { + foreach (['capped', 'comment', 'expireAfterSeconds', 'max', 'maxTimeMS', 'pipeline', 'size', 'validationAction', 'validationLevel', 'viewOn'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } diff --git a/src/Operation/CreateEncryptedCollection.php b/src/Operation/CreateEncryptedCollection.php index a5cde6514..c30db34af 100644 --- a/src/Operation/CreateEncryptedCollection.php +++ b/src/Operation/CreateEncryptedCollection.php @@ -48,7 +48,7 @@ * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#create-encrypted-collection-helper * @see https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/manage-collections/ */ -class CreateEncryptedCollection implements Executable +final class CreateEncryptedCollection { private const WIRE_VERSION_FOR_QUERYABLE_ENCRYPTION_V2 = 21; @@ -97,21 +97,20 @@ public function __construct(private string $databaseName, private string $collec * "encryptedFields" option and reconstruct the internal CreateCollection * operation used for creating the encrypted collection. * - * The $encryptedFields reference parameter may be used to determine which - * data keys have been created. + * Returns the data keys that have been created. * * @see \MongoDB\Database::createEncryptedCollection() * @see https://www.php.net/manual/en/mongodb-driver-clientencryption.createdatakey.php * @throws DriverRuntimeException for errors creating a data key */ - public function createDataKeys(ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, ?array &$encryptedFields = null): void + public function createDataKeys(ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey): array { /** @psalm-var array{fields: list|Serializable|PackedArray} */ $encryptedFields = document_to_array($this->options['encryptedFields']); // NOP if there are no fields to examine if (! isset($encryptedFields['fields'])) { - return; + return $encryptedFields; } // Allow PackedArray or Serializable object for the fields array @@ -128,7 +127,7 @@ public function createDataKeys(ClientEncryption $clientEncryption, string $kmsPr // Skip invalid types and defer to the server to raise an error if (! is_array($encryptedFields['fields'])) { - return; + return $encryptedFields; } $createDataKeyArgs = [ @@ -152,15 +151,15 @@ public function createDataKeys(ClientEncryption $clientEncryption, string $kmsPr $this->options['encryptedFields'] = $encryptedFields; $this->createCollection = new CreateCollection($this->databaseName, $this->collectionName, $this->options); + + return $encryptedFields; } /** - * @see Executable::execute() - * @return array|object Command result document from creating the encrypted collection * @throws DriverRuntimeException for other driver errors (e.g. connection errors) * @throws UnsupportedException if the server does not support Queryable Encryption */ - public function execute(Server $server) + public function execute(Server $server): void { if (! server_supports_feature($server, self::WIRE_VERSION_FOR_QUERYABLE_ENCRYPTION_V2)) { throw new UnsupportedException('Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption.'); @@ -170,10 +169,8 @@ public function execute(Server $server) $createMetadataCollection->execute($server); } - $result = $this->createCollection->execute($server); + $this->createCollection->execute($server); $this->createSafeContentIndex->execute($server); - - return $result; } } diff --git a/src/Operation/CreateIndexes.php b/src/Operation/CreateIndexes.php index e2ac08813..6591270b7 100644 --- a/src/Operation/CreateIndexes.php +++ b/src/Operation/CreateIndexes.php @@ -40,10 +40,8 @@ * @see \MongoDB\Collection::createIndex() * @see \MongoDB\Collection::createIndexes() * @see https://mongodb.com/docs/manual/reference/command/createIndexes/ - * - * @final extending this class will not be supported in v2.0.0 */ -class CreateIndexes implements Executable +final class CreateIndexes { private const WIRE_VERSION_FOR_COMMIT_QUORUM = 9; @@ -118,12 +116,11 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return string[] The names of the created indexes * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { diff --git a/src/Operation/CreateSearchIndexes.php b/src/Operation/CreateSearchIndexes.php index d5be61116..d21ed9428 100644 --- a/src/Operation/CreateSearchIndexes.php +++ b/src/Operation/CreateSearchIndexes.php @@ -36,10 +36,8 @@ * @see \MongoDB\Collection::createSearchIndex() * @see \MongoDB\Collection::createSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/createSearchIndexes/ - * - * @final extending this class will not be supported in v2.0.0 */ -class CreateSearchIndexes implements Executable +final class CreateSearchIndexes { private array $indexes = []; @@ -70,7 +68,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return string[] The names of the created indexes * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/DatabaseCommand.php b/src/Operation/DatabaseCommand.php index 7ea406baa..567bbff9e 100644 --- a/src/Operation/DatabaseCommand.php +++ b/src/Operation/DatabaseCommand.php @@ -18,7 +18,7 @@ namespace MongoDB\Operation; use MongoDB\Driver\Command; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Server; use MongoDB\Driver\Session; @@ -31,10 +31,8 @@ * Operation for executing a database command. * * @see \MongoDB\Database::command() - * - * @final extending this class will not be supported in v2.0.0 */ -class DatabaseCommand implements Executable +final class DatabaseCommand { private Command $command; @@ -80,13 +78,7 @@ public function __construct(private string $databaseName, array|object $command, $this->command = $command instanceof Command ? $command : new Command($command); } - /** - * Execute the operation. - * - * @see Executable::execute() - * @return Cursor - */ - public function execute(Server $server) + public function execute(Server $server): CursorInterface { $cursor = $server->executeCommand($this->databaseName, $this->command, $this->createOptions()); diff --git a/src/Operation/Delete.php b/src/Operation/Delete.php index 477cade91..7e264152f 100644 --- a/src/Operation/Delete.php +++ b/src/Operation/Delete.php @@ -42,7 +42,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/delete/ */ -class Delete implements Executable, Explainable +final class Delete implements Explainable { private const WIRE_VERSION_FOR_HINT = 9; @@ -120,12 +120,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return DeleteResult * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): DeleteResult { /* CRUD spec requires a client-side error when using "hint" with an * unacknowledged write concern on an unsupported server. */ @@ -153,9 +151,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { $cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]]; diff --git a/src/Operation/DeleteMany.php b/src/Operation/DeleteMany.php index d6d443b32..5278ee08a 100644 --- a/src/Operation/DeleteMany.php +++ b/src/Operation/DeleteMany.php @@ -28,10 +28,8 @@ * * @see \MongoDB\Collection::deleteOne() * @see https://mongodb.com/docs/manual/reference/command/delete/ - * - * @final extending this class will not be supported in v2.0.0 */ -class DeleteMany implements Executable, Explainable +final class DeleteMany implements Explainable { private Delete $delete; @@ -76,12 +74,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return DeleteResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): DeleteResult { return $this->delete->execute($server); } @@ -90,9 +86,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->delete->getCommandDocument(); } diff --git a/src/Operation/DeleteOne.php b/src/Operation/DeleteOne.php index 8bbdb45ce..44dd47ba5 100644 --- a/src/Operation/DeleteOne.php +++ b/src/Operation/DeleteOne.php @@ -28,10 +28,8 @@ * * @see \MongoDB\Collection::deleteOne() * @see https://mongodb.com/docs/manual/reference/command/delete/ - * - * @final extending this class will not be supported in v2.0.0 */ -class DeleteOne implements Executable, Explainable +final class DeleteOne implements Explainable { private Delete $delete; @@ -76,12 +74,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return DeleteResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): DeleteResult { return $this->delete->execute($server); } @@ -90,9 +86,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->delete->getCommandDocument(); } diff --git a/src/Operation/Distinct.php b/src/Operation/Distinct.php index 171a18269..09ae1f00d 100644 --- a/src/Operation/Distinct.php +++ b/src/Operation/Distinct.php @@ -39,10 +39,8 @@ * * @see \MongoDB\Collection::distinct() * @see https://mongodb.com/docs/manual/reference/command/distinct/ - * - * @final extending this class will not be supported in v2.0.0 */ -class Distinct implements Executable, Explainable +final class Distinct implements Explainable { /** * Constructs a distinct command. @@ -111,13 +109,11 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return array * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { @@ -143,9 +139,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { $cmd = $this->createCommandDocument(); diff --git a/src/Operation/DropCollection.php b/src/Operation/DropCollection.php index d19859de1..0c520801c 100644 --- a/src/Operation/DropCollection.php +++ b/src/Operation/DropCollection.php @@ -26,19 +26,14 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function current; -use function is_array; - /** * Operation for the drop command. * * @see \MongoDB\Collection::drop() * @see \MongoDB\Database::dropCollection() * @see https://mongodb.com/docs/manual/reference/command/drop/ - * - * @final extending this class will not be supported in v2.0.0 */ -class DropCollection implements Executable +final class DropCollection { private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; @@ -53,9 +48,6 @@ class DropCollection implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name @@ -69,10 +61,6 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); - } - if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } @@ -85,12 +73,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { @@ -98,23 +84,16 @@ public function execute(Server $server) } try { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } catch (CommandException $e) { /* The server may return an error if the collection does not exist. - * Check for an error code and return the command reply instead of - * throwing. */ + * Ignore the exception to make the drop operation idempotent */ if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND) { - return $e->getResultDocument(); + return; } throw $e; } - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); } /** diff --git a/src/Operation/DropDatabase.php b/src/Operation/DropDatabase.php index 290887900..1cc71a4c0 100644 --- a/src/Operation/DropDatabase.php +++ b/src/Operation/DropDatabase.php @@ -24,19 +24,14 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; -use function current; -use function is_array; - /** * Operation for the dropDatabase command. * * @see \MongoDB\Client::dropDatabase() * @see \MongoDB\Database::drop() * @see https://mongodb.com/docs/manual/reference/command/dropDatabase/ - * - * @final extending this class will not be supported in v2.0.0 */ -class DropDatabase implements Executable +final class DropDatabase { /** * Constructs a dropDatabase command. @@ -49,9 +44,6 @@ class DropDatabase implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name @@ -64,10 +56,6 @@ public function __construct(private string $databaseName, private array $options throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); - } - if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } @@ -80,19 +68,11 @@ public function __construct(private string $databaseName, private array $options /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } /** diff --git a/src/Operation/DropEncryptedCollection.php b/src/Operation/DropEncryptedCollection.php index 491a8e9eb..6caa07d2a 100644 --- a/src/Operation/DropEncryptedCollection.php +++ b/src/Operation/DropEncryptedCollection.php @@ -37,7 +37,7 @@ * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#drop-collection-helper * @see https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/manage-collections/ */ -class DropEncryptedCollection implements Executable +final class DropEncryptedCollection { private DropCollection $dropCollection; @@ -84,17 +84,13 @@ public function __construct(string $databaseName, string $collectionName, array $this->dropCollection = new DropCollection($databaseName, $collectionName, $options); } - /** - * @see Executable::execute() - * @return array|object Command result document from dropping the encrypted collection - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) + /** @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ + public function execute(Server $server): void { foreach ($this->dropMetadataCollections as $dropMetadataCollection) { $dropMetadataCollection->execute($server); } - return $this->dropCollection->execute($server); + $this->dropCollection->execute($server); } } diff --git a/src/Operation/DropIndexes.php b/src/Operation/DropIndexes.php index 8fde88b4d..46a89f22f 100644 --- a/src/Operation/DropIndexes.php +++ b/src/Operation/DropIndexes.php @@ -25,8 +25,6 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function current; -use function is_array; use function is_integer; /** @@ -34,10 +32,8 @@ * * @see \MongoDB\Collection::dropIndexes() * @see https://mongodb.com/docs/manual/reference/command/dropIndexes/ - * - * @final extending this class will not be supported in v2.0.0 */ -class DropIndexes implements Executable +final class DropIndexes { /** * Constructs a dropIndexes command. @@ -53,9 +49,6 @@ class DropIndexes implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name @@ -78,10 +71,6 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); - } - if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } @@ -94,25 +83,17 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } /** diff --git a/src/Operation/DropSearchIndex.php b/src/Operation/DropSearchIndex.php index e65adee6a..c79025fbb 100644 --- a/src/Operation/DropSearchIndex.php +++ b/src/Operation/DropSearchIndex.php @@ -29,10 +29,8 @@ * * @see \MongoDB\Collection::dropSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/dropSearchIndexes/ - * - * @final extending this class will not be supported in v2.0.0 */ -class DropSearchIndex implements Executable +final class DropSearchIndex { private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; @@ -55,7 +53,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/EstimatedDocumentCount.php b/src/Operation/EstimatedDocumentCount.php index db876c71c..716996541 100644 --- a/src/Operation/EstimatedDocumentCount.php +++ b/src/Operation/EstimatedDocumentCount.php @@ -34,10 +34,8 @@ * * @see \MongoDB\Collection::estimatedDocumentCount() * @see https://mongodb.com/docs/manual/reference/command/count/ - * - * @final extending this class will not be supported in v2.0.0 */ -class EstimatedDocumentCount implements Executable, Explainable +final class EstimatedDocumentCount implements Explainable { private array $options; @@ -89,13 +87,11 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): int { return $this->createCount()->execute($server); } @@ -104,9 +100,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->createCount()->getCommandDocument(); } diff --git a/src/Operation/Executable.php b/src/Operation/Executable.php deleted file mode 100644 index 5bd8c68b3..000000000 --- a/src/Operation/Executable.php +++ /dev/null @@ -1,38 +0,0 @@ -executeCommand($this->databaseName, $this->createCommand(), $this->createOptions()); diff --git a/src/Operation/Explainable.php b/src/Operation/Explainable.php index f380e1c32..0f8717f4b 100644 --- a/src/Operation/Explainable.php +++ b/src/Operation/Explainable.php @@ -23,12 +23,10 @@ * * @internal */ -interface Explainable extends Executable +interface Explainable { /** * Returns the command document for this operation. - * - * @return array */ - public function getCommandDocument(); + public function getCommandDocument(): array; } diff --git a/src/Operation/Find.php b/src/Operation/Find.php index 52c43c525..afa7a05eb 100644 --- a/src/Operation/Find.php +++ b/src/Operation/Find.php @@ -17,7 +17,6 @@ namespace MongoDB\Operation; -use Iterator; use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; @@ -35,7 +34,6 @@ use function is_integer; use function is_object; use function is_string; -use function MongoDB\document_to_array; use function MongoDB\is_document; /** @@ -44,10 +42,8 @@ * @see \MongoDB\Collection::find() * @see https://mongodb.com/docs/manual/tutorial/query-documents/ * @see https://mongodb.com/docs/manual/reference/operator/query-modifier/ - * - * @final extending this class will not be supported in v2.0.0 */ -class Find implements Executable, Explainable +final class Find implements Explainable { public const NON_TAILABLE = 1; public const TAILABLE = 2; @@ -91,28 +87,15 @@ class Find implements Executable, Explainable * * maxAwaitTimeMS (integer): The maxium amount of time for the server to wait * on new documents to satisfy a query, if cursorType is TAILABLE_AWAIT. * - * * maxScan (integer): Maximum number of documents or index keys to scan - * when executing the query. - * - * This option has been deprecated since version 1.4. - * * * maxTimeMS (integer): The maximum amount of time to allow the query to - * run. If "$maxTimeMS" also exists in the modifiers document, this - * option will take precedence. + * run. * * * min (document): The inclusive upper bound for a specific index. * - * * modifiers (document): Meta operators that modify the output or - * behavior of a query. Use of these operators is deprecated in favor of - * named options. - * * * noCursorTimeout (boolean): The server normally times out idle cursors * after an inactivity period (10 minutes) to prevent excess memory use. * Set this option to prevent that. * - * * oplogReplay (boolean): Internal replication use only. The driver - * should not set this. This option is deprecated as of MongoDB 4.4. - * * * projection (document): Limits the fields to return for the matching * document. * @@ -131,14 +114,7 @@ class Find implements Executable, Explainable * * * skip (integer): The number of documents to skip before returning. * - * * snapshot (boolean): Prevents the cursor from returning a document more - * than once because of an intervening write operation. - * - * This options has been deprecated since version 1.4. - * - * * sort (document): The order in which to return matching documents. If - * "$orderby" also exists in the modifiers document, this option will - * take precedence. + * * sort (document): The order in which to return matching documents. * * * let (document): Map of parameter names and values. Values must be * constant or closed expressions that do not reference document fields. @@ -210,10 +186,6 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $this->options['maxAwaitTimeMS'], 'integer'); } - if (isset($this->options['maxScan']) && ! is_integer($this->options['maxScan'])) { - throw InvalidArgumentException::invalidType('"maxScan" option', $this->options['maxScan'], 'integer'); - } - if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } @@ -222,18 +194,10 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::expectedDocumentType('"min" option', $this->options['min']); } - if (isset($this->options['modifiers']) && ! is_document($this->options['modifiers'])) { - throw InvalidArgumentException::expectedDocumentType('"modifiers" option', $this->options['modifiers']); - } - if (isset($this->options['noCursorTimeout']) && ! is_bool($this->options['noCursorTimeout'])) { throw InvalidArgumentException::invalidType('"noCursorTimeout" option', $this->options['noCursorTimeout'], 'boolean'); } - if (isset($this->options['oplogReplay']) && ! is_bool($this->options['oplogReplay'])) { - throw InvalidArgumentException::invalidType('"oplogReplay" option', $this->options['oplogReplay'], 'boolean'); - } - if (isset($this->options['projection']) && ! is_document($this->options['projection'])) { throw InvalidArgumentException::expectedDocumentType('"projection" option', $this->options['projection']); } @@ -262,10 +226,6 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::invalidType('"skip" option', $this->options['skip'], 'integer'); } - if (isset($this->options['snapshot']) && ! is_bool($this->options['snapshot'])) { - throw InvalidArgumentException::invalidType('"snapshot" option', $this->options['snapshot'], 'boolean'); - } - if (isset($this->options['sort']) && ! is_document($this->options['sort'])) { throw InvalidArgumentException::expectedDocumentType('"sort" option', $this->options['sort']); } @@ -290,12 +250,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return CursorInterface&Iterator * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): CursorInterface { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { @@ -319,9 +277,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { $cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter]; @@ -334,28 +291,6 @@ public function getCommandDocument() // maxAwaitTimeMS is a Query level option so should not be considered here unset($options['maxAwaitTimeMS']); - $modifierFallback = [ - ['allowPartialResults', 'partial'], - ['comment', '$comment'], - ['hint', '$hint'], - ['maxScan', '$maxScan'], - ['max', '$max'], - ['maxTimeMS', '$maxTimeMS'], - ['min', '$min'], - ['returnKey', '$returnKey'], - ['showRecordId', '$showDiskLoc'], - ['sort', '$orderby'], - ['snapshot', '$snapshot'], - ]; - - foreach ($modifierFallback as $modifier) { - if (! isset($options[$modifier[0]]) && isset($options['modifiers'][$modifier[1]])) { - $options[$modifier[0]] = $options['modifiers'][$modifier[1]]; - } - } - - unset($options['modifiers']); - return $cmd + $options; } @@ -400,7 +335,7 @@ private function createQueryOptions(): array } } - foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) { + foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxTimeMS', 'noCursorTimeout', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'sort'] as $option) { if (isset($this->options[$option])) { $options[$option] = $this->options[$option]; } @@ -412,12 +347,6 @@ private function createQueryOptions(): array } } - if (! empty($this->options['modifiers'])) { - /** @psalm-var array|object */ - $modifiers = $this->options['modifiers']; - $options['modifiers'] = is_object($modifiers) ? document_to_array($modifiers) : $modifiers; - } - return $options; } } diff --git a/src/Operation/FindAndModify.php b/src/Operation/FindAndModify.php index e67413b6c..27a888bc6 100644 --- a/src/Operation/FindAndModify.php +++ b/src/Operation/FindAndModify.php @@ -51,7 +51,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -class FindAndModify implements Executable, Explainable +final class FindAndModify implements Explainable { private const WIRE_VERSION_FOR_HINT = 9; @@ -221,13 +221,11 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { /* Server versions >= 4.2.0 raise errors for unsupported update options. * For previous versions, the CRUD spec requires a client-side error. */ @@ -274,9 +272,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->createCommandDocument(); } diff --git a/src/Operation/FindOne.php b/src/Operation/FindOne.php index 161d01dd0..f685de5ae 100644 --- a/src/Operation/FindOne.php +++ b/src/Operation/FindOne.php @@ -30,10 +30,8 @@ * @see \MongoDB\Collection::findOne() * @see https://mongodb.com/docs/manual/tutorial/query-documents/ * @see https://mongodb.com/docs/manual/reference/operator/query-modifier/ - * - * @final extending this class will not be supported in v2.0.0 */ -class FindOne implements Executable, Explainable +final class FindOne implements Explainable { private Find $find; @@ -57,20 +55,11 @@ class FindOne implements Executable, Explainable * * * max (document): The exclusive upper bound for a specific index. * - * * maxScan (integer): Maximum number of documents or index keys to scan - * when executing the query. - * - * This option has been deprecated since version 1.4. - * * * maxTimeMS (integer): The maximum amount of time to allow the query to - * run. If "$maxTimeMS" also exists in the modifiers document, this - * option will take precedence. + * run. * * * min (document): The inclusive upper bound for a specific index. * - * * modifiers (document): Meta-operators modifying the output or behavior - * of a query. - * * * projection (document): Limits the fields to return for the matching * document. * @@ -89,9 +78,7 @@ class FindOne implements Executable, Explainable * * * skip (integer): The number of documents to skip before returning. * - * * sort (document): The order in which to return matching documents. If - * "$orderby" also exists in the modifiers document, this option will - * take precedence. + * * sort (document): The order in which to return matching documents. * * * let (document): Map of parameter names and values. Values must be * constant or closed expressions that do not reference document fields. @@ -119,12 +106,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { $cursor = $this->find->execute($server); $document = current($cursor->toArray()); @@ -136,9 +121,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->find->getCommandDocument(); } diff --git a/src/Operation/FindOneAndDelete.php b/src/Operation/FindOneAndDelete.php index a560ee4c6..7c0f22ad0 100644 --- a/src/Operation/FindOneAndDelete.php +++ b/src/Operation/FindOneAndDelete.php @@ -29,10 +29,8 @@ * * @see \MongoDB\Collection::findOneAndDelete() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ - * - * @final extending this class will not be supported in v2.0.0 */ -class FindOneAndDelete implements Executable, Explainable +final class FindOneAndDelete implements Explainable { private FindAndModify $findAndModify; @@ -109,12 +107,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { return $this->findAndModify->execute($server); } @@ -123,9 +119,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->findAndModify->getCommandDocument(); } diff --git a/src/Operation/FindOneAndReplace.php b/src/Operation/FindOneAndReplace.php index dfd761305..5ea0c5a34 100644 --- a/src/Operation/FindOneAndReplace.php +++ b/src/Operation/FindOneAndReplace.php @@ -34,10 +34,8 @@ * * @see \MongoDB\Collection::findOneAndReplace() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ - * - * @final extending this class will not be supported in v2.0.0 */ -class FindOneAndReplace implements Executable, Explainable +final class FindOneAndReplace implements Explainable { public const RETURN_DOCUMENT_BEFORE = 1; public const RETURN_DOCUMENT_AFTER = 2; @@ -152,12 +150,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { return $this->findAndModify->execute($server); } @@ -166,15 +162,13 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->findAndModify->getCommandDocument(); } - /** @return array|object */ - private function validateReplacement(array|object $replacement, ?DocumentCodec $codec) + private function validateReplacement(array|object $replacement, ?DocumentCodec $codec): array|object { if (isset($codec)) { $replacement = $codec->encode($replacement); diff --git a/src/Operation/FindOneAndUpdate.php b/src/Operation/FindOneAndUpdate.php index 3566c9822..00e97972b 100644 --- a/src/Operation/FindOneAndUpdate.php +++ b/src/Operation/FindOneAndUpdate.php @@ -33,10 +33,8 @@ * * @see \MongoDB\Collection::findOneAndUpdate() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ - * - * @final extending this class will not be supported in v2.0.0 */ -class FindOneAndUpdate implements Executable, Explainable +final class FindOneAndUpdate implements Explainable { public const RETURN_DOCUMENT_BEFORE = 1; public const RETURN_DOCUMENT_AFTER = 2; @@ -152,12 +150,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { return $this->findAndModify->execute($server); } @@ -166,9 +162,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->findAndModify->getCommandDocument(); } diff --git a/src/Operation/InsertMany.php b/src/Operation/InsertMany.php index 5e227cbc2..70e149076 100644 --- a/src/Operation/InsertMany.php +++ b/src/Operation/InsertMany.php @@ -37,10 +37,8 @@ * * @see \MongoDB\Collection::insertMany() * @see https://mongodb.com/docs/manual/reference/command/insert/ - * - * @final extending this class will not be supported in v2.0.0 */ -class InsertMany implements Executable +final class InsertMany { /** @var list */ private array $documents; @@ -116,12 +114,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return InsertManyResult * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): InsertManyResult { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { diff --git a/src/Operation/InsertOne.php b/src/Operation/InsertOne.php index 6d8f58cd8..dff9f79f6 100644 --- a/src/Operation/InsertOne.php +++ b/src/Operation/InsertOne.php @@ -35,10 +35,8 @@ * * @see \MongoDB\Collection::insertOne() * @see https://mongodb.com/docs/manual/reference/command/insert/ - * - * @final extending this class will not be supported in v2.0.0 */ -class InsertOne implements Executable +final class InsertOne { private array|object $document; @@ -99,12 +97,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return InsertOneResult * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): InsertOneResult { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if (isset($this->options['writeConcern']) && $inTransaction) { @@ -158,8 +154,7 @@ private function createExecuteOptions(): array return $options; } - /** @return array|object */ - private function validateDocument(array|object $document, ?DocumentCodec $codec) + private function validateDocument(array|object $document, ?DocumentCodec $codec): array|object { if ($codec) { $document = $codec->encode($document); diff --git a/src/Operation/ListCollectionNames.php b/src/Operation/ListCollectionNames.php index fe0ea12c6..3b32cd589 100644 --- a/src/Operation/ListCollectionNames.php +++ b/src/Operation/ListCollectionNames.php @@ -22,6 +22,7 @@ use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\CachingIterator; use MongoDB\Model\CallbackIterator; /** @@ -29,10 +30,8 @@ * * @see \MongoDB\Database::listCollectionNames() * @see https://mongodb.com/docs/manual/reference/command/listCollections/ - * - * @final extending this class will not be supported in v2.0.0 */ -class ListCollectionNames implements Executable +final class ListCollectionNames { private ListCollectionsCommand $listCollections; @@ -69,15 +68,16 @@ public function __construct(string $databaseName, array $options = []) /** * Execute the operation. * - * @see Executable::execute() - * @return Iterator + * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server): Iterator { - return new CallbackIterator( - $this->listCollections->execute($server), - fn (array $collectionInfo): string => (string) $collectionInfo['name'], + return new CachingIterator( + new CallbackIterator( + $this->listCollections->execute($server), + fn (array $collectionInfo): string => (string) $collectionInfo['name'], + ), ); } } diff --git a/src/Operation/ListCollections.php b/src/Operation/ListCollections.php index 6a877b17f..a86b0a1e5 100644 --- a/src/Operation/ListCollections.php +++ b/src/Operation/ListCollections.php @@ -22,18 +22,17 @@ use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\CachingIterator; +use MongoDB\Model\CallbackIterator; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoCommandIterator; /** * Operation for the listCollections command. * * @see \MongoDB\Database::listCollections() * @see https://mongodb.com/docs/manual/reference/command/listCollections/ - * - * @final extending this class will not be supported in v2.0.0 */ -class ListCollections implements Executable +final class ListCollections { private ListCollectionsCommand $listCollections; @@ -62,7 +61,7 @@ class ListCollections implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(private string $databaseName, array $options = []) + public function __construct(string $databaseName, array $options = []) { $this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => false] + $options); } @@ -70,12 +69,19 @@ public function __construct(private string $databaseName, array $options = []) /** * Execute the operation. * - * @see Executable::execute() * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): Iterator { - return new CollectionInfoCommandIterator($this->listCollections->execute($server), $this->databaseName); + /** @var Iterator $collections */ + $collections = $this->listCollections->execute($server); + + return new CachingIterator( + new CallbackIterator( + $collections, + fn (array $collectionInfo, int $key): CollectionInfo => new CollectionInfo($collectionInfo), + ), + ); } } diff --git a/src/Operation/ListDatabaseNames.php b/src/Operation/ListDatabaseNames.php index f7301c169..74263fb53 100644 --- a/src/Operation/ListDatabaseNames.php +++ b/src/Operation/ListDatabaseNames.php @@ -32,10 +32,8 @@ * * @see \MongoDB\Client::listDatabaseNames() * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases - * - * @final extending this class will not be supported in v2.0.0 */ -class ListDatabaseNames implements Executable +final class ListDatabaseNames { private ListDatabasesCommand $listDatabases; @@ -71,7 +69,7 @@ public function __construct(array $options = []) /** * Execute the operation. * - * @see Executable::execute() + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/ListDatabases.php b/src/Operation/ListDatabases.php index 8aa31ac1f..ab32ef7e6 100644 --- a/src/Operation/ListDatabases.php +++ b/src/Operation/ListDatabases.php @@ -17,24 +17,23 @@ namespace MongoDB\Operation; +use ArrayIterator; use Iterator; use MongoDB\Command\ListDatabases as ListDatabasesCommand; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; +use MongoDB\Model\CallbackIterator; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoLegacyIterator; /** * Operation for the ListDatabases command. * * @see \MongoDB\Client::listDatabases() - * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases - * - * @final extending this class will not be supported in v2.0.0 + * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases` */ -class ListDatabases implements Executable +final class ListDatabases { private ListDatabasesCommand $listDatabases; @@ -70,13 +69,18 @@ public function __construct(array $options = []) /** * Execute the operation. * - * @see Executable::execute() * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): Iterator { - return new DatabaseInfoLegacyIterator($this->listDatabases->execute($server)); + /** @var list $databases */ + $databases = $this->listDatabases->execute($server); + + return new CallbackIterator( + new ArrayIterator($databases), + fn (array $databaseInfo): DatabaseInfo => new DatabaseInfo($databaseInfo), + ); } } diff --git a/src/Operation/ListIndexes.php b/src/Operation/ListIndexes.php index 1116a25a6..8fab516fc 100644 --- a/src/Operation/ListIndexes.php +++ b/src/Operation/ListIndexes.php @@ -20,14 +20,15 @@ use EmptyIterator; use Iterator; use MongoDB\Driver\Command; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Model\CachingIterator; +use MongoDB\Model\CallbackIterator; use MongoDB\Model\IndexInfo; -use MongoDB\Model\IndexInfoIteratorIterator; use function is_integer; @@ -36,10 +37,8 @@ * * @see \MongoDB\Collection::listIndexes() * @see https://mongodb.com/docs/manual/reference/command/listIndexes/ - * - * @final extending this class will not be supported in v2.0.0 */ -class ListIndexes implements Executable +final class ListIndexes { private const ERROR_CODE_DATABASE_NOT_FOUND = 60; private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; @@ -77,41 +76,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) - { - return $this->executeCommand($server); - } - - /** - * Create options for executing the command. - * - * Note: read preference is intentionally omitted, as the spec requires that - * the command be executed on the primary. - * - * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } - - /** - * Returns information for all indexes for this collection using the - * listIndexes command. - * - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - private function executeCommand(Server $server): IndexInfoIteratorIterator + public function execute(Server $server): Iterator { $cmd = ['listIndexes' => $this->collectionName]; @@ -122,24 +90,45 @@ private function executeCommand(Server $server): IndexInfoIteratorIterator } try { + /** @var CursorInterface $cursor */ $cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions()); + $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); } catch (CommandException $e) { /* The server may return an error if the collection does not exist. * Check for possible error codes (see: SERVER-20463) and return an * empty iterator instead of throwing. */ if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND || $e->getCode() === self::ERROR_CODE_DATABASE_NOT_FOUND) { - return new IndexInfoIteratorIterator(new EmptyIterator()); + return new EmptyIterator(); } throw $e; } - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + return new CachingIterator( + new CallbackIterator( + $cursor, + fn (array $indexInfo): IndexInfo => new IndexInfo($indexInfo), + ), + ); + } + + /** + * Create options for executing the command. + * + * Note: read preference is intentionally omitted, as the spec requires that + * the command be executed on the primary. + * + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php + */ + private function createOptions(): array + { + $options = []; - /** @var CachingIterator $iterator */ - $iterator = new CachingIterator($cursor); + if (isset($this->options['session'])) { + $options['session'] = $this->options['session']; + } - return new IndexInfoIteratorIterator($iterator, $this->databaseName . '.' . $this->collectionName); + return $options; } } diff --git a/src/Operation/ListSearchIndexes.php b/src/Operation/ListSearchIndexes.php index 5d59725ed..ae0eb6d6f 100644 --- a/src/Operation/ListSearchIndexes.php +++ b/src/Operation/ListSearchIndexes.php @@ -34,10 +34,8 @@ * * @see \MongoDB\Collection::listSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/listSearchIndexes/ - * - * @final extending this class will not be supported in v2.0.0 */ -class ListSearchIndexes implements Executable +final class ListSearchIndexes { private array $listSearchIndexesOptions; private array $aggregateOptions; @@ -70,7 +68,6 @@ public function __construct(private string $databaseName, private string $collec * Execute the operation. * * @return Iterator&Countable - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/MapReduce.php b/src/Operation/MapReduce.php deleted file mode 100644 index a62dd2f86..000000000 --- a/src/Operation/MapReduce.php +++ /dev/null @@ -1,391 +0,0 @@ -options['bypassDocumentValidation']) && ! is_bool($this->options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $this->options['bypassDocumentValidation'], 'boolean'); - } - - if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { - throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); - } - - if (isset($this->options['finalize']) && ! $this->options['finalize'] instanceof JavascriptInterface) { - throw InvalidArgumentException::invalidType('"finalize" option', $this->options['finalize'], JavascriptInterface::class); - } - - if (isset($this->options['jsMode']) && ! is_bool($this->options['jsMode'])) { - throw InvalidArgumentException::invalidType('"jsMode" option', $this->options['jsMode'], 'boolean'); - } - - if (isset($this->options['limit']) && ! is_integer($this->options['limit'])) { - throw InvalidArgumentException::invalidType('"limit" option', $this->options['limit'], 'integer'); - } - - if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); - } - - if (isset($this->options['query']) && ! is_document($this->options['query'])) { - throw InvalidArgumentException::expectedDocumentType('"query" option', $this->options['query']); - } - - if (isset($this->options['readConcern']) && ! $this->options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $this->options['readConcern'], ReadConcern::class); - } - - if (isset($this->options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); - } - - if (isset($this->options['scope']) && ! is_document($this->options['scope'])) { - throw InvalidArgumentException::expectedDocumentType('"scope" option', $this->options['scope']); - } - - if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); - } - - if (isset($this->options['sort']) && ! is_document($this->options['sort'])) { - throw InvalidArgumentException::expectedDocumentType('"sort" option', $this->options['sort']); - } - - if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); - } - - if (isset($this->options['verbose']) && ! is_bool($this->options['verbose'])) { - throw InvalidArgumentException::invalidType('"verbose" option', $this->options['verbose'], 'boolean'); - } - - if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); - } - - if (isset($this->options['bypassDocumentValidation']) && ! $this->options['bypassDocumentValidation']) { - unset($this->options['bypassDocumentValidation']); - } - - if (isset($this->options['readConcern']) && $this->options['readConcern']->isDefault()) { - unset($this->options['readConcern']); - } - - if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { - unset($this->options['writeConcern']); - } - - // Handle deprecation of CodeWScope - if ($map->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "$map" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - if ($reduce->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "$reduce" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - if (isset($this->options['finalize']) && $this->options['finalize']->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "finalize" option for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - $this->checkOutDeprecations($out); - - $this->out = $out; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return MapReduceResult - * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if read concern or write concern is used and unsupported - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction) { - if (isset($this->options['readConcern'])) { - throw UnsupportedException::readConcernNotSupportedInTransaction(); - } - - if (isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - } - - $hasOutputCollection = ! is_mapreduce_output_inline($this->out); - - $command = $this->createCommand(); - $options = $this->createOptions($hasOutputCollection); - - /* If the mapReduce operation results in a write, use - * executeReadWriteCommand to ensure we're handling the writeConcern - * option. - * In other cases, we use executeCommand as this will prevent the - * mapReduce operation from being retried when retryReads is enabled. - * See https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst#unsupported-read-operations. */ - $cursor = $hasOutputCollection - ? $server->executeReadWriteCommand($this->databaseName, $command, $options) - : $server->executeCommand($this->databaseName, $command, $options); - - if (isset($this->options['typeMap']) && ! $hasOutputCollection) { - $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'results.$')); - } - - $result = current($cursor->toArray()); - assert($result instanceof stdClass); - - $getIterator = $this->createGetIteratorCallable($result, $server); - - return new MapReduceResult($getIterator, $result); - } - - private function checkOutDeprecations(string|array|object $out): void - { - if (is_string($out)) { - return; - } - - $out = document_to_array($out); - - if (isset($out['nonAtomic']) && ! $out['nonAtomic']) { - @trigger_error('Specifying false for "out.nonAtomic" is deprecated.', E_USER_DEPRECATED); - } - - if (isset($out['sharded']) && ! $out['sharded']) { - @trigger_error('Specifying false for "out.sharded" is deprecated.', E_USER_DEPRECATED); - } - } - - /** - * Create the mapReduce command. - */ - private function createCommand(): Command - { - $cmd = [ - 'mapReduce' => $this->collectionName, - 'map' => $this->map, - 'reduce' => $this->reduce, - 'out' => $this->out, - ]; - - foreach (['bypassDocumentValidation', 'comment', 'finalize', 'jsMode', 'limit', 'maxTimeMS', 'verbose'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - foreach (['collation', 'query', 'scope', 'sort'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = (object) $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * Creates a callable for MapReduceResult::getIterator(). - * - * @psalm-return MapReduceCallable - * @throws UnexpectedValueException if the command response was malformed - */ - private function createGetIteratorCallable(stdClass $result, Server $server): callable - { - // Inline results can be wrapped with an ArrayIterator - if (isset($result->results) && is_array($result->results)) { - $results = $result->results; - - return fn () => new ArrayIterator($results); - } - - if (isset($result->result) && (is_string($result->result) || is_object($result->result))) { - $options = isset($this->options['typeMap']) ? ['typeMap' => $this->options['typeMap']] : []; - - $find = is_string($result->result) - ? new Find($this->databaseName, $result->result, [], $options) - : new Find($result->result->db, $result->result->collection, [], $options); - - return fn () => $find->execute($server); - } - - throw new UnexpectedValueException('mapReduce command did not return inline results or an output collection'); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php - * @see https://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php - */ - private function createOptions(bool $hasOutputCollection): array - { - $options = []; - - if (isset($this->options['readConcern'])) { - $options['readConcern'] = $this->options['readConcern']; - } - - if (! $hasOutputCollection && isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if ($hasOutputCollection && isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/src/Operation/ModifyCollection.php b/src/Operation/ModifyCollection.php index f8d0c721d..7b40f2f8c 100644 --- a/src/Operation/ModifyCollection.php +++ b/src/Operation/ModifyCollection.php @@ -32,10 +32,8 @@ * * @see \MongoDB\Database::modifyCollection() * @see https://mongodb.com/docs/manual/reference/command/collMod/ - * - * @final extending this class will not be supported in v2.0.0 */ -class ModifyCollection implements Executable +final class ModifyCollection { /** * Constructs a collMod command. @@ -85,11 +83,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object { $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); diff --git a/src/Operation/RenameCollection.php b/src/Operation/RenameCollection.php index 245231a13..b3848c67e 100644 --- a/src/Operation/RenameCollection.php +++ b/src/Operation/RenameCollection.php @@ -25,8 +25,6 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function current; -use function is_array; use function is_bool; /** @@ -35,10 +33,8 @@ * @see \MongoDB\Collection::rename() * @see \MongoDB\Database::renameCollection() * @see https://mongodb.com/docs/manual/reference/command/renameCollection/ - * - * @final extending this class will not be supported in v2.0.0 */ -class RenameCollection implements Executable +final class RenameCollection { private string $fromNamespace; @@ -55,9 +51,6 @@ class RenameCollection implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * * dropTarget (boolean): If true, MongoDB will drop the target before @@ -76,10 +69,6 @@ public function __construct(string $fromDatabaseName, string $fromCollectionName throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); - } - if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } @@ -99,25 +88,17 @@ public function __construct(string $fromDatabaseName, string $fromCollectionName /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $cursor = $server->executeWriteCommand('admin', $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand('admin', $this->createCommand(), $this->createOptions()); } /** diff --git a/src/Operation/ReplaceOne.php b/src/Operation/ReplaceOne.php index 3cec99ff0..3133ea034 100644 --- a/src/Operation/ReplaceOne.php +++ b/src/Operation/ReplaceOne.php @@ -33,10 +33,8 @@ * * @see \MongoDB\Collection::replaceOne() * @see https://mongodb.com/docs/manual/reference/command/update/ - * - * @final extending this class will not be supported in v2.0.0 */ -class ReplaceOne implements Executable +final class ReplaceOne { private Update $update; @@ -105,18 +103,15 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { return $this->update->execute($server); } - /** @return array|object */ - private function validateReplacement(array|object $replacement, ?DocumentCodec $codec) + private function validateReplacement(array|object $replacement, ?DocumentCodec $codec): array|object { if ($codec) { $replacement = $codec->encode($replacement); diff --git a/src/Operation/Update.php b/src/Operation/Update.php index 0b47b1cc1..3e2da46af 100644 --- a/src/Operation/Update.php +++ b/src/Operation/Update.php @@ -45,7 +45,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/update/ */ -class Update implements Executable, Explainable +final class Update implements Explainable { private const WIRE_VERSION_FOR_HINT = 8; @@ -164,12 +164,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { /* CRUD spec requires a client-side error when using "hint" with an * unacknowledged write concern on an unsupported server. */ @@ -197,9 +195,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { $cmd = ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]]; diff --git a/src/Operation/UpdateMany.php b/src/Operation/UpdateMany.php index f600d46e6..97865288c 100644 --- a/src/Operation/UpdateMany.php +++ b/src/Operation/UpdateMany.php @@ -31,10 +31,8 @@ * * @see \MongoDB\Collection::updateMany() * @see https://mongodb.com/docs/manual/reference/command/update/ - * - * @final extending this class will not be supported in v2.0.0 */ -class UpdateMany implements Executable, Explainable +final class UpdateMany implements Explainable { private Update $update; @@ -99,12 +97,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { return $this->update->execute($server); } @@ -113,9 +109,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->update->getCommandDocument(); } diff --git a/src/Operation/UpdateOne.php b/src/Operation/UpdateOne.php index 17bc4a554..794d80058 100644 --- a/src/Operation/UpdateOne.php +++ b/src/Operation/UpdateOne.php @@ -31,10 +31,8 @@ * * @see \MongoDB\Collection::updateOne() * @see https://mongodb.com/docs/manual/reference/command/update/ - * - * @final extending this class will not be supported in v2.0.0 */ -class UpdateOne implements Executable, Explainable +final class UpdateOne implements Explainable { private Update $update; @@ -99,12 +97,10 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { return $this->update->execute($server); } @@ -113,9 +109,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument() + public function getCommandDocument(): array { return $this->update->getCommandDocument(); } diff --git a/src/Operation/UpdateSearchIndex.php b/src/Operation/UpdateSearchIndex.php index effb4a942..4543914bb 100644 --- a/src/Operation/UpdateSearchIndex.php +++ b/src/Operation/UpdateSearchIndex.php @@ -30,10 +30,8 @@ * * @see \MongoDB\Collection::updateSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/updateSearchIndexes/ - * - * @final extending this class will not be supported in v2.0.0 */ -class UpdateSearchIndex implements Executable +final class UpdateSearchIndex { private object $definition; @@ -63,7 +61,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/Watch.php b/src/Operation/Watch.php index 7e192b298..3a09fd0a1 100644 --- a/src/Operation/Watch.php +++ b/src/Operation/Watch.php @@ -17,7 +17,6 @@ namespace MongoDB\Operation; -use Iterator; use MongoDB\BSON\TimestampInterface; use MongoDB\ChangeStream; use MongoDB\Codec\DocumentCodec; @@ -56,12 +55,9 @@ * * @see \MongoDB\Collection::watch() * @see https://mongodb.com/docs/manual/changeStreams/ - * - * @final extending this class will not be supported in v2.0.0 */ -class Watch implements Executable, /* @internal */ CommandSubscriber +final class Watch implements /* @internal */ CommandSubscriber { - public const FULL_DOCUMENT_DEFAULT = 'default'; public const FULL_DOCUMENT_UPDATE_LOOKUP = 'updateLookup'; public const FULL_DOCUMENT_WHEN_AVAILABLE = 'whenAvailable'; public const FULL_DOCUMENT_REQUIRED = 'required'; @@ -267,12 +263,10 @@ public function __construct(private Manager $manager, ?string $databaseName, pri /** * Execute the operation. * - * @see Executable::execute() - * @return ChangeStream * @throws UnsupportedException if collation or read concern is used and unsupported * @throws RuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): ChangeStream { return new ChangeStream( $this->createChangeStreamIterator($server), @@ -355,10 +349,8 @@ private function createChangeStreamIterator(Server $server): ChangeStreamIterato * * The command will be executed using APM so that we can capture data from * its response (e.g. firstBatch size, postBatchResumeToken). - * - * @return CursorInterface&Iterator */ - private function executeAggregate(Server $server) + private function executeAggregate(Server $server): CursorInterface { addSubscriber($this); @@ -373,9 +365,8 @@ private function executeAggregate(Server $server) * Return the initial resume token for creating the ChangeStreamIterator. * * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#updating-the-cached-resume-token - * @return array|object|null */ - private function getInitialResumeToken() + private function getInitialResumeToken(): array|object|null { if ($this->firstBatchSize === 0 && isset($this->postBatchResumeToken)) { return $this->postBatchResumeToken; diff --git a/src/Operation/WithTransaction.php b/src/Operation/WithTransaction.php index f0b5b0ec7..760825946 100644 --- a/src/Operation/WithTransaction.php +++ b/src/Operation/WithTransaction.php @@ -11,7 +11,7 @@ use function time; /** @internal */ -class WithTransaction +final class WithTransaction { /** @var callable */ private $callback; diff --git a/src/UpdateResult.php b/src/UpdateResult.php index 58a73a890..a9d50b751 100644 --- a/src/UpdateResult.php +++ b/src/UpdateResult.php @@ -17,19 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for an update operation. */ class UpdateResult { - private bool $isAcknowledged; - public function __construct(private WriteResult $writeResult) { - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -38,16 +35,11 @@ public function __construct(private WriteResult $writeResult) * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getMatchedCount() + public function getMatchedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getMatchedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getMatchedCount(); } /** @@ -59,16 +51,11 @@ public function getMatchedCount() * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getModifiedCount() + public function getModifiedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getModifiedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getModifiedCount(); } /** @@ -77,16 +64,11 @@ public function getModifiedCount() * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedCount() + public function getUpsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getUpsertedCount(); } /** @@ -101,20 +83,15 @@ public function getUpsertedCount() * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return mixed|null - * @throws BadMethodCallException if the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedId() + public function getUpsertedId(): mixed { - if ($this->isAcknowledged) { - foreach ($this->writeResult->getUpsertedIds() as $id) { - return $id; - } - - return null; + foreach ($this->writeResult->getUpsertedIds() as $id) { + return $id; } - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return null; } /** @@ -123,11 +100,9 @@ public function getUpsertedId() * If the update was not acknowledged, other fields from the WriteResult * (e.g. matchedCount) will be undefined and their getter methods should not * be invoked. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { - return $this->isAcknowledged; + return $this->writeResult->isAcknowledged(); } } diff --git a/src/functions.php b/src/functions.php index a445467ba..aca396723 100644 --- a/src/functions.php +++ b/src/functions.php @@ -122,10 +122,9 @@ function all_servers_support_write_stage_on_secondary(array $servers): bool * @internal * @param array|object $document Document to which the type map will be applied * @param array $typeMap Type map for BSON deserialization. - * @return array|object * @throws InvalidArgumentException */ -function apply_type_map_to_document(array|object $document, array $typeMap) +function apply_type_map_to_document(array|object $document, array $typeMap): array|object { if (! is_document($document)) { throw InvalidArgumentException::expectedDocumentType('$document', $document); @@ -183,9 +182,8 @@ function document_to_array(array|object $document): array * @see Collection::drop() * @see Database::createCollection() * @see Database::dropCollection() - * @return array|object|null */ -function get_encrypted_fields_from_driver(string $databaseName, string $collectionName, Manager $manager) +function get_encrypted_fields_from_driver(string $databaseName, string $collectionName, Manager $manager): array|object|null { $encryptedFieldsMap = (array) $manager->getEncryptedFieldsMap(); @@ -199,9 +197,8 @@ function get_encrypted_fields_from_driver(string $databaseName, string $collecti * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#collection-encryptedfields-lookup-getencryptedfields * @see Collection::drop() * @see Database::dropCollection() - * @return array|object|null */ -function get_encrypted_fields_from_server(string $databaseName, string $collectionName, Manager $manager, Server $server) +function get_encrypted_fields_from_server(string $databaseName, string $collectionName, Manager $manager, Server $server): array|object|null { // No-op if the encryptedFieldsMap autoEncryption driver option was omitted if ($manager->getEncryptedFieldsMap() === null) { @@ -390,24 +387,6 @@ function is_last_pipeline_operator_write(array $pipeline): bool return $key === '$merge' || $key === '$out'; } -/** - * Return whether the "out" option for a mapReduce operation is "inline". - * - * This is used to determine if a mapReduce command requires a primary. - * - * @internal - * @see https://mongodb.com/docs/manual/reference/command/mapReduce/#output-inline - * @param string|array|object $out Output specification - */ -function is_mapreduce_output_inline(string|array|object $out): bool -{ - if (! is_array($out) && ! is_object($out)) { - return false; - } - - return array_key_first(document_to_array($out)) === 'inline'; -} - /** * Return whether the write concern is acknowledged. * @@ -469,10 +448,9 @@ function is_string_array(mixed $input): bool * @internal * @see https://bugs.php.net/bug.php?id=49664 * @param mixed $element Value to be copied - * @return mixed * @throws ReflectionException */ -function recursive_copy(mixed $element) +function recursive_copy(mixed $element): mixed { if (is_array($element)) { foreach ($element as $key => $value) { diff --git a/stubs/Driver/Cursor.stub.php b/stubs/Driver/Cursor.stub.php new file mode 100644 index 000000000..2ee403832 --- /dev/null +++ b/stubs/Driver/Cursor.stub.php @@ -0,0 +1,59 @@ + + */ +final class Cursor implements CursorInterface +{ + /** + * @return TValue|null + * @psalm-ignore-nullable-return + */ + public function current(): array|object|null + { + } + + public function next(): void + { + } + + /** @psalm-ignore-nullable-return */ + public function key(): ?int + { + } + + public function valid(): bool + { + } + + public function rewind(): void + { + } + + /** @return array */ + public function toArray(): array + { + } + + public function getId(): Int64 + { + } + + public function getServer(): Server + { + } + + public function isDead(): bool + { + } + + public function setTypeMap(array $typemap): void + { + } +} diff --git a/stubs/Driver/CursorInterface.stub.php b/stubs/Driver/CursorInterface.stub.php new file mode 100644 index 000000000..d2a89737a --- /dev/null +++ b/stubs/Driver/CursorInterface.stub.php @@ -0,0 +1,33 @@ + + */ +interface CursorInterface extends Iterator +{ + /** + * @return TValue|null + * @psalm-ignore-nullable-return + */ + public function current(): array|object|null; + + public function getId(): Int64; + + public function getServer(): Server; + + public function isDead(): bool; + + /** @psalm-ignore-nullable-return */ + public function key(): ?int; + + public function setTypeMap(array $typemap): void; + + /** @return array */ + public function toArray(): array; +} diff --git a/stubs/Driver/WriteResult.stub.php b/stubs/Driver/WriteResult.stub.php new file mode 100644 index 000000000..daa2f4eb1 --- /dev/null +++ b/stubs/Driver/WriteResult.stub.php @@ -0,0 +1,32 @@ +manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->client->dropDatabase($this->getDatabaseName()); - $this->assertCommandSucceeded($commandResult); + $this->client->dropDatabase($this->getDatabaseName()); $this->assertCollectionCount($this->getNamespace(), 0); } @@ -62,7 +61,7 @@ public function testListDatabases(): void $databases = $this->client->listDatabases(); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); foreach ($databases as $database) { $this->assertInstanceOf(DatabaseInfo::class, $database); diff --git a/tests/Collection/CollectionFunctionalTest.php b/tests/Collection/CollectionFunctionalTest.php index 89bb26800..27e3b7d1d 100644 --- a/tests/Collection/CollectionFunctionalTest.php +++ b/tests/Collection/CollectionFunctionalTest.php @@ -3,7 +3,6 @@ namespace MongoDB\Tests\Collection; use Closure; -use MongoDB\BSON\Javascript; use MongoDB\Codec\Encoder; use MongoDB\Collection; use MongoDB\Database; @@ -14,11 +13,9 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use MongoDB\MapReduceResult; use MongoDB\Operation\Count; use MongoDB\Tests\CommandObserver; use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\Group; use TypeError; use function array_filter; @@ -28,7 +25,6 @@ use function json_encode; use function str_contains; use function usort; -use function version_compare; use const JSON_THROW_ON_ERROR; @@ -257,8 +253,7 @@ public function testDrop(): void $writeResult = $this->collection->insertOne(['x' => 1]); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->collection->drop(); - $this->assertCommandSucceeded($commandResult); + $this->collection->drop(); $this->assertCollectionDoesNotExist($this->getCollectionName()); } @@ -334,8 +329,7 @@ public function testRenameToSameDatabase(): void $writeResult = $this->collection->insertOne(['_id' => 1]); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->collection->rename($toCollectionName, null, ['dropTarget' => true]); - $this->assertCommandSucceeded($commandResult); + $this->collection->rename($toCollectionName, null, ['dropTarget' => true]); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName); @@ -363,8 +357,7 @@ public function testRenameToDifferentDatabase(): void $writeResult = $this->collection->insertOne(['_id' => 1]); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->collection->rename($toCollectionName, $toDatabaseName); - $this->assertCommandSucceeded($commandResult); + $this->collection->rename($toCollectionName, $toDatabaseName); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName, $toDatabaseName); @@ -423,35 +416,6 @@ public function testWithOptionsPassesOptions(): void $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - #[Group('matrix-testing-exclude-server-4.4-driver-4.0')] - #[Group('matrix-testing-exclude-server-4.4-driver-4.2')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.0')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.2')] - public function testMapReduce(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(1, this.x); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $result = $this->assertDeprecated( - fn () => $this->collection->mapReduce($map, $reduce, $out), - ); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $expected = [ - [ '_id' => 1.0, 'value' => 66.0 ], - ]; - - $this->assertSameDocuments($expected, $result); - - if (version_compare($this->getServerVersion(), '4.3.0', '<')) { - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - } - } - public static function collectionMethodClosures() { return [ @@ -664,19 +628,6 @@ function($collection, $session, $options = []) { ], */ - /* Disabled, as it's illegal to use mapReduce command in transactions - 'mapReduce' => [ - function($collection, $session, $options = []) { - $collection->mapReduce( - new \MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'), - new \MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'), - ['inline' => 1], - ['session' => $session] + $options - ); - }, 'rw' - ], - */ - 'replaceOne' => [ function ($collection, $session, $options = []): void { $collection->replaceOne( diff --git a/tests/Database/CollectionManagementFunctionalTest.php b/tests/Database/CollectionManagementFunctionalTest.php index 485bcd8e5..dc5b49974 100644 --- a/tests/Database/CollectionManagementFunctionalTest.php +++ b/tests/Database/CollectionManagementFunctionalTest.php @@ -2,9 +2,9 @@ namespace MongoDB\Tests\Database; +use Iterator; use MongoDB\Driver\BulkWrite; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoIterator; /** * Functional tests for collection management methods. @@ -16,8 +16,7 @@ public function testCreateCollection(): void $that = $this; $basicCollectionName = $this->getCollectionName() . '.basic'; - $commandResult = $this->database->createCollection($basicCollectionName); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($basicCollectionName); $this->assertCollectionExists($basicCollectionName, null, function (CollectionInfo $info) use ($that): void { $that->assertFalse($info->isCapped()); }); @@ -29,8 +28,7 @@ public function testCreateCollection(): void 'size' => 1_048_576, ]; - $commandResult = $this->database->createCollection($cappedCollectionName, $cappedCollectionOptions); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($cappedCollectionName, $cappedCollectionOptions); $this->assertCollectionExists($cappedCollectionName, null, function (CollectionInfo $info) use ($that): void { $that->assertTrue($info->isCapped()); $that->assertEquals(100, $info->getCappedMax()); @@ -46,18 +44,16 @@ public function testDropCollection(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->dropCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->dropCollection($this->getCollectionName()); $this->assertCollectionCount($this->getNamespace(), 0); } public function testListCollections(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collections = $this->database->listCollections(); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); @@ -66,14 +62,13 @@ public function testListCollections(): void public function testListCollectionsWithFilter(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collectionName = $this->getCollectionName(); $options = ['filter' => ['name' => $collectionName]]; $collections = $this->database->listCollections($options); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); @@ -83,8 +78,7 @@ public function testListCollectionsWithFilter(): void public function testListCollectionNames(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collections = $this->database->listCollectionNames(); @@ -95,8 +89,7 @@ public function testListCollectionNames(): void public function testListCollectionNamesWithFilter(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collectionName = $this->getCollectionName(); $options = ['filter' => ['name' => $collectionName]]; diff --git a/tests/Database/DatabaseFunctionalTest.php b/tests/Database/DatabaseFunctionalTest.php index d6e3ff5b7..2da985aef 100644 --- a/tests/Database/DatabaseFunctionalTest.php +++ b/tests/Database/DatabaseFunctionalTest.php @@ -139,8 +139,7 @@ public function testDrop(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->drop(); - $this->assertCommandSucceeded($commandResult); + $this->database->drop(); $this->assertCollectionCount($this->getNamespace(), 0); } @@ -152,8 +151,7 @@ public function testDropCollection(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->dropCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->dropCollection($this->getCollectionName()); $this->assertCollectionDoesNotExist($this->getCollectionName()); } @@ -219,13 +217,12 @@ public function testRenameCollectionToSameDatabase(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->renameCollection( + $this->database->renameCollection( $this->getCollectionName(), $toCollectionName, null, ['dropTarget' => true], ); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName); @@ -256,12 +253,11 @@ public function testRenameCollectionToDifferentDatabase(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->renameCollection( + $this->database->renameCollection( $this->getCollectionName(), $toCollectionName, $toDatabaseName, ); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName, $toDatabaseName); diff --git a/tests/FunctionalTestCase.php b/tests/FunctionalTestCase.php index 51c34cf3c..15229b944 100644 --- a/tests/FunctionalTestCase.php +++ b/tests/FunctionalTestCase.php @@ -242,10 +242,7 @@ public function configureFailPoint(array|stdClass $command, ?Server $server = nu $failPointServer = $server ?: $this->getPrimaryServer(); $operation = new DatabaseCommand('admin', $command); - $cursor = $operation->execute($failPointServer); - $result = $cursor->toArray()[0]; - - $this->assertCommandSucceeded($result); + $operation->execute($failPointServer); // Record the fail point so it can be disabled during tearDown() $this->configuredFailPoints[] = [$command->configureFailPoint, $failPointServer]; diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 999c33247..bf64b03ee 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -18,7 +18,6 @@ use function MongoDB\is_builder_pipeline; use function MongoDB\is_first_key_operator; use function MongoDB\is_last_pipeline_operator_write; -use function MongoDB\is_mapreduce_output_inline; use function MongoDB\is_pipeline; use function MongoDB\is_write_concern_acknowledged; @@ -161,20 +160,6 @@ public function testIsFirstKeyOperatorArgumentTypeCheck($document): void is_first_key_operator($document); } - #[DataProvider('provideDocumentCasts')] - public function testIsMapReduceOutputInlineWithDocumentValues(callable $cast): void - { - $this->assertTrue(is_mapreduce_output_inline($cast(['inline' => 1]))); - // Note: only the key is significant - $this->assertTrue(is_mapreduce_output_inline($cast(['inline' => 0]))); - $this->assertFalse(is_mapreduce_output_inline($cast(['replace' => 'collectionName']))); - } - - public function testIsMapReduceOutputInlineWithStringValue(): void - { - $this->assertFalse(is_mapreduce_output_inline('collectionName')); - } - #[DataProvider('provideTypeMapValues')] public function testCreateFieldPathTypeMap(array $expected, array $typeMap, $fieldPath = 'field'): void { diff --git a/tests/GridFS/BucketFunctionalTest.php b/tests/GridFS/BucketFunctionalTest.php index 1c239873c..b9bcf0d78 100644 --- a/tests/GridFS/BucketFunctionalTest.php +++ b/tests/GridFS/BucketFunctionalTest.php @@ -62,7 +62,6 @@ public function testValidConstructorOptions(): void 'readConcern' => new ReadConcern(ReadConcern::LOCAL), 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY, 1000), - 'disableMD5' => true, ]); } @@ -79,7 +78,6 @@ public static function provideInvalidConstructorOptions() 'bucketName' => self::getInvalidStringValues(true), 'chunkSizeBytes' => self::getInvalidIntegerValues(true), 'codec' => self::getInvalidDocumentCodecValues(), - 'disableMD5' => self::getInvalidBooleanValues(true), 'readConcern' => self::getInvalidReadConcernValues(), 'readPreference' => self::getInvalidReadPreferenceValues(), 'typeMap' => self::getInvalidArrayValues(), @@ -764,46 +762,16 @@ public function testUploadingAnEmptyFile(): void [ 'projection' => [ 'length' => 1, - 'md5' => 1, '_id' => 0, ], ], ); - $expected = [ - 'length' => 0, - 'md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ]; + $expected = ['length' => 0]; $this->assertSameDocument($expected, $fileDocument); } - public function testDisableMD5(): void - { - $options = ['disableMD5' => true]; - $id = $this->bucket->uploadFromStream('filename', self::createStream('data'), $options); - - $fileDocument = $this->filesCollection->findOne( - ['_id' => $id], - ); - - $this->assertArrayNotHasKey('md5', $fileDocument); - } - - public function testDisableMD5OptionInConstructor(): void - { - $options = ['disableMD5' => true]; - - $this->bucket = new Bucket($this->manager, $this->getDatabaseName(), $options); - $id = $this->bucket->uploadFromStream('filename', self::createStream('data')); - - $fileDocument = $this->filesCollection->findOne( - ['_id' => $id], - ); - - $this->assertArrayNotHasKey('md5', $fileDocument); - } - public function testUploadingFirstFileCreatesIndexes(): void { $this->bucket->uploadFromStream('filename', self::createStream('foo')); @@ -877,7 +845,7 @@ public function testDanglingOpenWritableStream(): void $client = MongoDB\Tests\FunctionalTestCase::createTestClient(); $database = $client->selectDatabase(getenv('MONGODB_DATABASE') ?: 'phplib_test'); $gridfs = $database->selectGridFSBucket(); - $stream = $gridfs->openUploadStream('hello.txt', ['disableMD5' => true]); + $stream = $gridfs->openUploadStream('hello.txt'); fwrite($stream, 'Hello MongoDB!'); PHP; @@ -975,7 +943,7 @@ public function testResolveStreamContextForWrite(): void $this->assertArrayHasKey('filename', $context); $this->assertSame('filename', $context['filename']); $this->assertArrayHasKey('options', $context); - $this->assertSame(['chunkSizeBytes' => 261120, 'disableMD5' => false], $context['options']); + $this->assertSame(['chunkSizeBytes' => 261120], $context['options']); } /** diff --git a/tests/GridFS/WritableStreamFunctionalTest.php b/tests/GridFS/WritableStreamFunctionalTest.php index 98b654b8a..2b22baa53 100644 --- a/tests/GridFS/WritableStreamFunctionalTest.php +++ b/tests/GridFS/WritableStreamFunctionalTest.php @@ -45,7 +45,6 @@ public static function provideInvalidConstructorOptions() { return self::createOptionDataProvider([ 'chunkSizeBytes' => self::getInvalidIntegerValues(true), - 'disableMD5' => self::getInvalidBooleanValues(true), 'metadata' => self::getInvalidDocumentValues(), ]); } @@ -72,31 +71,4 @@ public function testWriteBytesAlwaysUpdatesFileSize(): void $stream->close(); $this->assertSame(1536, $stream->getSize()); } - - #[DataProvider('provideInputDataAndExpectedMD5')] - public function testWriteBytesCalculatesMD5($input, $expectedMD5): void - { - $stream = new WritableStream($this->collectionWrapper, 'filename'); - $stream->writeBytes($input); - $stream->close(); - - $fileDocument = $this->filesCollection->findOne( - ['_id' => $stream->getFile()->_id], - ['projection' => ['md5' => 1, '_id' => 0]], - ); - - $this->assertSameDocument(['md5' => $expectedMD5], $fileDocument); - } - - public static function provideInputDataAndExpectedMD5() - { - return [ - ['', 'd41d8cd98f00b204e9800998ecf8427e'], - ['foobar', '3858f62230ac3c915f300c664312c63f'], - [str_repeat('foobar', 43520), '88ff0e5fcb0acb27947d736b5d69cb73'], - [str_repeat('foobar', 43521), '8ff86511c95a06a611842ceb555d8454'], - [str_repeat('foobar', 87040), '45bfa1a9ec36728ee7338d15c5a30c13'], - [str_repeat('foobar', 87041), '95e78f624f8e745bcfd2d11691fa601e'], - ]; - } } diff --git a/tests/Model/CodecCursorFunctionalTest.php b/tests/Model/CodecCursorFunctionalTest.php index d2662a138..64c7268b7 100644 --- a/tests/Model/CodecCursorFunctionalTest.php +++ b/tests/Model/CodecCursorFunctionalTest.php @@ -4,15 +4,9 @@ use MongoDB\BSON\Int64; use MongoDB\Codec\DocumentCodec; -use MongoDB\Driver\CursorId; use MongoDB\Model\CodecCursor; use MongoDB\Tests\FunctionalTestCase; -use function restore_error_handler; -use function set_error_handler; - -use const E_DEPRECATED; -use const E_USER_DEPRECATED; use const E_USER_WARNING; class CodecCursorFunctionalTest extends FunctionalTestCase @@ -34,44 +28,6 @@ public function testSetTypeMap(): void $this->assertError(E_USER_WARNING, fn () => $codecCursor->setTypeMap(['root' => 'array'])); } - public function testGetIdReturnTypeWithoutArgument(): void - { - $collection = self::createTestClient()->selectCollection($this->getDatabaseName(), $this->getCollectionName()); - $cursor = $collection->find(); - - $codecCursor = CodecCursor::fromCursor($cursor, $this->createMock(DocumentCodec::class)); - - $deprecations = []; - - try { - $previousErrorHandler = set_error_handler( - function (...$args) use (&$previousErrorHandler, &$deprecations) { - $deprecations[] = $args; - - return true; - }, - E_USER_DEPRECATED | E_DEPRECATED, - ); - - $cursorId = $codecCursor->getId(); - } finally { - restore_error_handler(); - } - - self::assertInstanceOf(CursorId::class, $cursorId); - - // Expect 2 deprecations: 1 from CodecCursor, one from Cursor - self::assertCount(2, $deprecations); - self::assertSame( - 'The method "MongoDB\Model\CodecCursor::getId" will no longer return a "MongoDB\Driver\CursorId" instance in the future. Pass "true" as argument to change to the new behavior and receive a "MongoDB\BSON\Int64" instance instead.', - $deprecations[0][1], - ); - self::assertSame( - 'MongoDB\Driver\Cursor::getId(): The method "MongoDB\Driver\Cursor::getId" will no longer return a "MongoDB\Driver\CursorId" instance in the future. Pass "true" as argument to change to the new behavior and receive a "MongoDB\BSON\Int64" instance instead.', - $deprecations[1][1], - ); - } - public function testGetIdReturnTypeWithArgument(): void { $collection = self::createTestClient()->selectCollection($this->getDatabaseName(), $this->getCollectionName()); @@ -79,24 +35,6 @@ public function testGetIdReturnTypeWithArgument(): void $codecCursor = CodecCursor::fromCursor($cursor, $this->createMock(DocumentCodec::class)); - $deprecations = []; - - try { - $previousErrorHandler = set_error_handler( - function (...$args) use (&$previousErrorHandler, &$deprecations) { - $deprecations[] = $args; - - return true; - }, - E_USER_DEPRECATED | E_DEPRECATED, - ); - - $cursorId = $codecCursor->getId(true); - } finally { - restore_error_handler(); - } - - self::assertInstanceOf(Int64::class, $cursorId); - self::assertCount(0, $deprecations); + self::assertInstanceOf(Int64::class, $codecCursor->getId()); } } diff --git a/tests/Model/IndexInfoFunctionalTest.php b/tests/Model/IndexInfoFunctionalTest.php index 33537382f..824507ec6 100644 --- a/tests/Model/IndexInfoFunctionalTest.php +++ b/tests/Model/IndexInfoFunctionalTest.php @@ -4,7 +4,6 @@ use MongoDB\Collection; use MongoDB\Tests\FunctionalTestCase; -use PHPUnit\Framework\Attributes\Group; class IndexInfoFunctionalTest extends FunctionalTestCase { @@ -33,27 +32,6 @@ public function testIs2dSphere(): void $this->assertEquals(3, $index['2dsphereIndexVersion']); } - #[Group('matrix-testing-exclude-server-5.0-driver-4.0')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.2')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.4')] - public function testIsGeoHaystack(): void - { - $this->skipIfGeoHaystackIndexIsNotSupported(); - - $indexName = $this->collection->createIndex(['pos' => 'geoHaystack', 'x' => 1], ['bucketSize' => 5]); - $result = $this->collection->listIndexes(); - - $result->rewind(); - $result->next(); - $index = $result->current(); - - $this->assertEquals($indexName, $index->getName()); - $this->assertDeprecated(function () use ($index): void { - $this->assertTrue($index->isGeoHaystack()); - }); - $this->assertEquals(5, $index['bucketSize']); - } - public function testIsText(): void { $indexName = $this->collection->createIndex(['x' => 'text']); diff --git a/tests/Model/IndexInfoTest.php b/tests/Model/IndexInfoTest.php index 30cb26aea..189b522bd 100644 --- a/tests/Model/IndexInfoTest.php +++ b/tests/Model/IndexInfoTest.php @@ -14,17 +14,12 @@ public function testBasicIndex(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['x' => 1], $info->getKey()); $this->assertSame('x_1', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -37,18 +32,13 @@ public function testSparseIndex(): void 'v' => 1, 'key' => ['y' => 1], 'name' => 'y_sparse', - 'ns' => 'foo.bar', 'sparse' => true, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['y' => 1], $info->getKey()); $this->assertSame('y_sparse', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertTrue($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -61,18 +51,13 @@ public function testUniqueIndex(): void 'v' => 1, 'key' => ['z' => 1], 'name' => 'z_unique', - 'ns' => 'foo.bar', 'unique' => true, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['z' => 1], $info->getKey()); $this->assertSame('z_unique', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -85,18 +70,13 @@ public function testTtlIndex(): void 'v' => 1, 'key' => ['z' => 1], 'name' => 'z_unique', - 'ns' => 'foo.bar', 'expireAfterSeconds' => 100, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['z' => 1], $info->getKey()); $this->assertSame('z_unique', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertTrue($info->isTtl()); @@ -111,7 +91,6 @@ public function testDebugInfo(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]; $info = new IndexInfo($expectedInfo); @@ -124,7 +103,6 @@ public function testImplementsArrayAccess(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->assertInstanceOf('ArrayAccess', $info); @@ -138,7 +116,6 @@ public function testOffsetSetCannotBeCalled(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->expectException(BadMethodCallException::class); @@ -152,7 +129,6 @@ public function testOffsetUnsetCannotBeCalled(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->expectException(BadMethodCallException::class); @@ -166,40 +142,12 @@ public function testIs2dSphere(): void 'v' => 2, 'key' => ['pos' => '2dsphere'], 'name' => 'pos_2dsphere', - 'ns' => 'foo.bar', ]); $this->assertSame(2, $info->getVersion()); $this->assertSame(['pos' => '2dsphere'], $info->getKey()); $this->assertSame('pos_2dsphere', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertTrue($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); - $this->assertFalse($info->isSparse()); - $this->assertFalse($info->isText()); - $this->assertFalse($info->isTtl()); - $this->assertFalse($info->isUnique()); - } - - public function testIsGeoHaystack(): void - { - $info = new IndexInfo([ - 'v' => 2, - 'key' => ['pos2' => 'geoHaystack', 'x' => 1], - 'name' => 'pos2_geoHaystack_x_1', - 'ns' => 'foo.bar', - ]); - - $this->assertSame(2, $info->getVersion()); - $this->assertSame(['pos2' => 'geoHaystack', 'x' => 1], $info->getKey()); - $this->assertSame('pos2_geoHaystack_x_1', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); - $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertTrue($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -212,17 +160,12 @@ public function testIsText(): void 'v' => 2, 'key' => ['_fts' => 'text', '_ftsx' => 1], 'name' => 'title_text_description_text', - 'ns' => 'foo.bar', ]); $this->assertSame(2, $info->getVersion()); $this->assertSame(['_fts' => 'text', '_ftsx' => 1], $info->getKey()); $this->assertSame('title_text_description_text', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertTrue($info->isText()); $this->assertFalse($info->isTtl()); diff --git a/tests/Operation/BulkWriteFunctionalTest.php b/tests/Operation/BulkWriteFunctionalTest.php index 7a9cb5df9..a597024e2 100644 --- a/tests/Operation/BulkWriteFunctionalTest.php +++ b/tests/Operation/BulkWriteFunctionalTest.php @@ -7,8 +7,8 @@ use MongoDB\BulkWriteResult; use MongoDB\Collection; use MongoDB\Driver\BulkWrite as Bulk; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\Model\BSONDocument; use MongoDB\Operation\BulkWrite; use MongoDB\Tests\CommandObserver; @@ -318,48 +318,48 @@ public function testUnacknowledgedWriteConcern() #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesDeletedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getDeletedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesMatchedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getMatchedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesModifiedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getModifiedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedIds(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedIds(); } diff --git a/tests/Operation/CreateCollectionTest.php b/tests/Operation/CreateCollectionTest.php index 4012a7ed5..4ec92b32f 100644 --- a/tests/Operation/CreateCollectionTest.php +++ b/tests/Operation/CreateCollectionTest.php @@ -25,14 +25,12 @@ public function testConstructorOptionTypeChecks(array $options): void public static function provideInvalidConstructorOptions() { return self::createOptionDataProvider([ - 'autoIndexId' => self::getInvalidBooleanValues(), 'capped' => self::getInvalidBooleanValues(), 'changeStreamPreAndPostImages' => self::getInvalidDocumentValues(), 'clusteredIndex' => self::getInvalidDocumentValues(), 'collation' => self::getInvalidDocumentValues(), 'encryptedFields' => self::getInvalidDocumentValues(), 'expireAfterSeconds' => self::getInvalidIntegerValues(), - 'flags' => self::getInvalidIntegerValues(), 'indexOptionDefaults' => self::getInvalidDocumentValues(), 'max' => self::getInvalidIntegerValues(), 'maxTimeMS' => self::getInvalidIntegerValues(), @@ -41,7 +39,6 @@ public static function provideInvalidConstructorOptions() 'size' => self::getInvalidIntegerValues(), 'storageEngine' => self::getInvalidDocumentValues(), 'timeseries' => self::getInvalidDocumentValues(), - 'typeMap' => self::getInvalidArrayValues(), 'validationAction' => self::getInvalidStringValues(), 'validationLevel' => self::getInvalidStringValues(), 'validator' => self::getInvalidDocumentValues(), @@ -49,22 +46,4 @@ public static function provideInvalidConstructorOptions() 'writeConcern' => self::getInvalidWriteConcernValues(), ]); } - - public function testAutoIndexIdOptionIsDeprecated(): void - { - $this->assertDeprecated(function (): void { - new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['autoIndexId' => true]); - }); - - $this->assertDeprecated(function (): void { - new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['autoIndexId' => false]); - }); - } - - public function testFlagsOptionIsDeprecated(): void - { - $this->assertDeprecated(function (): void { - new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['flags' => CreateCollection::USE_POWER_OF_2_SIZES]); - }); - } } diff --git a/tests/Operation/CreateEncryptedCollectionFunctionalTest.php b/tests/Operation/CreateEncryptedCollectionFunctionalTest.php index 8d4978639..69381620c 100644 --- a/tests/Operation/CreateEncryptedCollectionFunctionalTest.php +++ b/tests/Operation/CreateEncryptedCollectionFunctionalTest.php @@ -64,11 +64,10 @@ public function testCreateDataKeysNopIfFieldsIsMissing($input, array $expectedOu ['encryptedFields' => $input], ); - $operation->createDataKeys( + $encryptedFieldsOutput = $operation->createDataKeys( $this->clientEncryption, 'local', null, - $encryptedFieldsOutput, ); $this->assertSame($expectedOutput, $encryptedFieldsOutput); @@ -95,11 +94,10 @@ public function testCreateDataKeysNopIfFieldsHasInvalidType($input, array $expec ['encryptedFields' => $input], ); - $operation->createDataKeys( + $encryptedFieldsOutput = $operation->createDataKeys( $this->clientEncryption, 'local', null, - $encryptedFieldsOutput, ); $this->assertSame($expectedOutput, $encryptedFieldsOutput); @@ -126,11 +124,10 @@ public function testCreateDataKeysSkipsNonDocumentFields($input, array $expected ['encryptedFields' => $input], ); - $operation->createDataKeys( + $encryptedFieldsOutput = $operation->createDataKeys( $this->clientEncryption, 'local', null, - $encryptedFieldsOutput, ); $this->assertSame($expectedOutput, $encryptedFieldsOutput); @@ -159,11 +156,10 @@ public function testCreateDataKeysDoesNotModifyOriginalEncryptedFieldsOption(): ['encryptedFields' => $originalEncryptedFields], ); - $operation->createDataKeys( + $modifiedEncryptedFields = $operation->createDataKeys( $this->clientEncryption, 'local', null, - $modifiedEncryptedFields, ); $this->assertSame($originalField, $originalEncryptedFields->fields[0]); @@ -181,11 +177,10 @@ public function testEncryptedFieldsDocuments($input): void ['encryptedFields' => $input], ); - $operation->createDataKeys( + $modifiedEncryptedFields = $operation->createDataKeys( $this->clientEncryption, 'local', null, - $modifiedEncryptedFields, ); $this->assertInstanceOf(Binary::class, $modifiedEncryptedFields['fields'][0]['keyId'] ?? null); diff --git a/tests/Operation/DeleteFunctionalTest.php b/tests/Operation/DeleteFunctionalTest.php index b501aa21b..33b000478 100644 --- a/tests/Operation/DeleteFunctionalTest.php +++ b/tests/Operation/DeleteFunctionalTest.php @@ -5,8 +5,8 @@ use MongoDB\Collection; use MongoDB\DeleteResult; use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\Exception\UnsupportedException; use MongoDB\Operation\Delete; use MongoDB\Tests\CommandObserver; @@ -138,8 +138,8 @@ public function testUnacknowledgedWriteConcern() #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesDeletedCount(DeleteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getDeletedCount(); } diff --git a/tests/Operation/DropCollectionFunctionalTest.php b/tests/Operation/DropCollectionFunctionalTest.php index a7d317e67..3d2015521 100644 --- a/tests/Operation/DropCollectionFunctionalTest.php +++ b/tests/Operation/DropCollectionFunctionalTest.php @@ -36,9 +36,8 @@ public function testDropExistingCollection(): void $this->assertEquals(1, $writeResult->getInsertedCount()); $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $commandResult = $operation->execute($server); + $operation->execute($server); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); } @@ -48,11 +47,7 @@ public function testDropNonexistentCollection(): void $this->assertCollectionDoesNotExist($this->getCollectionName()); $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $commandResult = $operation->execute($this->getPrimaryServer()); - - /* Avoid inspecting the result document as mongos returns {ok:1.0}, - * which is inconsistent from the expected mongod response of {ok:0}. */ - $this->assertIsObject($commandResult); + $operation->execute($this->getPrimaryServer()); } public function testSessionOption(): void diff --git a/tests/Operation/DropCollectionTest.php b/tests/Operation/DropCollectionTest.php index 19dc28bd4..5aa580520 100644 --- a/tests/Operation/DropCollectionTest.php +++ b/tests/Operation/DropCollectionTest.php @@ -19,7 +19,6 @@ public static function provideInvalidConstructorOptions() { return self::createOptionDataProvider([ 'session' => self::getInvalidSessionValues(), - 'typeMap' => self::getInvalidArrayValues(), 'writeConcern' => self::getInvalidWriteConcernValues(), ]); } diff --git a/tests/Operation/DropDatabaseTest.php b/tests/Operation/DropDatabaseTest.php index cd58e6356..c2d950223 100644 --- a/tests/Operation/DropDatabaseTest.php +++ b/tests/Operation/DropDatabaseTest.php @@ -19,7 +19,6 @@ public static function provideInvalidConstructorOptions() { return self::createOptionDataProvider([ 'session' => self::getInvalidSessionValues(), - 'typeMap' => self::getInvalidArrayValues(), 'writeConcern' => self::getInvalidWriteConcernValues(), ]); } diff --git a/tests/Operation/DropIndexesFunctionalTest.php b/tests/Operation/DropIndexesFunctionalTest.php index d54c8544a..f273cb216 100644 --- a/tests/Operation/DropIndexesFunctionalTest.php +++ b/tests/Operation/DropIndexesFunctionalTest.php @@ -48,7 +48,7 @@ public function testDropOneIndexByName(): void $this->assertIndexExists('x_1'); $operation = new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), 'x_1'); - $this->assertCommandSucceeded($operation->execute($this->getPrimaryServer())); + $operation->execute($this->getPrimaryServer()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); @@ -76,7 +76,7 @@ public function testDropAllIndexesByWildcard(): void $this->assertIndexExists('y_1'); $operation = new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), '*'); - $this->assertCommandSucceeded($operation->execute($this->getPrimaryServer())); + $operation->execute($this->getPrimaryServer()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); @@ -108,7 +108,7 @@ public function testDropByIndexInfo(): void $this->assertIndexExists('x_1'); $operation = new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), $info); - $this->assertCommandSucceeded($operation->execute($this->getPrimaryServer())); + $operation->execute($this->getPrimaryServer()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); diff --git a/tests/Operation/DropIndexesTest.php b/tests/Operation/DropIndexesTest.php index cc2aff405..214330512 100644 --- a/tests/Operation/DropIndexesTest.php +++ b/tests/Operation/DropIndexesTest.php @@ -26,7 +26,6 @@ public static function provideInvalidConstructorOptions() return self::createOptionDataProvider([ 'maxTimeMS' => self::getInvalidIntegerValues(), 'session' => self::getInvalidSessionValues(), - 'typeMap' => self::getInvalidArrayValues(), 'writeConcern' => self::getInvalidWriteConcernValues(), ]); } diff --git a/tests/Operation/ExplainFunctionalTest.php b/tests/Operation/ExplainFunctionalTest.php index e40024bff..4c5949828 100644 --- a/tests/Operation/ExplainFunctionalTest.php +++ b/tests/Operation/ExplainFunctionalTest.php @@ -158,30 +158,6 @@ function (array $event): void { ); } - public function testFindModifiers(): void - { - $this->createFixtures(3); - - $operation = new Find( - $this->getDatabaseName(), - $this->getCollectionName(), - [], - ['modifiers' => ['$orderby' => ['_id' => 1]]], - ); - - (new CommandObserver())->observe( - function () use ($operation): void { - $explainOperation = new Explain($this->getDatabaseName(), $operation, ['typeMap' => ['root' => 'array', 'document' => 'array']]); - $explainOperation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $command = $event['started']->getCommand(); - $this->assertObjectHasProperty('sort', $command->explain); - $this->assertObjectNotHasProperty('modifiers', $command->explain); - }, - ); - } - #[DataProvider('provideVerbosityInformation')] public function testFindOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { diff --git a/tests/Operation/FindFunctionalTest.php b/tests/Operation/FindFunctionalTest.php index 81745c23f..0927b29fb 100644 --- a/tests/Operation/FindFunctionalTest.php +++ b/tests/Operation/FindFunctionalTest.php @@ -2,10 +2,8 @@ namespace MongoDB\Tests\Operation; -use MongoDB\BSON\Document; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\ReadPreference; -use MongoDB\Model\BSONDocument; use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\Find; use MongoDB\Tests\CommandObserver; @@ -14,7 +12,6 @@ use PHPUnit\Framework\Attributes\DataProvider; use stdClass; -use function is_array; use function microtime; class FindFunctionalTest extends FunctionalTestCase @@ -38,45 +35,6 @@ function (array $event) use ($expectedQuery): void { ); } - #[DataProvider('provideModifierDocuments')] - public function testModifierDocuments($modifiers, stdClass $expectedSort): void - { - (new CommandObserver())->observe( - function () use ($modifiers): void { - // @todo revert this lines after PHPC-2457 - if (is_array($modifiers)) { - $modifiers = [...$modifiers]; - } - - $operation = new Find( - $this->getDatabaseName(), - $this->getCollectionName(), - [], - ['modifiers' => $modifiers], - ); - - $this->assertDeprecated( - fn () => $operation->execute($this->getPrimaryServer()), - ); - }, - function (array $event) use ($expectedSort): void { - $this->assertEquals($expectedSort, $event['started']->getCommand()->sort ?? null); - }, - ); - } - - public static function provideModifierDocuments(): array - { - $expectedSort = (object) ['x' => 1]; - - return [ - 'array' => [['$orderby' => ['x' => 1]], $expectedSort], - 'object' => [(object) ['$orderby' => ['x' => 1]], $expectedSort], - 'Serializable' => [new BSONDocument(['$orderby' => ['x' => 1]]), $expectedSort], - 'Document' => [Document::fromPHP(['$orderby' => ['x' => 1]]), $expectedSort], - ]; - } - public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( diff --git a/tests/Operation/FindTest.php b/tests/Operation/FindTest.php index d7eccf7f3..97cc1d388 100644 --- a/tests/Operation/FindTest.php +++ b/tests/Operation/FindTest.php @@ -38,11 +38,8 @@ public static function provideInvalidConstructorOptions() 'limit' => self::getInvalidIntegerValues(), 'max' => self::getInvalidDocumentValues(), 'maxAwaitTimeMS' => self::getInvalidIntegerValues(), - 'maxScan' => self::getInvalidIntegerValues(), 'maxTimeMS' => self::getInvalidIntegerValues(), 'min' => self::getInvalidDocumentValues(), - 'modifiers' => self::getInvalidDocumentValues(), - 'oplogReplay' => self::getInvalidBooleanValues(), 'projection' => self::getInvalidDocumentValues(), 'readConcern' => self::getInvalidReadConcernValues(), 'readPreference' => self::getInvalidReadPreferenceValues(), @@ -50,7 +47,6 @@ public static function provideInvalidConstructorOptions() 'session' => self::getInvalidSessionValues(), 'showRecordId' => self::getInvalidBooleanValues(), 'skip' => self::getInvalidIntegerValues(), - 'snapshot' => self::getInvalidBooleanValues(), 'sort' => self::getInvalidDocumentValues(), 'typeMap' => self::getInvalidArrayValues(), ]); @@ -70,7 +66,6 @@ public static function provideInvalidConstructorCursorTypeOptions() public function testExplainableCommandDocument(): void { - // all options except deprecated "snapshot" and "maxScan" $options = [ 'allowDiskUse' => true, 'allowPartialResults' => true, @@ -83,7 +78,6 @@ public function testExplainableCommandDocument(): void 'maxTimeMS' => 100, 'min' => ['x' => 10], 'noCursorTimeout' => true, - 'oplogReplay' => true, 'projection' => ['_id' => 0], 'readConcern' => new ReadConcern(ReadConcern::LOCAL), 'returnKey' => true, @@ -94,7 +88,6 @@ public function testExplainableCommandDocument(): void // Intentionally omitted options 'cursorType' => Find::NON_TAILABLE, 'maxAwaitTimeMS' => 500, - 'modifiers' => ['foo' => 'bar'], 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], ]; @@ -111,7 +104,6 @@ public function testExplainableCommandDocument(): void 'limit' => 15, 'maxTimeMS' => 100, 'noCursorTimeout' => true, - 'oplogReplay' => true, 'projection' => ['_id' => 0], 'readConcern' => new ReadConcern(ReadConcern::LOCAL), 'returnKey' => true, diff --git a/tests/Operation/InsertManyFunctionalTest.php b/tests/Operation/InsertManyFunctionalTest.php index 68de6dfce..d8e520c98 100644 --- a/tests/Operation/InsertManyFunctionalTest.php +++ b/tests/Operation/InsertManyFunctionalTest.php @@ -5,8 +5,8 @@ use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\Collection; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\InsertManyResult; use MongoDB\Model\BSONDocument; use MongoDB\Operation\InsertMany; @@ -193,8 +193,8 @@ public function testUnacknowledgedWriteConcern() #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertManyResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } diff --git a/tests/Operation/InsertOneFunctionalTest.php b/tests/Operation/InsertOneFunctionalTest.php index 9cef81f04..204d8bacc 100644 --- a/tests/Operation/InsertOneFunctionalTest.php +++ b/tests/Operation/InsertOneFunctionalTest.php @@ -5,8 +5,8 @@ use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\Collection; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\InsertOneResult; use MongoDB\Model\BSONDocument; use MongoDB\Operation\InsertOne; @@ -183,8 +183,8 @@ public function testUnacknowledgedWriteConcern() #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertOneResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } diff --git a/tests/Operation/ListCollectionsFunctionalTest.php b/tests/Operation/ListCollectionsFunctionalTest.php index 259e6cbdb..ed8cb9b14 100644 --- a/tests/Operation/ListCollectionsFunctionalTest.php +++ b/tests/Operation/ListCollectionsFunctionalTest.php @@ -2,8 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoIterator; use MongoDB\Operation\DropDatabase; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListCollections; @@ -26,7 +26,7 @@ public function testListCollectionsForNewlyCreatedDatabase(): void $operation = new ListCollections($this->getDatabaseName(), ['filter' => ['name' => $this->getCollectionName()]]); $collections = $operation->execute($server); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); $this->assertCount(1, $collections); @@ -51,12 +51,14 @@ public function testIdIndexAndInfo(): void $operation = new ListCollections($this->getDatabaseName(), ['filter' => ['name' => $this->getCollectionName()]]); $collections = $operation->execute($server); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); $this->assertArrayHasKey('readOnly', $collection['info']); - $this->assertEquals(['v' => 2, 'key' => ['_id' => 1], 'name' => '_id_', 'ns' => $this->getNamespace()], $collection['idIndex']); + // Use assertMatchesDocument as MongoDB 4.0 and 4.2 include a ns field + // TODO: change to assertEquals when dropping support for MongoDB 4.2 + $this->assertMatchesDocument(['v' => 2, 'key' => ['_id' => 1], 'name' => '_id_'], $collection['idIndex']); } } diff --git a/tests/Operation/ListDatabasesFunctionalTest.php b/tests/Operation/ListDatabasesFunctionalTest.php index 0a6271827..f881b34dd 100644 --- a/tests/Operation/ListDatabasesFunctionalTest.php +++ b/tests/Operation/ListDatabasesFunctionalTest.php @@ -2,8 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoIterator; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListDatabases; use MongoDB\Tests\CommandObserver; @@ -30,7 +30,7 @@ function (array $event): void { }, ); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); foreach ($databases as $database) { $this->assertInstanceOf(DatabaseInfo::class, $database); @@ -65,7 +65,7 @@ public function testFilterOption(): void $operation = new ListDatabases(['filter' => ['name' => $this->getDatabaseName()]]); $databases = $operation->execute($server); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); $this->assertCount(1, $databases); diff --git a/tests/Operation/ListIndexesFunctionalTest.php b/tests/Operation/ListIndexesFunctionalTest.php index dcdcac71c..f5a2f6bc2 100644 --- a/tests/Operation/ListIndexesFunctionalTest.php +++ b/tests/Operation/ListIndexesFunctionalTest.php @@ -2,8 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\IndexInfo; -use MongoDB\Model\IndexInfoIterator; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListIndexes; use MongoDB\Tests\CommandObserver; @@ -21,14 +21,13 @@ public function testListIndexesForNewlyCreatedCollection(): void $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); - $this->assertInstanceOf(IndexInfoIterator::class, $indexes); + $this->assertInstanceOf(Iterator::class, $indexes); $this->assertCount(1, $indexes); foreach ($indexes as $index) { $this->assertInstanceOf(IndexInfo::class, $index); $this->assertEquals(['_id' => 1], $index->getKey()); - $this->assertSame($this->getNamespace(), $index->getNamespace()); } } diff --git a/tests/Operation/MapReduceFunctionalTest.php b/tests/Operation/MapReduceFunctionalTest.php deleted file mode 100644 index 5dbe45f3a..000000000 --- a/tests/Operation/MapReduceFunctionalTest.php +++ /dev/null @@ -1,314 +0,0 @@ -createCollection($this->getDatabaseName(), $this->getCollectionName()); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['readConcern' => $this->createDefaultReadConcern()], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); - }, - ); - } - - public function testDefaultWriteConcernIsOmitted(): void - { - // Collection must exist for mapReduce command - $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); - $this->dropCollection($this->getDatabaseName(), $this->getCollectionName() . '.output'); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - $this->getCollectionName() . '.output', - ['writeConcern' => $this->createDefaultWriteConcern()], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); - }, - ); - } - - public function testFinalize(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - $finalize = new Javascript('function(key, reducedValue) { return reducedValue; }'); - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['finalize' => $finalize]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertNotNull($result); - } - - public function testResult(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - - if (version_compare($this->getServerVersion(), '4.3.0', '<')) { - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - } - } - - public function testResultIncludesTimingWithVerboseOption(): void - { - $this->skipIfServerVersion('>=', '4.3.0', 'mapReduce statistics are no longer exposed'); - - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['verbose' => true]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - $this->assertNotEmpty($result->getTiming()); - } - - public function testResultDoesNotIncludeTimingWithoutVerboseOption(): void - { - $this->skipIfServerVersion('>=', '4.3.0', 'mapReduce statistics are no longer exposed'); - - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['verbose' => false]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - $this->assertEmpty($result->getTiming()); - } - - public function testSessionOption(): void - { - $this->createFixtures(3); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['session' => $this->createSession()], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); - }, - ); - } - - public function testBypassDocumentValidationSetWhenTrue(): void - { - $this->createFixtures(1); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['bypassDocumentValidation' => true], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); - $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - }, - ); - } - - public function testBypassDocumentValidationUnsetWhenFalse(): void - { - $this->createFixtures(1); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['bypassDocumentValidation' => false], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); - }, - ); - } - - #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] - public function testTypeMapOptionWithInlineResults(?array $typeMap, array $expectedDocuments): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['typeMap' => $typeMap]); - $results = iterator_to_array($operation->execute($this->getPrimaryServer())); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults($results)); - } - - public static function provideTypeMapOptionsAndExpectedDocuments() - { - return [ - [ - null, - [ - (object) ['_id' => 1, 'value' => 3], - (object) ['_id' => 2, 'value' => 6], - (object) ['_id' => 3, 'value' => 9], - ], - ], - [ - ['root' => 'array'], - [ - ['_id' => 1, 'value' => 3], - ['_id' => 2, 'value' => 6], - ['_id' => 3, 'value' => 9], - ], - ], - [ - ['root' => 'object'], - [ - (object) ['_id' => 1, 'value' => 3], - (object) ['_id' => 2, 'value' => 6], - (object) ['_id' => 3, 'value' => 9], - ], - ], - ]; - } - - #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] - public function testTypeMapOptionWithOutputCollection(?array $typeMap, array $expectedDocuments): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = $this->getCollectionName() . '.output'; - $this->dropCollection($this->getDatabaseName(), $out); - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['typeMap' => $typeMap]); - $results = iterator_to_array($operation->execute($this->getPrimaryServer())); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults($results)); - - $operation = new Find($this->getDatabaseName(), $out, [], ['typeMap' => $typeMap]); - $cursor = $operation->execute($this->getPrimaryServer()); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults(iterator_to_array($cursor))); - } - - /** - * Create data fixtures. - */ - private function createFixtures(int $n): void - { - $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); - $bulkWrite = new BulkWrite(['ordered' => true]); - - for ($i = 1; $i <= $n; $i++) { - $bulkWrite->insert(['x' => $i, 'y' => $i]); - $bulkWrite->insert(['x' => $i, 'y' => $i * 2]); - } - - $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); - - $this->assertEquals($n * 2, $result->getInsertedCount()); - } - - private function sortResults(array $results): array - { - $sortFunction = static function ($resultA, $resultB): int { - $idA = is_object($resultA) ? $resultA->_id : $resultA['_id']; - $idB = is_object($resultB) ? $resultB->_id : $resultB['_id']; - - return $idA <=> $idB; - }; - - $sortedResults = $results; - usort($sortedResults, $sortFunction); - - return $sortedResults; - } -} diff --git a/tests/Operation/MapReduceTest.php b/tests/Operation/MapReduceTest.php deleted file mode 100644 index 86af81197..000000000 --- a/tests/Operation/MapReduceTest.php +++ /dev/null @@ -1,95 +0,0 @@ -expectException(TypeError::class); - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - } - - public static function provideInvalidOutValues() - { - return self::wrapValuesForDataProvider([123, 3.14, true]); - } - - #[DataProvider('provideDeprecatedOutValues')] - public function testConstructorOutArgumentDeprecations($out): void - { - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - - $this->assertDeprecated(function () use ($map, $reduce, $out): void { - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - }); - } - - public static function provideDeprecatedOutValues(): array - { - return [ - 'nonAtomic:array' => [['nonAtomic' => false]], - 'nonAtomic:object' => [(object) ['nonAtomic' => false]], - 'nonAtomic:Serializable' => [new BSONDocument(['nonAtomic' => false])], - 'nonAtomic:Document' => [Document::fromPHP(['nonAtomic' => false])], - 'sharded:array' => [['sharded' => false]], - 'sharded:object' => [(object) ['sharded' => false]], - 'sharded:Serializable' => [new BSONDocument(['sharded' => false])], - 'sharded:Document' => [Document::fromPHP(['sharded' => false])], - ]; - } - - #[DataProvider('provideInvalidConstructorOptions')] - public function testConstructorOptionTypeChecks(array $options): void - { - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $this->expectException(InvalidArgumentException::class); - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, $options); - } - - public static function provideInvalidConstructorOptions() - { - return self::createOptionDataProvider([ - 'bypassDocumentValidation' => self::getInvalidBooleanValues(), - 'collation' => self::getInvalidDocumentValues(), - 'finalize' => self::getInvalidJavascriptValues(), - 'jsMode' => self::getInvalidBooleanValues(), - 'limit' => self::getInvalidIntegerValues(), - 'maxTimeMS' => self::getInvalidIntegerValues(), - 'query' => self::getInvalidDocumentValues(), - 'readConcern' => self::getInvalidReadConcernValues(), - 'readPreference' => self::getInvalidReadPreferenceValues(), - 'scope' => self::getInvalidDocumentValues(), - 'session' => self::getInvalidSessionValues(), - 'sort' => self::getInvalidDocumentValues(), - 'typeMap' => self::getInvalidArrayValues(), - 'verbose' => self::getInvalidBooleanValues(), - 'writeConcern' => self::getInvalidWriteConcernValues(), - ]); - } - - private static function getInvalidJavascriptValues() - { - return [123, 3.14, 'foo', true, [], new stdClass(), new ObjectId()]; - } -} diff --git a/tests/Operation/RenameCollectionFunctionalTest.php b/tests/Operation/RenameCollectionFunctionalTest.php index 9dccae0e3..37fb23d4d 100644 --- a/tests/Operation/RenameCollectionFunctionalTest.php +++ b/tests/Operation/RenameCollectionFunctionalTest.php @@ -65,9 +65,8 @@ public function testRenameCollectionToNonexistentTarget(): void $this->getDatabaseName(), $this->toCollectionName, ); - $commandResult = $operation->execute($server); + $operation->execute($server); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($this->toCollectionName); diff --git a/tests/Operation/RenameCollectionTest.php b/tests/Operation/RenameCollectionTest.php index bae80e9ee..e0a438329 100644 --- a/tests/Operation/RenameCollectionTest.php +++ b/tests/Operation/RenameCollectionTest.php @@ -26,7 +26,6 @@ public static function provideInvalidConstructorOptions() return self::createOptionDataProvider([ 'dropTarget' => self::getInvalidBooleanValues(), 'session' => self::getInvalidSessionValues(), - 'typeMap' => self::getInvalidArrayValues(), 'writeConcern' => self::getInvalidWriteConcernValues(), ]); } diff --git a/tests/Operation/UpdateFunctionalTest.php b/tests/Operation/UpdateFunctionalTest.php index 9415960f5..55c71c7e2 100644 --- a/tests/Operation/UpdateFunctionalTest.php +++ b/tests/Operation/UpdateFunctionalTest.php @@ -5,8 +5,8 @@ use MongoDB\BSON\ObjectId; use MongoDB\Collection; use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\Exception\UnsupportedException; use MongoDB\Operation\Update; use MongoDB\Tests\CommandObserver; @@ -287,32 +287,32 @@ public function testUnacknowledgedWriteConcern() #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesMatchedCount(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getMatchedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesModifiedCount(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getModifiedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedCount(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedCount(); } #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedId(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedId(); } diff --git a/tests/Operation/WatchFunctionalTest.php b/tests/Operation/WatchFunctionalTest.php index 15326f696..cec9aa259 100644 --- a/tests/Operation/WatchFunctionalTest.php +++ b/tests/Operation/WatchFunctionalTest.php @@ -720,7 +720,7 @@ public function testInitialCursorIsNotClosed(): void * reports the cursor as alive. While the cursor ID is accessed through * ChangeStream, we'll need to use reflection to access the internal * Cursor and call isDead(). */ - $this->assertNotEquals('0', (string) $changeStream->getCursorId(true)); + $this->assertNotEquals(0, $changeStream->getCursorId()); $rc = new ReflectionClass(ChangeStream::class); $iterator = $rc->getProperty('iterator')->getValue($changeStream); @@ -1366,11 +1366,11 @@ public function testOriginalReadPreferenceIsPreservedOnResume(): void } $changeStream = $operation->execute($secondary); - $previousCursorId = $changeStream->getCursorId(true); + $previousCursorId = $changeStream->getCursorId(); $this->forceChangeStreamResume($secondary); $changeStream->next(); - $this->assertNotEquals($previousCursorId, $changeStream->getCursorId(true)); + $this->assertNotEquals($previousCursorId, $changeStream->getCursorId()); $getCursor = Closure::bind( fn () => $this->iterator->getInnerIterator(), diff --git a/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php b/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php index 874671642..e5d8e72d5 100644 --- a/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php +++ b/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php @@ -69,7 +69,7 @@ public function tearDown(): void #[DataProvider('provideKmsProviderAndMasterKey')] public function testCase1_SimpleCreationAndValidation(string $kmsProvider, ?array $masterKey): void { - [$result, $encryptedFields] = $this->database->createEncryptedCollection( + $encryptedFields = $this->database->createEncryptedCollection( $this->getCollectionName(), $this->clientEncryption, $kmsProvider, @@ -77,7 +77,6 @@ public function testCase1_SimpleCreationAndValidation(string $kmsProvider, ?arra ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]], ); - $this->assertCommandSucceeded($result); $this->assertInstanceOf(Binary::class, $encryptedFields['fields'][0]['keyId'] ?? null); $this->expectException(BulkWriteException::class); @@ -140,7 +139,7 @@ public function testCase3_InvalidKeyId(string $kmsProvider, ?array $masterKey): #[DataProvider('provideKmsProviderAndMasterKey')] public function testCase4_InsertEncryptedValue(string $kmsProvider, ?array $masterKey): void { - [$result, $encryptedFields] = $this->database->createEncryptedCollection( + $encryptedFields = $this->database->createEncryptedCollection( $this->getCollectionName(), $this->clientEncryption, $kmsProvider, @@ -148,7 +147,6 @@ public function testCase4_InsertEncryptedValue(string $kmsProvider, ?array $mast ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]], ); - $this->assertCommandSucceeded($result); $this->assertInstanceOf(Binary::class, $encryptedFields['fields'][0]['keyId'] ?? null); $encrypted = $this->clientEncryption->encrypt('123-45-6789', [ diff --git a/tests/SpecTests/Operation.php b/tests/SpecTests/Operation.php index 3672314cf..d605a52aa 100644 --- a/tests/SpecTests/Operation.php +++ b/tests/SpecTests/Operation.php @@ -12,9 +12,9 @@ use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\GridFS\Bucket; -use MongoDB\MapReduceResult; use MongoDB\Operation\FindOneAndReplace; use MongoDB\Operation\FindOneAndUpdate; +use PHPUnit\Framework\Assert; use stdClass; use function array_diff_key; @@ -183,10 +183,6 @@ public function assert(FunctionalTestCase $test, Context $context, bool $bubbleE * is not used (e.g. Command Monitoring spec). */ if ($result instanceof Cursor) { $result = $result->toArray(); - } elseif ($result instanceof MapReduceResult) { - /* For mapReduce operations, we ignore the mapReduce metadata - * and only return the result iterator for evaluation. */ - $result = iterator_to_array($result->getIterator()); } } catch (Exception $e) { $exception = $e; @@ -411,12 +407,8 @@ private function executeForCollection(Collection $collection, Context $context): return $collection->listIndexes($args); case 'mapReduce': - return $collection->mapReduce( - $args['map'], - $args['reduce'], - $args['out'], - array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]), - ); + Assert::markTestSkipped('mapReduce is not supported'); + break; case 'watch': return $collection->watch( diff --git a/tests/UnifiedSpecTests/Context.php b/tests/UnifiedSpecTests/Context.php index fde8e3ee4..6b57a8130 100644 --- a/tests/UnifiedSpecTests/Context.php +++ b/tests/UnifiedSpecTests/Context.php @@ -499,7 +499,7 @@ private static function prepareCollectionOrDatabaseOptions(array $options): arra private static function prepareBucketOptions(array $options): array { - Util::assertHasOnlyKeys($options, ['bucketName', 'chunkSizeBytes', 'disableMD5', 'readConcern', 'readPreference', 'writeConcern']); + Util::assertHasOnlyKeys($options, ['bucketName', 'chunkSizeBytes', 'readConcern', 'readPreference', 'writeConcern']); if (array_key_exists('bucketName', $options)) { assertIsString($options['bucketName']); @@ -509,10 +509,6 @@ private static function prepareBucketOptions(array $options): array assertIsInt($options['chunkSizeBytes']); } - if (array_key_exists('disableMD5', $options)) { - assertIsBool($options['disableMD5']); - } - return Util::prepareCommonOptions($options); } diff --git a/tests/UnifiedSpecTests/Operation.php b/tests/UnifiedSpecTests/Operation.php index cc7cd703a..63ca8fcd9 100644 --- a/tests/UnifiedSpecTests/Operation.php +++ b/tests/UnifiedSpecTests/Operation.php @@ -3,7 +3,6 @@ namespace MongoDB\Tests\UnifiedSpecTests; use Error; -use MongoDB\BSON\Javascript; use MongoDB\ChangeStream; use MongoDB\Client; use MongoDB\Collection; @@ -526,19 +525,8 @@ private function executeForCollection(Collection $collection) ); case 'mapReduce': - assertArrayHasKey('map', $args); - assertArrayHasKey('reduce', $args); - assertArrayHasKey('out', $args); - assertInstanceOf(Javascript::class, $args['map']); - assertInstanceOf(Javascript::class, $args['reduce']); - assertThat($args['out'], logicalOr(new IsType('string'), new IsType('array'), new IsType('object'))); - - return iterator_to_array($collection->mapReduce( - $args['map'], - $args['reduce'], - $args['out'], - array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]), - )); + Assert::markTestSkipped('mapReduce operation is not supported'); + break; case 'rename': assertArrayHasKey('to', $args); diff --git a/tests/UnifiedSpecTests/UnifiedSpecTest.php b/tests/UnifiedSpecTests/UnifiedSpecTest.php index 46ce8adbe..39d893e3d 100644 --- a/tests/UnifiedSpecTests/UnifiedSpecTest.php +++ b/tests/UnifiedSpecTests/UnifiedSpecTest.php @@ -64,6 +64,10 @@ class UnifiedSpecTest extends FunctionalTestCase 'index-management/search index operations ignore read and write concern: listSearchIndexes ignores read and write concern' => 'libmongoc appends readConcern to aggregate command', // Uses an invalid object name 'run-command/runCursorCommand: does not close the cursor when receiving an empty batch' => 'Uses an invalid object name', + // GridFS deprecated fields are removed + 'gridfs/gridfs-upload-disableMD5: upload when length is 0 sans MD5' => 'Deprecated fields are removed', + 'gridfs/gridfs-upload-disableMD5: upload when length is 1 sans MD5' => 'Deprecated fields are removed', + 'gridfs/gridfs-upload: upload when contentType is provided' => 'Deprecated fields are removed', ]; /** diff --git a/tests/UnifiedSpecTests/Util.php b/tests/UnifiedSpecTests/Util.php index 4f2fb22f1..f33522a31 100644 --- a/tests/UnifiedSpecTests/Util.php +++ b/tests/UnifiedSpecTests/Util.php @@ -75,7 +75,7 @@ final class Util Database::class => [ 'aggregate' => ['pipeline', 'session', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'showExpandedEvents'], - 'createCollection' => ['collection', 'session', 'autoIndexId', 'capped', 'changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'expireAfterSeconds', 'flags', 'indexOptionDefaults', 'max', 'maxTimeMS', 'pipeline', 'size', 'storageEngine', 'timeseries', 'validationAction', 'validationLevel', 'validator', 'viewOn'], + 'createCollection' => ['collection', 'session', 'capped', 'changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'expireAfterSeconds', 'indexOptionDefaults', 'max', 'maxTimeMS', 'pipeline', 'size', 'storageEngine', 'timeseries', 'validationAction', 'validationLevel', 'validator', 'viewOn'], 'dropCollection' => ['collection', 'session'], 'listCollectionNames' => ['authorizedCollections', 'filter', 'maxTimeMS', 'session'], 'listCollections' => ['authorizedCollections', 'filter', 'maxTimeMS', 'session'], @@ -87,7 +87,7 @@ final class Util 'aggregate' => ['pipeline', 'session', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], 'bulkWrite' => ['let', 'requests', 'session', 'ordered', 'bypassDocumentValidation', 'comment'], 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'fullDocumentBeforeChange', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'comment', 'showExpandedEvents'], - 'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], 'createIndex' => ['keys', 'comment', 'commitQuorum', 'maxTimeMS', 'name', 'session', 'unique'], 'createSearchIndex' => ['model'], 'createSearchIndexes' => ['models'], @@ -101,8 +101,8 @@ final class Util 'distinct' => ['fieldName', 'filter', 'session', 'collation', 'maxTimeMS', 'comment'], 'drop' => ['session', 'comment'], 'dropSearchIndex' => ['name'], - 'find' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], - 'findOne' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'find' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], + 'findOne' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], 'findOneAndReplace' => ['let', 'returnDocument', 'filter', 'replacement', 'session', 'projection', 'returnDocument', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'new', 'remove', 'sort', 'comment'], 'rename' => ['to', 'comment', 'dropTarget'], 'replaceOne' => ['let', 'filter', 'replacement', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'], @@ -134,8 +134,8 @@ final class Util 'delete' => ['id'], 'downloadByName' => ['filename', 'revision'], 'download' => ['id'], - 'uploadWithId' => ['id', 'filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], - 'upload' => ['filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], + 'uploadWithId' => ['id', 'filename', 'source', 'chunkSizeBytes', 'metadata'], + 'upload' => ['filename', 'source', 'chunkSizeBytes', 'metadata'], ], ];