From 8ae5ebb507fbbce5bcd5603b6402797f67fcaca5 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 7 Feb 2023 21:16:54 +0800 Subject: [PATCH] MDL-77163 cachestore_mongodb: Remve from core --- cache/README.md | 3 - cache/stores/mongodb/LICENSE | 202 --- .../mongodb/MongoDB/BulkWriteResult.php | 188 --- cache/stores/mongodb/MongoDB/ChangeStream.php | 284 ---- cache/stores/mongodb/MongoDB/Client.php | 425 ------ cache/stores/mongodb/MongoDB/Collection.php | 1169 ----------------- .../MongoDB/Command/ListCollections.php | 154 --- .../mongodb/MongoDB/Command/ListDatabases.php | 157 --- cache/stores/mongodb/MongoDB/Database.php | 618 --------- cache/stores/mongodb/MongoDB/DeleteResult.php | 70 - .../Exception/BadMethodCallException.php | 47 - .../mongodb/MongoDB/Exception/Exception.php | 24 - .../Exception/InvalidArgumentException.php | 62 - .../Exception/ResumeTokenException.php | 45 - .../MongoDB/Exception/RuntimeException.php | 24 - .../Exception/UnexpectedValueException.php | 24 - .../Exception/UnsupportedException.php | 128 -- .../stores/mongodb/MongoDB/GridFS/Bucket.php | 738 ----------- .../MongoDB/GridFS/CollectionWrapper.php | 367 ------ .../GridFS/Exception/CorruptFileException.php | 68 - .../Exception/FileNotFoundException.php | 54 - .../GridFS/Exception/StreamException.php | 47 - .../mongodb/MongoDB/GridFS/ReadableStream.php | 323 ----- .../mongodb/MongoDB/GridFS/StreamWrapper.php | 330 ----- .../mongodb/MongoDB/GridFS/WritableStream.php | 311 ----- .../mongodb/MongoDB/InsertManyResult.php | 90 -- .../mongodb/MongoDB/InsertOneResult.php | 95 -- .../mongodb/MongoDB/MapReduceResult.php | 109 -- .../mongodb/MongoDB/Model/BSONArray.php | 105 -- .../mongodb/MongoDB/Model/BSONDocument.php | 109 -- .../mongodb/MongoDB/Model/BSONIterator.php | 159 --- .../mongodb/MongoDB/Model/CachingIterator.php | 172 --- .../MongoDB/Model/CallbackIterator.php | 88 -- .../MongoDB/Model/ChangeStreamIterator.php | 308 ----- .../mongodb/MongoDB/Model/CollectionInfo.php | 203 --- .../Model/CollectionInfoCommandIterator.php | 62 - .../MongoDB/Model/CollectionInfoIterator.php | 40 - .../mongodb/MongoDB/Model/DatabaseInfo.php | 145 -- .../MongoDB/Model/DatabaseInfoIterator.php | 40 - .../Model/DatabaseInfoLegacyIterator.php | 95 -- .../mongodb/MongoDB/Model/IndexInfo.php | 238 ---- .../MongoDB/Model/IndexInfoIterator.php | 40 - .../Model/IndexInfoIteratorIterator.php | 66 - .../mongodb/MongoDB/Model/IndexInput.php | 95 -- .../mongodb/MongoDB/Operation/Aggregate.php | 396 ------ .../mongodb/MongoDB/Operation/BulkWrite.php | 391 ------ .../mongodb/MongoDB/Operation/Count.php | 230 ---- .../MongoDB/Operation/CountDocuments.php | 170 --- .../MongoDB/Operation/CreateCollection.php | 323 ----- .../MongoDB/Operation/CreateIndexes.php | 207 --- .../MongoDB/Operation/DatabaseCommand.php | 127 -- .../mongodb/MongoDB/Operation/Delete.php | 250 ---- .../mongodb/MongoDB/Operation/DeleteMany.php | 99 -- .../mongodb/MongoDB/Operation/DeleteOne.php | 99 -- .../mongodb/MongoDB/Operation/Distinct.php | 222 ---- .../MongoDB/Operation/DropCollection.php | 166 --- .../MongoDB/Operation/DropDatabase.php | 139 -- .../mongodb/MongoDB/Operation/DropIndexes.php | 174 --- .../Operation/EstimatedDocumentCount.php | 133 -- .../mongodb/MongoDB/Operation/Executable.php | 38 - .../mongodb/MongoDB/Operation/Explain.php | 171 --- .../mongodb/MongoDB/Operation/Explainable.php | 36 - .../stores/mongodb/MongoDB/Operation/Find.php | 438 ------ .../MongoDB/Operation/FindAndModify.php | 329 ----- .../mongodb/MongoDB/Operation/FindOne.php | 142 -- .../MongoDB/Operation/FindOneAndDelete.php | 130 -- .../MongoDB/Operation/FindOneAndReplace.php | 173 --- .../MongoDB/Operation/FindOneAndUpdate.php | 177 --- .../mongodb/MongoDB/Operation/InsertMany.php | 198 --- .../mongodb/MongoDB/Operation/InsertOne.php | 168 --- .../MongoDB/Operation/ListCollectionNames.php | 85 -- .../MongoDB/Operation/ListCollections.php | 84 -- .../MongoDB/Operation/ListDatabaseNames.php | 84 -- .../MongoDB/Operation/ListDatabases.php | 81 -- .../mongodb/MongoDB/Operation/ListIndexes.php | 156 --- .../mongodb/MongoDB/Operation/MapReduce.php | 418 ------ .../MongoDB/Operation/ModifyCollection.php | 149 --- .../MongoDB/Operation/RenameCollection.php | 166 --- .../mongodb/MongoDB/Operation/ReplaceOne.php | 118 -- .../mongodb/MongoDB/Operation/Update.php | 305 ----- .../mongodb/MongoDB/Operation/UpdateMany.php | 128 -- .../mongodb/MongoDB/Operation/UpdateOne.php | 128 -- .../mongodb/MongoDB/Operation/Watch.php | 478 ------- .../MongoDB/Operation/WithTransaction.php | 130 -- cache/stores/mongodb/MongoDB/UpdateResult.php | 138 -- cache/stores/mongodb/MongoDB/functions.php | 569 -------- cache/stores/mongodb/addinstanceform.php | 101 -- .../mongodb/classes/privacy/provider.php | 109 -- .../mongodb/lang/en/cachestore_mongodb.php | 47 - cache/stores/mongodb/lib.php | 585 --------- cache/stores/mongodb/readme_moodle.txt | 13 - cache/stores/mongodb/settings.php | 33 - cache/stores/mongodb/tests/store_test.php | 73 - cache/stores/mongodb/thirdpartylibs.xml | 12 - cache/stores/mongodb/version.php | 29 - cache/upgrade.txt | 3 + lib/classes/component.php | 1 - lib/classes/plugin_manager.php | 4 +- lib/db/upgrade.php | 11 + version.php | 2 +- 100 files changed, 17 insertions(+), 17470 deletions(-) delete mode 100644 cache/stores/mongodb/LICENSE delete mode 100644 cache/stores/mongodb/MongoDB/BulkWriteResult.php delete mode 100644 cache/stores/mongodb/MongoDB/ChangeStream.php delete mode 100644 cache/stores/mongodb/MongoDB/Client.php delete mode 100644 cache/stores/mongodb/MongoDB/Collection.php delete mode 100644 cache/stores/mongodb/MongoDB/Command/ListCollections.php delete mode 100644 cache/stores/mongodb/MongoDB/Command/ListDatabases.php delete mode 100644 cache/stores/mongodb/MongoDB/Database.php delete mode 100644 cache/stores/mongodb/MongoDB/DeleteResult.php delete mode 100644 cache/stores/mongodb/MongoDB/Exception/BadMethodCallException.php delete mode 100644 cache/stores/mongodb/MongoDB/Exception/Exception.php delete mode 100644 cache/stores/mongodb/MongoDB/Exception/InvalidArgumentException.php delete mode 100644 cache/stores/mongodb/MongoDB/Exception/ResumeTokenException.php delete mode 100644 cache/stores/mongodb/MongoDB/Exception/RuntimeException.php delete mode 100644 cache/stores/mongodb/MongoDB/Exception/UnexpectedValueException.php delete mode 100644 cache/stores/mongodb/MongoDB/Exception/UnsupportedException.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/Bucket.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/CollectionWrapper.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/Exception/CorruptFileException.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/Exception/FileNotFoundException.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/Exception/StreamException.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/ReadableStream.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/StreamWrapper.php delete mode 100644 cache/stores/mongodb/MongoDB/GridFS/WritableStream.php delete mode 100644 cache/stores/mongodb/MongoDB/InsertManyResult.php delete mode 100644 cache/stores/mongodb/MongoDB/InsertOneResult.php delete mode 100644 cache/stores/mongodb/MongoDB/MapReduceResult.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/BSONArray.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/BSONDocument.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/BSONIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/CachingIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/CallbackIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/ChangeStreamIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/CollectionInfo.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/CollectionInfoCommandIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/CollectionInfoIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/DatabaseInfo.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/DatabaseInfoIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/DatabaseInfoLegacyIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/IndexInfo.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/IndexInfoIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/IndexInfoIteratorIterator.php delete mode 100644 cache/stores/mongodb/MongoDB/Model/IndexInput.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Aggregate.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/BulkWrite.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Count.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/CountDocuments.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/CreateCollection.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/CreateIndexes.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/DatabaseCommand.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Delete.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/DeleteMany.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/DeleteOne.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Distinct.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/DropCollection.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/DropDatabase.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/DropIndexes.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/EstimatedDocumentCount.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Executable.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Explain.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Explainable.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Find.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/FindAndModify.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/FindOne.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/FindOneAndDelete.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/FindOneAndReplace.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/FindOneAndUpdate.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/InsertMany.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/InsertOne.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/ListCollectionNames.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/ListCollections.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/ListDatabaseNames.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/ListDatabases.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/ListIndexes.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/MapReduce.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/ModifyCollection.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/RenameCollection.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/ReplaceOne.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Update.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/UpdateMany.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/UpdateOne.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/Watch.php delete mode 100644 cache/stores/mongodb/MongoDB/Operation/WithTransaction.php delete mode 100644 cache/stores/mongodb/MongoDB/UpdateResult.php delete mode 100644 cache/stores/mongodb/MongoDB/functions.php delete mode 100644 cache/stores/mongodb/addinstanceform.php delete mode 100644 cache/stores/mongodb/classes/privacy/provider.php delete mode 100644 cache/stores/mongodb/lang/en/cachestore_mongodb.php delete mode 100644 cache/stores/mongodb/lib.php delete mode 100644 cache/stores/mongodb/readme_moodle.txt delete mode 100644 cache/stores/mongodb/settings.php delete mode 100644 cache/stores/mongodb/tests/store_test.php delete mode 100644 cache/stores/mongodb/thirdpartylibs.xml delete mode 100644 cache/stores/mongodb/version.php diff --git a/cache/README.md b/cache/README.md index aae260c7ed5..f4987d83fae 100644 --- a/cache/README.md +++ b/cache/README.md @@ -242,9 +242,6 @@ Both the cache API and the cache stores have tests. Please be aware that several of the cache stores require configuration in order to be able operate in the tests. Tests for stores requiring configuration that haven't been configured will be skipped. All configuration is done in your sites config.php through definitions. -The following snippet illustrates how to configure core cache stores that require configuration. - - define('TEST_CACHESTORE_MONGODB_TESTSERVER', 'mongodb://localhost:27017'); As of Moodle 2.8 it is also possible to set the default cache stores used when running tests. You can do this by adding the following define to your config.php file: diff --git a/cache/stores/mongodb/LICENSE b/cache/stores/mongodb/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/cache/stores/mongodb/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cache/stores/mongodb/MongoDB/BulkWriteResult.php b/cache/stores/mongodb/MongoDB/BulkWriteResult.php deleted file mode 100644 index dec8d59de1e..00000000000 --- a/cache/stores/mongodb/MongoDB/BulkWriteResult.php +++ /dev/null @@ -1,188 +0,0 @@ -writeResult = $writeResult; - $this->insertedIds = $insertedIds; - $this->isAcknowledged = $writeResult->isAcknowledged(); - } - - /** - * Return the number of documents that were deleted. - * - * This method should only be called if the write was acknowledged. - * - * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getDeletedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getDeletedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return the number of documents that were inserted. - * - * This method should only be called if the write was acknowledged. - * - * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getInsertedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return a map of the inserted documents' IDs. - * - * The index of each ID in the map corresponds to each document's position - * in the bulk operation. If a document had an ID prior to inserting (i.e. - * 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() - { - return $this->insertedIds; - } - - /** - * Return the number of documents that were matched by the filter. - * - * This method should only be called if the write was acknowledged. - * - * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getMatchedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getMatchedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return the number of documents that were modified. - * - * This value is undefined (i.e. null) if the write executed as a legacy - * operation instead of command. - * - * This method should only be called if the write was acknowledged. - * - * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getModifiedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getModifiedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return the number of documents that were upserted. - * - * This method should only be called if the write was acknowledged. - * - * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getUpsertedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return a map of the upserted documents' IDs. - * - * The index of each ID in the map corresponds to each document's position - * in bulk operation. If a document had an ID prior to upserting (i.e. the - * server did not need to generate an ID), this will contain its "_id". Any - * server-generated ID will be a MongoDB\BSON\ObjectId instance. - * - * This method should only be called if the write was acknowledged. - * - * @see BulkWriteResult::isAcknowledged() - * @return array - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getUpsertedIds() - { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedIds(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return whether this update was acknowledged by the server. - * - * If the update was not acknowledged, other fields from the WriteResult - * (e.g. matchedCount) will be undefined. - * - * @return boolean - */ - public function isAcknowledged() - { - return $this->isAcknowledged; - } -} diff --git a/cache/stores/mongodb/MongoDB/ChangeStream.php b/cache/stores/mongodb/MongoDB/ChangeStream.php deleted file mode 100644 index 33c461801d3..00000000000 --- a/cache/stores/mongodb/MongoDB/ChangeStream.php +++ /dev/null @@ -1,284 +0,0 @@ -iterator = $iterator; - $this->resumeCallable = $resumeCallable; - } - - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() - { - return $this->iterator->current(); - } - - /** - * @return CursorId - */ - public function getCursorId() - { - return $this->iterator->getInnerIterator()->getId(); - } - - /** - * Returns the resume token for the iterator's current position. - * - * 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() - { - return $this->iterator->getResumeToken(); - } - - /** - * @see https://php.net/iterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() - { - if ($this->valid()) { - return $this->key; - } - - return null; - } - - /** - * @see https://php.net/iterator.next - * @return void - * @throws ResumeTokenException - */ - #[ReturnTypeWillChange] - public function next() - { - try { - $this->iterator->next(); - $this->onIteration($this->hasAdvanced); - } catch (RuntimeException $e) { - $this->resumeOrThrow($e); - } - } - - /** - * @see https://php.net/iterator.rewind - * @return void - * @throws ResumeTokenException - */ - #[ReturnTypeWillChange] - public function rewind() - { - try { - $this->iterator->rewind(); - /* Unlike next() and resume(), the decision to increment the key - * does not depend on whether the change stream has advanced. This - * ensures that multiple calls to rewind() do not alter state. */ - $this->onIteration(false); - } catch (RuntimeException $e) { - $this->resumeOrThrow($e); - } - } - - /** - * @see https://php.net/iterator.valid - * @return boolean - */ - #[ReturnTypeWillChange] - public function valid() - { - return $this->iterator->valid(); - } - - /** - * Determines if an exception is a resumable error. - * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resumable-error - */ - private function isResumableError(RuntimeException $exception): bool - { - if ($exception instanceof ConnectionException) { - return true; - } - - if (! $exception instanceof ServerException) { - return false; - } - - if ($exception->getCode() === self::$cursorNotFound) { - return true; - } - - if (server_supports_feature($this->iterator->getServer(), self::$wireVersionForResumableChangeStreamError)) { - return $exception->hasErrorLabel('ResumableChangeStreamError'); - } - - return in_array($exception->getCode(), self::$resumableErrorCodes); - } - - /** - * Perform housekeeping after an iteration event. - * - * @param boolean $incrementKey Increment $key if there is a current result - * @throws ResumeTokenException - */ - private function onIteration(bool $incrementKey): void - { - /* If the cursorId is 0, the server has invalidated the cursor and we - * will never perform another getMore nor need to resume since any - * remaining results (up to and including the invalidate event) will - * 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() === '0') { - $this->resumeCallable = null; - } - - /* Return early if there is not a current result. Avoid any attempt to - * increment the iterator's key. */ - if (! $this->valid()) { - return; - } - - if ($incrementKey) { - $this->key++; - } - - $this->hasAdvanced = true; - } - - /** - * Recreates the ChangeStreamIterator after a resumable server error. - */ - private function resume(): void - { - if (! $this->resumeCallable) { - throw new BadMethodCallException('Cannot resume a closed change stream.'); - } - - $this->iterator = call_user_func($this->resumeCallable, $this->getResumeToken(), $this->hasAdvanced); - - $this->iterator->rewind(); - - $this->onIteration($this->hasAdvanced); - } - - /** - * Either resumes after a resumable error or re-throws the exception. - * - * @throws RuntimeException - */ - private function resumeOrThrow(RuntimeException $exception): void - { - if ($this->isResumableError($exception)) { - $this->resume(); - - return; - } - - throw $exception; - } -} diff --git a/cache/stores/mongodb/MongoDB/Client.php b/cache/stores/mongodb/MongoDB/Client.php deleted file mode 100644 index bb12c3776bb..00000000000 --- a/cache/stores/mongodb/MongoDB/Client.php +++ /dev/null @@ -1,425 +0,0 @@ - BSONArray::class, - 'document' => BSONDocument::class, - 'root' => BSONDocument::class, - ]; - - /** @var string */ - private static $handshakeSeparator = ' / '; - - /** @var string|null */ - private static $version; - - /** @var Manager */ - private $manager; - - /** @var ReadConcern */ - private $readConcern; - - /** @var ReadPreference */ - private $readPreference; - - /** @var string */ - private $uri; - - /** @var array */ - private $typeMap; - - /** @var WriteConcern */ - private $writeConcern; - - /** - * Constructs a new Client instance. - * - * This is the preferred class for connecting to a MongoDB server or - * cluster of servers. It serves as a gateway for accessing individual - * databases and collections. - * - * Supported driver-specific options: - * - * * typeMap (array): Default type map for cursors and BSON documents. - * - * Other options are documented in MongoDB\Driver\Manager::__construct(). - * - * @see https://mongodb.com/docs/manual/reference/connection-string/ - * @see https://php.net/manual/en/mongodb-driver-manager.construct.php - * @see https://php.net/manual/en/mongodb.persistence.php#mongodb.persistence.typemaps - * @param string|null $uri MongoDB connection string. If none is provided, this defaults to self::DEFAULT_URI. - * @param array $uriOptions Additional connection string options - * @param array $driverOptions Driver-specific options - * @throws InvalidArgumentException for parameter/option parsing errors - * @throws DriverInvalidArgumentException for parameter/option parsing errors in the driver - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function __construct(?string $uri = null, array $uriOptions = [], array $driverOptions = []) - { - $driverOptions += ['typeMap' => self::$defaultTypeMap]; - - if (! is_array($driverOptions['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" driver option', $driverOptions['typeMap'], 'array'); - } - - if (isset($driverOptions['autoEncryption']['keyVaultClient'])) { - if ($driverOptions['autoEncryption']['keyVaultClient'] instanceof self) { - $driverOptions['autoEncryption']['keyVaultClient'] = $driverOptions['autoEncryption']['keyVaultClient']->manager; - } elseif (! $driverOptions['autoEncryption']['keyVaultClient'] instanceof Manager) { - throw InvalidArgumentException::invalidType('"keyVaultClient" autoEncryption option', $driverOptions['autoEncryption']['keyVaultClient'], [self::class, Manager::class]); - } - } - - $driverOptions['driver'] = $this->mergeDriverInfo($driverOptions['driver'] ?? []); - - $this->uri = $uri ?? self::DEFAULT_URI; - $this->typeMap = $driverOptions['typeMap']; - - unset($driverOptions['typeMap']); - - $this->manager = new Manager($uri, $uriOptions, $driverOptions); - $this->readConcern = $this->manager->getReadConcern(); - $this->readPreference = $this->manager->getReadPreference(); - $this->writeConcern = $this->manager->getWriteConcern(); - } - - /** - * 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() - { - return [ - 'manager' => $this->manager, - 'uri' => $this->uri, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - } - - /** - * Select a database. - * - * Note: databases whose names contain special characters (e.g. "-") may - * be selected with complex syntax (e.g. $client->{"that-database"}) or - * {@link selectDatabase()}. - * - * @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) - { - return $this->selectDatabase($databaseName); - } - - /** - * Return the connection string (i.e. URI). - * - * @return string - */ - public function __toString() - { - return $this->uri; - } - - /** - * Returns a ClientEncryption instance for explicit encryption and decryption - * - * @param array $options Encryption options - * - * @return ClientEncryption - */ - public function createClientEncryption(array $options) - { - if (isset($options['keyVaultClient'])) { - if ($options['keyVaultClient'] instanceof self) { - $options['keyVaultClient'] = $options['keyVaultClient']->manager; - } elseif (! $options['keyVaultClient'] instanceof Manager) { - throw InvalidArgumentException::invalidType('"keyVaultClient" option', $options['keyVaultClient'], [self::class, Manager::class]); - } - } - - return $this->manager->createClientEncryption($options); - } - - /** - * Drop a database. - * - * @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 = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new DropDatabase($databaseName, $options); - - return $operation->execute($server); - } - - /** - * Return the Manager. - * - * @return Manager - */ - public function getManager() - { - return $this->manager; - } - - /** - * Return the read concern for this client. - * - * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern - */ - public function getReadConcern() - { - return $this->readConcern; - } - - /** - * Return the read preference for this client. - * - * @return ReadPreference - */ - public function getReadPreference() - { - return $this->readPreference; - } - - /** - * Return the type map for this client. - * - * @return array - */ - public function getTypeMap() - { - return $this->typeMap; - } - - /** - * Return the write concern for this client. - * - * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern - */ - public function getWriteConcern() - { - return $this->writeConcern; - } - - /** - * List database names. - * - * @see ListDatabaseNames::__construct() for supported options - * @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) - */ - public function listDatabaseNames(array $options = []): Iterator - { - $operation = new ListDatabaseNames($options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * List databases. - * - * @see ListDatabases::__construct() for supported options - * @return DatabaseInfoIterator - * @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) - */ - public function listDatabases(array $options = []) - { - $operation = new ListDatabases($options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Select a collection. - * - * @see Collection::__construct() for supported options - * @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 = []) - { - $options += ['typeMap' => $this->typeMap]; - - return new Collection($this->manager, $databaseName, $collectionName, $options); - } - - /** - * Select a database. - * - * @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 = []) - { - $options += ['typeMap' => $this->typeMap]; - - return new Database($this->manager, $databaseName, $options); - } - - /** - * Start a new client session. - * - * @see https://php.net/manual/en/mongodb-driver-manager.startsession.php - * @param array $options Session options - * @return Session - */ - public function startSession(array $options = []) - { - return $this->manager->startSession($options); - } - - /** - * Create a change stream for watching changes to the cluster. - * - * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @param array $options Command options - * @return ChangeStream - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function watch(array $pipeline = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new Watch($this->manager, null, null, $pipeline, $options); - - return $operation->execute($server); - } - - private static function getVersion(): string - { - if (self::$version === null) { - try { - self::$version = PrettyVersions::getVersion('mongodb/mongodb')->getPrettyVersion(); - } catch (Throwable $t) { - return 'unknown'; - } - } - - return self::$version; - } - - private function mergeDriverInfo(array $driver): array - { - $mergedDriver = [ - 'name' => 'PHPLIB', - 'version' => self::getVersion(), - ]; - - if (isset($driver['name'])) { - if (! is_string($driver['name'])) { - throw InvalidArgumentException::invalidType('"name" handshake option', $driver['name'], 'string'); - } - - $mergedDriver['name'] .= self::$handshakeSeparator . $driver['name']; - } - - if (isset($driver['version'])) { - if (! is_string($driver['version'])) { - throw InvalidArgumentException::invalidType('"version" handshake option', $driver['version'], 'string'); - } - - $mergedDriver['version'] .= self::$handshakeSeparator . $driver['version']; - } - - if (isset($driver['platform'])) { - $mergedDriver['platform'] = $driver['platform']; - } - - return $mergedDriver; - } -} diff --git a/cache/stores/mongodb/MongoDB/Collection.php b/cache/stores/mongodb/MongoDB/Collection.php deleted file mode 100644 index 517cd307a9a..00000000000 --- a/cache/stores/mongodb/MongoDB/Collection.php +++ /dev/null @@ -1,1169 +0,0 @@ - BSONArray::class, - 'document' => BSONDocument::class, - 'root' => BSONDocument::class, - ]; - - /** @var integer */ - private static $wireVersionForReadConcernWithWriteStage = 8; - - /** @var string */ - private $collectionName; - - /** @var string */ - private $databaseName; - - /** @var Manager */ - private $manager; - - /** @var ReadConcern */ - private $readConcern; - - /** @var ReadPreference */ - private $readPreference; - - /** @var array */ - private $typeMap; - - /** @var WriteConcern */ - private $writeConcern; - - /** - * Constructs new Collection instance. - * - * This class provides methods for collection-specific operations, such as - * CRUD (i.e. create, read, update, and delete) and index management. - * - * Supported options: - * - * * readConcern (MongoDB\Driver\ReadConcern): The default read concern to - * use for collection operations. Defaults to the Manager's read concern. - * - * * readPreference (MongoDB\Driver\ReadPreference): The default read - * preference to use for collection operations. Defaults to the Manager's - * read preference. - * - * * typeMap (array): Default type map for cursors and BSON documents. - * - * * writeConcern (MongoDB\Driver\WriteConcern): The default write concern - * to use for collection operations. Defaults to the Manager's write - * concern. - * - * @param Manager $manager Manager instance from the driver - * @param string $databaseName Database name - * @param string $collectionName Collection name - * @param array $options Collection options - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function __construct(Manager $manager, string $databaseName, string $collectionName, array $options = []) - { - if (strlen($databaseName) < 1) { - throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); - } - - if (strlen($collectionName) < 1) { - throw new InvalidArgumentException('$collectionName is invalid: ' . $collectionName); - } - - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); - } - - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); - } - - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - $this->manager = $manager; - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); - $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; - $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); - } - - /** - * 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() - { - return [ - 'collectionName' => $this->collectionName, - 'databaseName' => $this->databaseName, - 'manager' => $this->manager, - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - } - - /** - * Return the collection namespace (e.g. "db.collection"). - * - * @see https://mongodb.com/docs/manual/core/databases-and-collections/ - * @return string - */ - public function __toString() - { - return $this->databaseName . '.' . $this->collectionName; - } - - /** - * Executes an aggregation framework pipeline on the collection. - * - * Note: this method's return value depends on the MongoDB server version - * and the "useCursor" option. If "useCursor" is true, a Cursor will be - * returned; otherwise, an ArrayIterator is returned, which wraps the - * "result" array from the command response document. - * - * @see Aggregate::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @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 = []) - { - $hasWriteStage = is_last_pipeline_operator_write($pipeline); - - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = $hasWriteStage - ? select_server_for_aggregate_write_stage($this->manager, $options) - : select_server($this->manager, $options); - - /* MongoDB 4.2 and later supports a read concern when an $out stage is - * being used, but earlier versions do not. - * - * A read concern is also not compatible with transactions. - */ - if ( - ! isset($options['readConcern']) && - ! is_in_transaction($options) && - ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage)) - ) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - if ($hasWriteStage && ! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new Aggregate($this->databaseName, $this->collectionName, $pipeline, $options); - - return $operation->execute($server); - } - - /** - * Executes multiple write operations. - * - * @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 = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new BulkWrite($this->databaseName, $this->collectionName, $operations, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Gets the number of documents matching the filter. - * - * @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 - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - * - * @deprecated 1.4 - */ - public function count($filter = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - $operation = new Count($this->databaseName, $this->collectionName, $filter, $options); - - return $operation->execute($server); - } - - /** - * Gets the number of documents matching the filter. - * - * @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($filter = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - $operation = new CountDocuments($this->databaseName, $this->collectionName, $filter, $options); - - return $operation->execute($server); - } - - /** - * Create a single index for the collection. - * - * @see Collection::createIndexes() - * @see CreateIndexes::__construct() for supported command options - * @param array|object $key Document containing fields mapped to values, - * which denote order or an index type - * @param array $options Index and command options - * @return string The name of the created index - * @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 createIndex($key, array $options = []) - { - $commandOptionKeys = ['commitQuorum' => 1, 'maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1]; - $indexOptions = array_diff_key($options, $commandOptionKeys); - $commandOptions = array_intersect_key($options, $commandOptionKeys); - - return current($this->createIndexes([['key' => $key] + $indexOptions], $commandOptions)); - } - - /** - * Create one or more indexes for the collection. - * - * Each element in the $indexes array must have a "key" document, which - * contains fields mapped to an order or type. Other options may follow. - * For example: - * - * $indexes = [ - * // Create a unique index on the "username" field - * [ 'key' => [ 'username' => 1 ], 'unique' => true ], - * // Create a 2dsphere index on the "loc" field with a custom name - * [ 'key' => [ 'loc' => '2dsphere' ], 'name' => 'geo' ], - * ]; - * - * If the "name" option is unspecified, a name will be generated from the - * "key" document. - * - * @see https://mongodb.com/docs/manual/reference/command/createIndexes/ - * @see https://mongodb.com/docs/manual/reference/method/db.collection.createIndex/ - * @see CreateIndexes::__construct() for supported command options - * @param array[] $indexes List of index specifications - * @param array $options Command options - * @return string[] The names of the created indexes - * @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 createIndexes(array $indexes, array $options = []) - { - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new CreateIndexes($this->databaseName, $this->collectionName, $indexes, $options); - - return $operation->execute($server); - } - - /** - * Deletes all documents matching the filter. - * - * @see DeleteMany::__construct() for supported 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 deleteMany($filter, array $options = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new DeleteMany($this->databaseName, $this->collectionName, $filter, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Deletes at most one document matching the filter. - * - * @see DeleteOne::__construct() for supported 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($filter, array $options = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new DeleteOne($this->databaseName, $this->collectionName, $filter, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Finds the distinct values for a specified field across the collection. - * - * @see Distinct::__construct() for supported 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, $filter = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - $operation = new Distinct($this->databaseName, $this->collectionName, $fieldName, $filter, $options); - - return $operation->execute($server); - } - - /** - * Drop this collection. - * - * @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 = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $encryptedFields = $options['encryptedFields'] - ?? get_encrypted_fields_from_driver($this->databaseName, $this->collectionName, $this->manager) - ?? get_encrypted_fields_from_server($this->databaseName, $this->collectionName, $this->manager, $server) - ?? null; - - if ($encryptedFields !== null) { - // encryptedFields is not passed to the drop command - unset($options['encryptedFields']); - - $encryptedFields = (array) $encryptedFields; - (new DropCollection($this->databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $this->collectionName . '.esc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['eccCollection'] ?? 'enxcol_.' . $this->collectionName . '.ecc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $this->collectionName . '.ecoc'))->execute($server); - } - - $operation = new DropCollection($this->databaseName, $this->collectionName, $options); - - return $operation->execute($server); - } - - /** - * Drop a single index in the collection. - * - * @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($indexName, array $options = []) - { - $indexName = (string) $indexName; - - if ($indexName === '*') { - throw new InvalidArgumentException('dropIndexes() must be used to drop multiple indexes'); - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new DropIndexes($this->databaseName, $this->collectionName, $indexName, $options); - - return $operation->execute($server); - } - - /** - * Drop all indexes in the collection. - * - * @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 = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new DropIndexes($this->databaseName, $this->collectionName, '*', $options); - - return $operation->execute($server); - } - - /** - * Gets an estimated number of documents in the collection using the collection metadata. - * - * @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 = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - $operation = new EstimatedDocumentCount($this->databaseName, $this->collectionName, $options); - - return $operation->execute($server); - } - - /** - * Explains explainable commands. - * - * @see Explain::__construct() for supported 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 = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - $operation = new Explain($this->databaseName, $explainable, $options); - - return $operation->execute($server); - } - - /** - * Finds documents matching the query. - * - * @see Find::__construct() for supported 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 Cursor - * @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($filter = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new Find($this->databaseName, $this->collectionName, $filter, $options); - - return $operation->execute($server); - } - - /** - * Finds a single document matching the query. - * - * @see FindOne::__construct() for supported 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($filter = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new FindOne($this->databaseName, $this->collectionName, $filter, $options); - - return $operation->execute($server); - } - - /** - * Finds a single document and deletes it, returning the original. - * - * The document to return may be null if no document matched the filter. - * - * @see FindOneAndDelete::__construct() for supported 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($filter, array $options = []) - { - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new FindOneAndDelete($this->databaseName, $this->collectionName, $filter, $options); - - return $operation->execute($server); - } - - /** - * Finds a single document and replaces it, returning either the original or - * the replaced document. - * - * The document to return may be null if no document matched the filter. By - * default, the original document is returned. Specify - * FindOneAndReplace::RETURN_DOCUMENT_AFTER for the "returnDocument" option - * to return the updated document. - * - * @see FindOneAndReplace::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ - * @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($filter, $replacement, array $options = []) - { - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new FindOneAndReplace($this->databaseName, $this->collectionName, $filter, $replacement, $options); - - return $operation->execute($server); - } - - /** - * Finds a single document and updates it, returning either the original or - * the updated document. - * - * The document to return may be null if no document matched the filter. By - * default, the original document is returned. Specify - * FindOneAndUpdate::RETURN_DOCUMENT_AFTER for the "returnDocument" option - * to return the updated document. - * - * @see FindOneAndReplace::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ - * @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($filter, $update, array $options = []) - { - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new FindOneAndUpdate($this->databaseName, $this->collectionName, $filter, $update, $options); - - return $operation->execute($server); - } - - /** - * Return the collection name. - * - * @return string - */ - public function getCollectionName() - { - return $this->collectionName; - } - - /** - * Return the database name. - * - * @return string - */ - public function getDatabaseName() - { - return $this->databaseName; - } - - /** - * Return the Manager. - * - * @return Manager - */ - public function getManager() - { - return $this->manager; - } - - /** - * Return the collection namespace. - * - * @see https://mongodb.com/docs/manual/reference/glossary/#term-namespace - * @return string - */ - public function getNamespace() - { - return $this->databaseName . '.' . $this->collectionName; - } - - /** - * Return the read concern for this collection. - * - * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern - */ - public function getReadConcern() - { - return $this->readConcern; - } - - /** - * Return the read preference for this collection. - * - * @return ReadPreference - */ - public function getReadPreference() - { - return $this->readPreference; - } - - /** - * Return the type map for this collection. - * - * @return array - */ - public function getTypeMap() - { - return $this->typeMap; - } - - /** - * Return the write concern for this collection. - * - * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern - */ - public function getWriteConcern() - { - return $this->writeConcern; - } - - /** - * Inserts multiple documents. - * - * @see InsertMany::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/insert/ - * @param array[]|object[] $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 = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new InsertMany($this->databaseName, $this->collectionName, $documents, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Inserts one document. - * - * @see InsertOne::__construct() for supported 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($document, array $options = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new InsertOne($this->databaseName, $this->collectionName, $document, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Returns information for all indexes for the collection. - * - * @see ListIndexes::__construct() for supported options - * @return IndexInfoIterator - * @throws InvalidArgumentException for parameter/option parsing errors - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function listIndexes(array $options = []) - { - $operation = new ListIndexes($this->databaseName, $this->collectionName, $options); - $server = select_server($this->manager, $options); - - 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, $out, array $options = []) - { - $hasOutputCollection = ! is_mapreduce_output_inline($out); - - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - // 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::RP_PRIMARY); - } - - $server = select_server($this->manager, $options); - - /* A "majority" read concern is not compatible with inline output, so - * avoid providing the Collection's read concern if it would conflict. - * - * A read concern is also not compatible with transactions. - */ - if (! isset($options['readConcern']) && ! ($hasOutputCollection && $this->readConcern->getLevel() === ReadConcern::MAJORITY) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new MapReduce($this->databaseName, $this->collectionName, $map, $reduce, $out, $options); - - return $operation->execute($server); - } - - /** - * Renames the collection. - * - * @see RenameCollection::__construct() for supported options - * @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 = []) - { - if (! isset($toDatabaseName)) { - $toDatabaseName = $this->databaseName; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new RenameCollection($this->databaseName, $this->collectionName, $toDatabaseName, $toCollectionName, $options); - - return $operation->execute($server); - } - - /** - * Replaces at most one document matching the filter. - * - * @see ReplaceOne::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/update/ - * @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($filter, $replacement, array $options = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new ReplaceOne($this->databaseName, $this->collectionName, $filter, $replacement, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Updates all documents matching the filter. - * - * @see UpdateMany::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/update/ - * @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($filter, $update, array $options = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new UpdateMany($this->databaseName, $this->collectionName, $filter, $update, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Updates at most one document matching the filter. - * - * @see UpdateOne::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/update/ - * @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($filter, $update, array $options = []) - { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new UpdateOne($this->databaseName, $this->collectionName, $filter, $update, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Create a change stream for watching changes to the collection. - * - * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @param array $options Command options - * @return ChangeStream - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function watch(array $pipeline = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - /* Although change streams require a newer version of the server than - * read concerns, perform the usual wire version check before inheriting - * the collection's read concern. In the event that the server is too - * old, this makes it more likely that users will encounter an error - * related to change streams being unsupported instead of an - * UnsupportedException regarding use of the "readConcern" option from - * the Aggregate operation class. */ - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new Watch($this->manager, $this->databaseName, $this->collectionName, $pipeline, $options); - - return $operation->execute($server); - } - - /** - * Get a clone of this collection with different 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 = []) - { - $options += [ - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - - return new Collection($this->manager, $this->databaseName, $this->collectionName, $options); - } -} diff --git a/cache/stores/mongodb/MongoDB/Command/ListCollections.php b/cache/stores/mongodb/MongoDB/Command/ListCollections.php deleted file mode 100644 index a970aeb079c..00000000000 --- a/cache/stores/mongodb/MongoDB/Command/ListCollections.php +++ /dev/null @@ -1,154 +0,0 @@ -databaseName = $databaseName; - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server): CachingIterator - { - $cursor = $server->executeReadCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - - return new CachingIterator($cursor); - } - - /** - * Create the listCollections command. - */ - private function createCommand(): Command - { - $cmd = ['listCollections' => 1]; - - if (! empty($this->options['filter'])) { - $cmd['filter'] = (object) $this->options['filter']; - } - - foreach (['authorizedCollections', 'comment', 'maxTimeMS', 'nameOnly'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * 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; - } -} diff --git a/cache/stores/mongodb/MongoDB/Command/ListDatabases.php b/cache/stores/mongodb/MongoDB/Command/ListDatabases.php deleted file mode 100644 index 4dabc6ed2ee..00000000000 --- a/cache/stores/mongodb/MongoDB/Command/ListDatabases.php +++ /dev/null @@ -1,157 +0,0 @@ -options = $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) - */ - 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'])) { - throw new UnexpectedValueException('listDatabases command did not return a "databases" array'); - } - - return $result['databases']; - } - - /** - * Create the listDatabases command. - */ - private function createCommand(): Command - { - $cmd = ['listDatabases' => 1]; - - if (! empty($this->options['filter'])) { - $cmd['filter'] = (object) $this->options['filter']; - } - - foreach (['authorizedDatabases', 'comment', 'maxTimeMS', 'nameOnly'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * 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; - } -} diff --git a/cache/stores/mongodb/MongoDB/Database.php b/cache/stores/mongodb/MongoDB/Database.php deleted file mode 100644 index 9bb2484dd46..00000000000 --- a/cache/stores/mongodb/MongoDB/Database.php +++ /dev/null @@ -1,618 +0,0 @@ - BSONArray::class, - 'document' => BSONDocument::class, - 'root' => BSONDocument::class, - ]; - - /** @var integer */ - private static $wireVersionForReadConcernWithWriteStage = 8; - - /** @var string */ - private $databaseName; - - /** @var Manager */ - private $manager; - - /** @var ReadConcern */ - private $readConcern; - - /** @var ReadPreference */ - private $readPreference; - - /** @var array */ - private $typeMap; - - /** @var WriteConcern */ - private $writeConcern; - - /** - * Constructs new Database instance. - * - * This class provides methods for database-specific operations and serves - * as a gateway for accessing collections. - * - * Supported options: - * - * * readConcern (MongoDB\Driver\ReadConcern): The default read concern to - * use for database operations and selected collections. Defaults to the - * Manager's read concern. - * - * * readPreference (MongoDB\Driver\ReadPreference): The default read - * preference to use for database operations and selected collections. - * Defaults to the Manager's read preference. - * - * * typeMap (array): Default type map for cursors and BSON documents. - * - * * writeConcern (MongoDB\Driver\WriteConcern): The default write concern - * to use for database operations and selected collections. Defaults to - * the Manager's write concern. - * - * @param Manager $manager Manager instance from the driver - * @param string $databaseName Database name - * @param array $options Database options - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function __construct(Manager $manager, string $databaseName, array $options = []) - { - if (strlen($databaseName) < 1) { - throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); - } - - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); - } - - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); - } - - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - $this->manager = $manager; - $this->databaseName = $databaseName; - $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); - $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; - $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); - } - - /** - * 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() - { - return [ - 'databaseName' => $this->databaseName, - 'manager' => $this->manager, - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - } - - /** - * Select a collection within this database. - * - * Note: collections whose names contain special characters (e.g. ".") may - * be selected with complex syntax (e.g. $database->{"system.profile"}) or - * {@link selectCollection()}. - * - * @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) - { - return $this->selectCollection($collectionName); - } - - /** - * Return the database name. - * - * @return string - */ - public function __toString() - { - return $this->databaseName; - } - - /** - * Runs an aggregation framework pipeline on the database for pipeline - * stages that do not require an underlying collection, such as $currentOp - * and $listLocalSessions. Requires MongoDB >= 3.6 - * - * @see Aggregate::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @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 = []) - { - $hasWriteStage = is_last_pipeline_operator_write($pipeline); - - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = $hasWriteStage - ? select_server_for_aggregate_write_stage($this->manager, $options) - : select_server($this->manager, $options); - - /* MongoDB 4.2 and later supports a read concern when an $out stage is - * being used, but earlier versions do not. - * - * A read concern is also not compatible with transactions. - */ - if ( - ! isset($options['readConcern']) && - ! is_in_transaction($options) && - ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage)) - ) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - if ($hasWriteStage && ! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new Aggregate($this->databaseName, null, $pipeline, $options); - - return $operation->execute($server); - } - - /** - * Execute a command on this database. - * - * @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($command, array $options = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new DatabaseCommand($this->databaseName, $command, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Create a new collection explicitly. - * - * @see CreateCollection::__construct() for supported 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 createCollection(string $collectionName, array $options = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $encryptedFields = $options['encryptedFields'] - ?? get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager) - ?? null; - - if ($encryptedFields !== null) { - // encryptedFields is passed to the create command - $options['encryptedFields'] = $encryptedFields; - - $encryptedFields = (array) $encryptedFields; - $enxcolOptions = ['clusteredIndex' => ['key' => ['_id' => 1], 'unique' => true]]; - (new CreateCollection($this->databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc', $enxcolOptions))->execute($server); - (new CreateCollection($this->databaseName, $encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc', $enxcolOptions))->execute($server); - (new CreateCollection($this->databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc', $enxcolOptions))->execute($server); - } - - $operation = new CreateCollection($this->databaseName, $collectionName, $options); - - $result = $operation->execute($server); - - if ($encryptedFields !== null) { - (new CreateIndexes($this->databaseName, $collectionName, [['key' => ['__safeContent__' => 1]]]))->execute($server); - } - - return $result; - } - - /** - * Drop this database. - * - * @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 = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new DropDatabase($this->databaseName, $options); - - return $operation->execute($server); - } - - /** - * Drop a collection within this database. - * - * @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 = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $encryptedFields = $options['encryptedFields'] - ?? get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager) - ?? get_encrypted_fields_from_server($this->databaseName, $collectionName, $this->manager, $server) - ?? null; - - if ($encryptedFields !== null) { - // encryptedFields is not passed to the drop command - unset($options['encryptedFields']); - - $encryptedFields = (array) $encryptedFields; - (new DropCollection($this->databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc'))->execute($server); - } - - $operation = new DropCollection($this->databaseName, $collectionName, $options); - - return $operation->execute($server); - } - - /** - * Returns the database name. - * - * @return string - */ - public function getDatabaseName() - { - return $this->databaseName; - } - - /** - * Return the Manager. - * - * @return Manager - */ - public function getManager() - { - return $this->manager; - } - - /** - * Return the read concern for this database. - * - * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern - */ - public function getReadConcern() - { - return $this->readConcern; - } - - /** - * Return the read preference for this database. - * - * @return ReadPreference - */ - public function getReadPreference() - { - return $this->readPreference; - } - - /** - * Return the type map for this database. - * - * @return array - */ - public function getTypeMap() - { - return $this->typeMap; - } - - /** - * Return the write concern for this database. - * - * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern - */ - public function getWriteConcern() - { - return $this->writeConcern; - } - - /** - * Returns the names of all collections in this database - * - * @see ListCollectionNames::__construct() for supported options - * @throws InvalidArgumentException for parameter/option parsing errors - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function listCollectionNames(array $options = []): Iterator - { - $operation = new ListCollectionNames($this->databaseName, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Returns information for all collections in this database. - * - * @see ListCollections::__construct() for supported options - * @return CollectionInfoIterator - * @throws InvalidArgumentException for parameter/option parsing errors - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function listCollections(array $options = []) - { - $operation = new ListCollections($this->databaseName, $options); - $server = select_server($this->manager, $options); - - return $operation->execute($server); - } - - /** - * Modifies a collection or view. - * - * @see ModifyCollection::__construct() for supported 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 = []) - { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new ModifyCollection($this->databaseName, $collectionName, $collectionOptions, $options); - - return $operation->execute($server); - } - - /** - * Rename a collection within this database. - * - * @see RenameCollection::__construct() for supported options - * @param string $fromCollectionName Collection name - * @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 = []) - { - if (! isset($toDatabaseName)) { - $toDatabaseName = $this->databaseName; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new RenameCollection($this->databaseName, $fromCollectionName, $toDatabaseName, $toCollectionName, $options); - - return $operation->execute($server); - } - - /** - * Select a collection within this database. - * - * @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 = []) - { - $options += [ - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - - return new Collection($this->manager, $this->databaseName, $collectionName, $options); - } - - /** - * Select a GridFS bucket within this database. - * - * @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 = []) - { - $options += [ - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - - return new Bucket($this->manager, $this->databaseName, $options); - } - - /** - * Create a change stream for watching changes to the database. - * - * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @param array $options Command options - * @return ChangeStream - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function watch(array $pipeline = [], array $options = []) - { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $operation = new Watch($this->manager, $this->databaseName, null, $pipeline, $options); - - return $operation->execute($server); - } - - /** - * Get a clone of this database with different 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 = []) - { - $options += [ - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - - return new Database($this->manager, $this->databaseName, $options); - } -} diff --git a/cache/stores/mongodb/MongoDB/DeleteResult.php b/cache/stores/mongodb/MongoDB/DeleteResult.php deleted file mode 100644 index 697ee869266..00000000000 --- a/cache/stores/mongodb/MongoDB/DeleteResult.php +++ /dev/null @@ -1,70 +0,0 @@ -writeResult = $writeResult; - $this->isAcknowledged = $writeResult->isAcknowledged(); - } - - /** - * Return the number of documents that were deleted. - * - * This method should only be called if the write was acknowledged. - * - * @see DeleteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getDeletedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getDeletedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return whether this delete was acknowledged by the server. - * - * If the delete was not acknowledged, other fields from the WriteResult - * (e.g. deletedCount) will be undefined. - * - * @return boolean - */ - public function isAcknowledged() - { - return $this->isAcknowledged; - } -} diff --git a/cache/stores/mongodb/MongoDB/Exception/BadMethodCallException.php b/cache/stores/mongodb/MongoDB/Exception/BadMethodCallException.php deleted file mode 100644 index b140ff3aed5..00000000000 --- a/cache/stores/mongodb/MongoDB/Exception/BadMethodCallException.php +++ /dev/null @@ -1,47 +0,0 @@ - BSONArray::class, - 'document' => BSONDocument::class, - 'root' => BSONDocument::class, - ]; - - /** @var string */ - private static $streamWrapperProtocol = 'gridfs'; - - /** @var CollectionWrapper */ - private $collectionWrapper; - - /** @var string */ - private $databaseName; - - /** @var Manager */ - private $manager; - - /** @var string */ - private $bucketName; - - /** @var boolean */ - private $disableMD5; - - /** @var integer */ - private $chunkSizeBytes; - - /** @var ReadConcern */ - private $readConcern; - - /** @var ReadPreference */ - private $readPreference; - - /** @var array */ - private $typeMap; - - /** @var WriteConcern */ - private $writeConcern; - - /** - * Constructs a GridFS bucket. - * - * Supported options: - * - * * bucketName (string): The bucket name, which will be used as a prefix - * for the files and chunks collections. Defaults to "fs". - * - * * 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. - * - * * typeMap (array): Default type map for cursors and BSON documents. - * - * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. - * - * @param Manager $manager Manager instance from the driver - * @param string $databaseName Database name - * @param array $options Bucket options - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function __construct(Manager $manager, string $databaseName, array $options = []) - { - $options += [ - 'bucketName' => self::$defaultBucketName, - 'chunkSizeBytes' => self::$defaultChunkSizeBytes, - 'disableMD5' => false, - ]; - - if (! is_string($options['bucketName'])) { - throw InvalidArgumentException::invalidType('"bucketName" option', $options['bucketName'], 'string'); - } - - if (! is_integer($options['chunkSizeBytes'])) { - throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer'); - } - - if ($options['chunkSizeBytes'] < 1) { - 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['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); - } - - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); - } - - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - $this->manager = $manager; - $this->databaseName = $databaseName; - $this->bucketName = $options['bucketName']; - $this->chunkSizeBytes = $options['chunkSizeBytes']; - $this->disableMD5 = $options['disableMD5']; - $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); - $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; - $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); - - $collectionOptions = array_intersect_key($options, ['readConcern' => 1, 'readPreference' => 1, 'typeMap' => 1, 'writeConcern' => 1]); - - $this->collectionWrapper = new CollectionWrapper($manager, $databaseName, $options['bucketName'], $collectionOptions); - $this->registerStreamWrapper(); - } - - /** - * 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() - { - return [ - 'bucketName' => $this->bucketName, - 'databaseName' => $this->databaseName, - 'manager' => $this->manager, - 'chunkSizeBytes' => $this->chunkSizeBytes, - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - } - - /** - * Delete a file from the GridFS bucket. - * - * If the files collection document is not found, this method will still - * attempt to delete orphaned chunks. - * - * @param mixed $id File ID - * @throws FileNotFoundException if no file could be selected - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function delete($id) - { - $file = $this->collectionWrapper->findFileById($id); - $this->collectionWrapper->deleteFileAndChunksById($id); - - if ($file === null) { - throw FileNotFoundException::byId($id, $this->getFilesNamespace()); - } - } - - /** - * Writes the contents of a GridFS file to a writable stream. - * - * @param mixed $id File ID - * @param resource $destination Writable Stream - * @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($id, $destination) - { - if (! is_resource($destination) || get_resource_type($destination) != "stream") { - throw InvalidArgumentException::invalidType('$destination', $destination, 'resource'); - } - - $source = $this->openDownloadStream($id); - if (@stream_copy_to_stream($source, $destination) === false) { - throw StreamException::downloadFromIdFailed($id, $source, $destination); - } - } - - /** - * Writes the contents of a GridFS file, which is selected by name and - * revision, to a writable stream. - * - * Supported options: - * - * * revision (integer): Which revision (i.e. documents with the same - * filename and different uploadDate) of the file to retrieve. Defaults - * to -1 (i.e. the most recent revision). - * - * Revision numbers are defined as follows: - * - * * 0 = the original stored file - * * 1 = the first revision - * * 2 = the second revision - * * etc… - * * -2 = the second most recent revision - * * -1 = the most recent revision - * - * @param string $filename Filename - * @param resource $destination Writable Stream - * @param array $options Download options - * @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 = []) - { - if (! is_resource($destination) || get_resource_type($destination) != "stream") { - throw InvalidArgumentException::invalidType('$destination', $destination, 'resource'); - } - - $source = $this->openDownloadStreamByName($filename, $options); - if (@stream_copy_to_stream($source, $destination) === false) { - throw StreamException::downloadFromFilenameFailed($filename, $source, $destination); - } - } - - /** - * Drops the files and chunks collections associated with this GridFS - * bucket. - * - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function drop() - { - $this->collectionWrapper->dropCollections(); - } - - /** - * Finds documents from the GridFS bucket's files collection matching the - * query. - * - * @see Find::__construct() for supported options - * @param array|object $filter Query by which to filter documents - * @param array $options Additional options - * @return Cursor - * @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($filter = [], array $options = []) - { - return $this->collectionWrapper->findFiles($filter, $options); - } - - /** - * Finds a single document from the GridFS bucket's files collection - * matching the query. - * - * @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($filter = [], array $options = []) - { - return $this->collectionWrapper->findOneFile($filter, $options); - } - - /** - * Return the bucket name. - * - * @return string - */ - public function getBucketName() - { - return $this->bucketName; - } - - /** - * Return the chunks collection. - * - * @return Collection - */ - public function getChunksCollection() - { - return $this->collectionWrapper->getChunksCollection(); - } - - /** - * Return the chunk size in bytes. - * - * @return integer - */ - public function getChunkSizeBytes() - { - return $this->chunkSizeBytes; - } - - /** - * Return the database name. - * - * @return string - */ - public function getDatabaseName() - { - return $this->databaseName; - } - - /** - * 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) - { - $file = $this->getRawFileDocumentForStream($stream); - - // Filter the raw document through the specified type map - return apply_type_map_to_document($file, $this->typeMap); - } - - /** - * 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) - { - $file = $this->getRawFileDocumentForStream($stream); - - /* Filter the raw document through the specified type map, but override - * the root type so we can reliably access the ID. - */ - $typeMap = ['root' => 'stdClass'] + $this->typeMap; - $file = apply_type_map_to_document($file, $typeMap); - assert(is_object($file)); - - if (! isset($file->_id) && ! property_exists($file, '_id')) { - throw new CorruptFileException('file._id does not exist'); - } - - return $file->_id; - } - - /** - * Return the files collection. - * - * @return Collection - */ - public function getFilesCollection() - { - return $this->collectionWrapper->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() - { - return $this->readConcern; - } - - /** - * Return the read preference for this GridFS bucket. - * - * @return ReadPreference - */ - public function getReadPreference() - { - return $this->readPreference; - } - - /** - * Return the type map for this GridFS bucket. - * - * @return array - */ - public function getTypeMap() - { - return $this->typeMap; - } - - /** - * Return the write concern for this GridFS bucket. - * - * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern - */ - public function getWriteConcern() - { - return $this->writeConcern; - } - - /** - * Opens a readable stream for reading a GridFS file. - * - * @param mixed $id File ID - * @return resource - * @throws FileNotFoundException if no file could be selected - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function openDownloadStream($id) - { - $file = $this->collectionWrapper->findFileById($id); - - if ($file === null) { - throw FileNotFoundException::byId($id, $this->getFilesNamespace()); - } - - return $this->openDownloadStreamByFile($file); - } - - /** - * Opens a readable stream stream to read a GridFS file, which is selected - * by name and revision. - * - * Supported options: - * - * * revision (integer): Which revision (i.e. documents with the same - * filename and different uploadDate) of the file to retrieve. Defaults - * to -1 (i.e. the most recent revision). - * - * Revision numbers are defined as follows: - * - * * 0 = the original stored file - * * 1 = the first revision - * * 2 = the second revision - * * etc… - * * -2 = the second most recent revision - * * -1 = the most recent revision - * - * @param string $filename Filename - * @param array $options Download options - * @return resource - * @throws FileNotFoundException if no file could be selected - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function openDownloadStreamByName(string $filename, array $options = []) - { - $options += ['revision' => -1]; - - $file = $this->collectionWrapper->findFileByFilenameAndRevision($filename, $options['revision']); - - if ($file === null) { - throw FileNotFoundException::byFilenameAndRevision($filename, $options['revision'], $this->getFilesNamespace()); - } - - return $this->openDownloadStreamByFile($file); - } - - /** - * Opens a writable stream for writing a GridFS file. - * - * Supported options: - * - * * _id (mixed): File document identifier. Defaults to a new ObjectId. - * - * * 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. - * - * @param string $filename Filename - * @param array $options Upload options - * @return resource - */ - public function openUploadStream(string $filename, array $options = []) - { - $options += ['chunkSizeBytes' => $this->chunkSizeBytes]; - - $path = $this->createPathForUpload(); - $context = stream_context_create([ - self::$streamWrapperProtocol => [ - 'collectionWrapper' => $this->collectionWrapper, - 'filename' => $filename, - 'options' => $options, - ], - ]); - - return fopen($path, 'w', false, $context); - } - - /** - * Renames the GridFS file with the specified ID. - * - * @param mixed $id File ID - * @param string $newFilename New filename - * @throws FileNotFoundException if no file could be selected - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function rename($id, string $newFilename) - { - $updateResult = $this->collectionWrapper->updateFilenameForId($id, $newFilename); - - if ($updateResult->getModifiedCount() === 1) { - 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) { - throw FileNotFoundException::byId($id, $this->getFilesNamespace()); - } - } - - /** - * Writes the contents of a readable stream to a GridFS file. - * - * Supported options: - * - * * _id (mixed): File document identifier. Defaults to a new ObjectId. - * - * * 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. - * - * @param string $filename Filename - * @param resource $source Readable stream - * @param array $options Stream options - * @return mixed ID of the newly created GridFS file - * @throws InvalidArgumentException if $source is not a GridFS stream - * @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 = []) - { - if (! is_resource($source) || get_resource_type($source) != "stream") { - throw InvalidArgumentException::invalidType('$source', $source, 'resource'); - } - - $destination = $this->openUploadStream($filename, $options); - - if (@stream_copy_to_stream($source, $destination) === false) { - $destinationUri = $this->createPathForFile($this->getRawFileDocumentForStream($destination)); - - throw StreamException::uploadFailed($filename, $source, $destinationUri); - } - - return $this->getFileIdForStream($destination); - } - - /** - * Creates a path for an existing GridFS file. - * - * @param object $file GridFS file document - */ - private function createPathForFile(object $file): string - { - if (is_array($file->_id) || (is_object($file->_id) && ! method_exists($file->_id, '__toString'))) { - $id = toJSON(fromPHP(['_id' => $file->_id])); - } else { - $id = (string) $file->_id; - } - - return sprintf( - '%s://%s/%s.files/%s', - self::$streamWrapperProtocol, - urlencode($this->databaseName), - urlencode($this->bucketName), - urlencode($id) - ); - } - - /** - * Creates a path for a new GridFS file, which does not yet have an ID. - */ - private function createPathForUpload(): string - { - return sprintf( - '%s://%s/%s.files', - self::$streamWrapperProtocol, - urlencode($this->databaseName), - urlencode($this->bucketName) - ); - } - - /** - * Returns the names of the files collection. - */ - private function getFilesNamespace(): string - { - return sprintf('%s.%s.files', $this->databaseName, $this->bucketName); - } - - /** - * Gets the file document of the GridFS file associated with a stream. - * - * This returns the raw document from the StreamWrapper, which does not - * respect the Bucket's type map. - * - * @param resource $stream GridFS stream - * @throws InvalidArgumentException - */ - private function getRawFileDocumentForStream($stream): object - { - if (! is_resource($stream) || get_resource_type($stream) != "stream") { - throw InvalidArgumentException::invalidType('$stream', $stream, 'resource'); - } - - $metadata = stream_get_meta_data($stream); - - if (! isset($metadata['wrapper_data']) || ! $metadata['wrapper_data'] instanceof StreamWrapper) { - throw InvalidArgumentException::invalidType('$stream wrapper data', $metadata['wrapper_data'] ?? null, StreamWrapper::class); - } - - return $metadata['wrapper_data']->getFile(); - } - - /** - * Opens a readable stream for the GridFS file. - * - * @param object $file GridFS file document - * @return resource - */ - private function openDownloadStreamByFile(object $file) - { - $path = $this->createPathForFile($file); - $context = stream_context_create([ - self::$streamWrapperProtocol => [ - 'collectionWrapper' => $this->collectionWrapper, - 'file' => $file, - ], - ]); - - return fopen($path, 'r', false, $context); - } - - /** - * Registers the GridFS stream wrapper if it is not already registered. - */ - private function registerStreamWrapper(): void - { - if (in_array(self::$streamWrapperProtocol, stream_get_wrappers())) { - return; - } - - StreamWrapper::register(self::$streamWrapperProtocol); - } -} diff --git a/cache/stores/mongodb/MongoDB/GridFS/CollectionWrapper.php b/cache/stores/mongodb/MongoDB/GridFS/CollectionWrapper.php deleted file mode 100644 index 28218707cf6..00000000000 --- a/cache/stores/mongodb/MongoDB/GridFS/CollectionWrapper.php +++ /dev/null @@ -1,367 +0,0 @@ -databaseName = $databaseName; - $this->bucketName = $bucketName; - - $this->filesCollection = new Collection($manager, $databaseName, sprintf('%s.files', $bucketName), $collectionOptions); - $this->chunksCollection = new Collection($manager, $databaseName, sprintf('%s.chunks', $bucketName), $collectionOptions); - } - - /** - * Deletes all GridFS chunks for a given file ID. - * - * @param mixed $id - */ - public function deleteChunksByFilesId($id): void - { - $this->chunksCollection->deleteMany(['files_id' => $id]); - } - - /** - * Deletes a GridFS file and related chunks by ID. - * - * @param mixed $id - */ - public function deleteFileAndChunksById($id): void - { - $this->filesCollection->deleteOne(['_id' => $id]); - $this->chunksCollection->deleteMany(['files_id' => $id]); - } - - /** - * Drops the GridFS files and chunks collections. - */ - public function dropCollections(): void - { - $this->filesCollection->drop(['typeMap' => []]); - $this->chunksCollection->drop(['typeMap' => []]); - } - - /** - * Finds GridFS chunk documents for a given file ID and optional offset. - * - * @param mixed $id File ID - * @param integer $fromChunk Starting chunk (inclusive) - */ - public function findChunksByFileId($id, int $fromChunk = 0): Cursor - { - return $this->chunksCollection->find( - [ - 'files_id' => $id, - 'n' => ['$gte' => $fromChunk], - ], - [ - 'sort' => ['n' => 1], - 'typeMap' => ['root' => 'stdClass'], - ] - ); - } - - /** - * Finds a GridFS file document for a given filename and revision. - * - * Revision numbers are defined as follows: - * - * * 0 = the original stored file - * * 1 = the first revision - * * 2 = the second revision - * * etc… - * * -2 = the second most recent revision - * * -1 = the most recent revision - * - * @see Bucket::downloadToStreamByName() - * @see Bucket::openDownloadStreamByName() - */ - public function findFileByFilenameAndRevision(string $filename, int $revision): ?object - { - $filename = $filename; - $revision = $revision; - - if ($revision < 0) { - $skip = abs($revision) - 1; - $sortOrder = -1; - } else { - $skip = $revision; - $sortOrder = 1; - } - - $file = $this->filesCollection->findOne( - ['filename' => $filename], - [ - 'skip' => $skip, - 'sort' => ['uploadDate' => $sortOrder], - 'typeMap' => ['root' => 'stdClass'], - ] - ); - assert(is_object($file) || $file === null); - - return $file; - } - - /** - * Finds a GridFS file document for a given ID. - * - * @param mixed $id - */ - public function findFileById($id): ?object - { - $file = $this->filesCollection->findOne( - ['_id' => $id], - ['typeMap' => ['root' => 'stdClass']] - ); - assert(is_object($file) || $file === null); - - return $file; - } - - /** - * Finds documents from the GridFS bucket's files collection. - * - * @see Find::__construct() for supported options - * @param array|object $filter Query by which to filter documents - * @param array $options Additional options - * @return Cursor - */ - public function findFiles($filter, array $options = []) - { - return $this->filesCollection->find($filter, $options); - } - - /** - * Finds a single document from the GridFS bucket's files collection. - * - * @param array|object $filter Query by which to filter documents - * @param array $options Additional options - * @return array|object|null - */ - public function findOneFile($filter, array $options = []) - { - return $this->filesCollection->findOne($filter, $options); - } - - public function getBucketName(): string - { - return $this->bucketName; - } - - public function getChunksCollection(): Collection - { - return $this->chunksCollection; - } - - public function getDatabaseName(): string - { - return $this->databaseName; - } - - public function getFilesCollection(): Collection - { - return $this->filesCollection; - } - - /** - * Inserts a document into the chunks collection. - * - * @param array|object $chunk Chunk document - */ - public function insertChunk($chunk): void - { - if (! $this->checkedIndexes) { - $this->ensureIndexes(); - } - - $this->chunksCollection->insertOne($chunk); - } - - /** - * Inserts a document into the files collection. - * - * The file document should be inserted after all chunks have been inserted. - * - * @param array|object $file File document - */ - public function insertFile($file): void - { - if (! $this->checkedIndexes) { - $this->ensureIndexes(); - } - - $this->filesCollection->insertOne($file); - } - - /** - * Updates the filename field in the file document for a given ID. - * - * @param mixed $id - */ - public function updateFilenameForId($id, string $filename): UpdateResult - { - return $this->filesCollection->updateOne( - ['_id' => $id], - ['$set' => ['filename' => $filename]] - ); - } - - /** - * Create an index on the chunks collection if it does not already exist. - */ - private function ensureChunksIndex(): void - { - $expectedIndex = ['files_id' => 1, 'n' => 1]; - - foreach ($this->chunksCollection->listIndexes() as $index) { - if ($index->isUnique() && $this->indexKeysMatch($expectedIndex, $index->getKey())) { - return; - } - } - - $this->chunksCollection->createIndex($expectedIndex, ['unique' => true]); - } - - /** - * Create an index on the files collection if it does not already exist. - */ - private function ensureFilesIndex(): void - { - $expectedIndex = ['filename' => 1, 'uploadDate' => 1]; - - foreach ($this->filesCollection->listIndexes() as $index) { - if ($this->indexKeysMatch($expectedIndex, $index->getKey())) { - return; - } - } - - $this->filesCollection->createIndex($expectedIndex); - } - - /** - * Ensure indexes on the files and chunks collections exist. - * - * This method is called once before the first write operation on a GridFS - * bucket. Indexes are only be created if the files collection is empty. - */ - private function ensureIndexes(): void - { - if ($this->checkedIndexes) { - return; - } - - $this->checkedIndexes = true; - - if (! $this->isFilesCollectionEmpty()) { - return; - } - - $this->ensureFilesIndex(); - $this->ensureChunksIndex(); - } - - private function indexKeysMatch(array $expectedKeys, array $actualKeys): bool - { - if (count($expectedKeys) !== count($actualKeys)) { - return false; - } - - $iterator = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); - $iterator->attachIterator(new ArrayIterator($expectedKeys)); - $iterator->attachIterator(new ArrayIterator($actualKeys)); - - foreach ($iterator as $key => $value) { - [$expectedKey, $actualKey] = $key; - [$expectedValue, $actualValue] = $value; - - if ($expectedKey !== $actualKey) { - return false; - } - - /* Since we don't expect special indexes (e.g. text), we mark any - * index with a non-numeric definition as unequal. All others are - * compared against their int value to avoid differences due to - * some drivers using float values in the key specification. */ - if (! is_numeric($actualValue) || (int) $expectedValue !== (int) $actualValue) { - return false; - } - } - - return true; - } - - /** - * Returns whether the files collection is empty. - */ - private function isFilesCollectionEmpty(): bool - { - return null === $this->filesCollection->findOne([], [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), - 'projection' => ['_id' => 1], - 'typeMap' => [], - ]); - } -} diff --git a/cache/stores/mongodb/MongoDB/GridFS/Exception/CorruptFileException.php b/cache/stores/mongodb/MongoDB/GridFS/Exception/CorruptFileException.php deleted file mode 100644 index e31d7daeb73..00000000000 --- a/cache/stores/mongodb/MongoDB/GridFS/Exception/CorruptFileException.php +++ /dev/null @@ -1,68 +0,0 @@ - $id])); - - return new static(sprintf('File "%s" not found in "%s"', $json, $namespace)); - } -} diff --git a/cache/stores/mongodb/MongoDB/GridFS/Exception/StreamException.php b/cache/stores/mongodb/MongoDB/GridFS/Exception/StreamException.php deleted file mode 100644 index 241f1b9a4c7..00000000000 --- a/cache/stores/mongodb/MongoDB/GridFS/Exception/StreamException.php +++ /dev/null @@ -1,47 +0,0 @@ - $id])); - $sourceMetadata = stream_get_meta_data($source); - $destinationMetadata = stream_get_meta_data($destination); - - return new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString)); - } - - /** @param resource $source */ - public static function uploadFailed(string $filename, $source, string $destinationUri): self - { - $sourceMetadata = stream_get_meta_data($source); - - return new static(sprintf('Uploading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationUri, $filename)); - } -} diff --git a/cache/stores/mongodb/MongoDB/GridFS/ReadableStream.php b/cache/stores/mongodb/MongoDB/GridFS/ReadableStream.php deleted file mode 100644 index d76e061720a..00000000000 --- a/cache/stores/mongodb/MongoDB/GridFS/ReadableStream.php +++ /dev/null @@ -1,323 +0,0 @@ -chunkSize) || ! is_integer($file->chunkSize) || $file->chunkSize < 1) { - throw new CorruptFileException('file.chunkSize is not an integer >= 1'); - } - - if (! isset($file->length) || ! is_integer($file->length) || $file->length < 0) { - throw new CorruptFileException('file.length is not an integer > 0'); - } - - if (! isset($file->_id) && ! property_exists($file, '_id')) { - throw new CorruptFileException('file._id does not exist'); - } - - $this->file = $file; - $this->chunkSize = $file->chunkSize; - $this->length = $file->length; - - $this->collectionWrapper = $collectionWrapper; - - if ($this->length > 0) { - $this->numChunks = (integer) ceil($this->length / $this->chunkSize); - $this->expectedLastChunkSize = $this->length - (($this->numChunks - 1) * $this->chunkSize); - } - } - - /** - * 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(): array - { - return [ - 'bucketName' => $this->collectionWrapper->getBucketName(), - 'databaseName' => $this->collectionWrapper->getDatabaseName(), - 'file' => $this->file, - ]; - } - - public function close(): void - { - // Nothing to do - } - - public function getFile(): object - { - return $this->file; - } - - public function getSize(): int - { - return $this->length; - } - - /** - * Return whether the current read position is at the end of the stream. - */ - public function isEOF(): bool - { - if ($this->chunkOffset === $this->numChunks - 1) { - return $this->bufferOffset >= $this->expectedLastChunkSize; - } - - return $this->chunkOffset >= $this->numChunks; - } - - /** - * Read bytes from the stream. - * - * Note: this method may return a string smaller than the requested length - * if data is not available to be read. - * - * @param integer $length Number of bytes to read - * @throws InvalidArgumentException if $length is negative - */ - public function readBytes(int $length): string - { - if ($length < 0) { - throw new InvalidArgumentException(sprintf('$length must be >= 0; given: %d', $length)); - } - - if ($this->chunksIterator === null) { - $this->initChunksIterator(); - } - - if ($this->buffer === null && ! $this->initBufferFromCurrentChunk()) { - return ''; - } - - assert($this->buffer !== null); - - $data = ''; - - while (strlen($data) < $length) { - if ($this->bufferOffset >= strlen($this->buffer) && ! $this->initBufferFromNextChunk()) { - break; - } - - $initialDataLength = strlen($data); - $data .= substr($this->buffer, $this->bufferOffset, $length - $initialDataLength); - $this->bufferOffset += strlen($data) - $initialDataLength; - } - - return $data; - } - - /** - * Seeks the chunk and buffer offsets for the next read operation. - * - * @throws InvalidArgumentException if $offset is out of range - */ - public function seek(int $offset): void - { - if ($offset < 0 || $offset > $this->file->length) { - throw new InvalidArgumentException(sprintf('$offset must be >= 0 and <= %d; given: %d', $this->file->length, $offset)); - } - - /* Compute the offsets for the chunk and buffer (i.e. chunk data) from - * which we will expect to read after seeking. If the chunk offset - * changed, we'll also need to reset the buffer. - */ - $lastChunkOffset = $this->chunkOffset; - $this->chunkOffset = (integer) floor($offset / $this->chunkSize); - $this->bufferOffset = $offset % $this->chunkSize; - - if ($lastChunkOffset === $this->chunkOffset) { - return; - } - - if ($this->chunksIterator === null) { - return; - } - - // Clear the buffer since the current chunk will be changed - $this->buffer = null; - - /* If we are seeking to a previous chunk, we need to reinitialize the - * chunk iterator. - */ - if ($lastChunkOffset > $this->chunkOffset) { - $this->chunksIterator = null; - - return; - } - - /* If we are seeking to a subsequent chunk, we do not need to - * reinitalize the chunk iterator. Instead, we can simply move forward - * to $this->chunkOffset. - */ - $numChunks = $this->chunkOffset - $lastChunkOffset; - for ($i = 0; $i < $numChunks; $i++) { - $this->chunksIterator->next(); - } - } - - /** - * Return the current position of the stream. - * - * This is the offset within the stream where the next byte would be read. - */ - public function tell(): int - { - return ($this->chunkOffset * $this->chunkSize) + $this->bufferOffset; - } - - /** - * Initialize the buffer to the current chunk's data. - * - * @return boolean Whether there was a current chunk to read - * @throws CorruptFileException if an expected chunk could not be read successfully - */ - private function initBufferFromCurrentChunk(): bool - { - if ($this->chunkOffset === 0 && $this->numChunks === 0) { - return false; - } - - if ($this->chunksIterator === null) { - return false; - } - - if (! $this->chunksIterator->valid()) { - throw CorruptFileException::missingChunk($this->chunkOffset); - } - - $currentChunk = $this->chunksIterator->current(); - assert(is_object($currentChunk)); - - if ($currentChunk->n !== $this->chunkOffset) { - throw CorruptFileException::unexpectedIndex($currentChunk->n, $this->chunkOffset); - } - - if (! $currentChunk->data instanceof Binary) { - throw CorruptFileException::invalidChunkData($this->chunkOffset); - } - - $this->buffer = $currentChunk->data->getData(); - - $actualChunkSize = strlen($this->buffer); - - $expectedChunkSize = $this->chunkOffset === $this->numChunks - 1 - ? $this->expectedLastChunkSize - : $this->chunkSize; - - if ($actualChunkSize !== $expectedChunkSize) { - throw CorruptFileException::unexpectedSize($actualChunkSize, $expectedChunkSize); - } - - return true; - } - - /** - * Advance to the next chunk and initialize the buffer to its data. - * - * @return boolean Whether there was a next chunk to read - * @throws CorruptFileException if an expected chunk could not be read successfully - */ - private function initBufferFromNextChunk(): bool - { - if ($this->chunkOffset === $this->numChunks - 1) { - return false; - } - - if ($this->chunksIterator === null) { - return false; - } - - $this->bufferOffset = 0; - $this->chunkOffset++; - $this->chunksIterator->next(); - - return $this->initBufferFromCurrentChunk(); - } - - /** - * Initializes the chunk iterator starting from the current offset. - */ - private function initChunksIterator(): void - { - $this->chunksIterator = $this->collectionWrapper->findChunksByFileId($this->file->_id, $this->chunkOffset); - $this->chunksIterator->rewind(); - } -} diff --git a/cache/stores/mongodb/MongoDB/GridFS/StreamWrapper.php b/cache/stores/mongodb/MongoDB/GridFS/StreamWrapper.php deleted file mode 100644 index 3e04c3d14c0..00000000000 --- a/cache/stores/mongodb/MongoDB/GridFS/StreamWrapper.php +++ /dev/null @@ -1,330 +0,0 @@ -stream = null; - } - - /** - * Return the stream's file document. - */ - public function getFile(): object - { - assert($this->stream !== null); - - return $this->stream->getFile(); - } - - /** - * Register the GridFS stream wrapper. - * - * @param string $protocol Protocol to use for stream_wrapper_register() - */ - public static function register(string $protocol = 'gridfs'): void - { - if (in_array($protocol, stream_get_wrappers())) { - stream_wrapper_unregister($protocol); - } - - stream_wrapper_register($protocol, static::class, STREAM_IS_URL); - } - - /** - * Closes the stream. - * - * @see https://php.net/manual/en/streamwrapper.stream-close.php - */ - public function stream_close(): void - { - if (! $this->stream) { - return; - } - - $this->stream->close(); - } - - /** - * Returns whether the file pointer is at the end of the stream. - * - * @see https://php.net/manual/en/streamwrapper.stream-eof.php - */ - public function stream_eof(): bool - { - if (! $this->stream instanceof ReadableStream) { - return false; - } - - return $this->stream->isEOF(); - } - - /** - * Opens the stream. - * - * @see https://php.net/manual/en/streamwrapper.stream-open.php - * @param string $path Path to the file resource - * @param string $mode Mode used to open the file (only "r" and "w" are supported) - * @param integer $options Additional flags set by the streams API - * @param string|null $openedPath Not used - */ - public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool - { - $this->initProtocol($path); - $this->mode = $mode; - - if ($mode === 'r') { - return $this->initReadableStream(); - } - - if ($mode === 'w') { - return $this->initWritableStream(); - } - - return false; - } - - /** - * Read bytes from the stream. - * - * Note: this method may return a string smaller than the requested length - * if data is not available to be read. - * - * @see https://php.net/manual/en/streamwrapper.stream-read.php - * @param integer $length Number of bytes to read - */ - public function stream_read(int $length): string - { - if (! $this->stream instanceof ReadableStream) { - return ''; - } - - return $this->stream->readBytes($length); - } - - /** - * Return the current position of the stream. - * - * @see https://php.net/manual/en/streamwrapper.stream-seek.php - * @param integer $offset Stream offset to seek to - * @param integer $whence One of SEEK_SET, SEEK_CUR, or SEEK_END - * @return boolean True if the position was updated and false otherwise - */ - public function stream_seek(int $offset, int $whence = SEEK_SET): bool - { - assert($this->stream !== null); - - $size = $this->stream->getSize(); - - if ($whence === SEEK_CUR) { - $offset += $this->stream->tell(); - } - - if ($whence === SEEK_END) { - $offset += $size; - } - - // WritableStreams are always positioned at the end of the stream - if ($this->stream instanceof WritableStream) { - return $offset === $size; - } - - if ($offset < 0 || $offset > $size) { - return false; - } - - $this->stream->seek($offset); - - return true; - } - - /** - * Return information about the stream. - * - * @see https://php.net/manual/en/streamwrapper.stream-stat.php - */ - public function stream_stat(): array - { - assert($this->stream !== null); - - $stat = $this->getStatTemplate(); - - $stat[2] = $stat['mode'] = $this->stream instanceof ReadableStream - ? 0100444 // S_IFREG & S_IRUSR & S_IRGRP & S_IROTH - : 0100222; // S_IFREG & S_IWUSR & S_IWGRP & S_IWOTH - $stat[7] = $stat['size'] = $this->stream->getSize(); - - $file = $this->stream->getFile(); - - if (isset($file->uploadDate) && $file->uploadDate instanceof UTCDateTime) { - $timestamp = $file->uploadDate->toDateTime()->getTimestamp(); - $stat[9] = $stat['mtime'] = $timestamp; - $stat[10] = $stat['ctime'] = $timestamp; - } - - if (isset($file->chunkSize) && is_integer($file->chunkSize)) { - $stat[11] = $stat['blksize'] = $file->chunkSize; - } - - return $stat; - } - - /** - * Return the current position of the stream. - * - * @see https://php.net/manual/en/streamwrapper.stream-tell.php - * @return integer The current position of the stream - */ - public function stream_tell(): int - { - assert($this->stream !== null); - - return $this->stream->tell(); - } - - /** - * Write bytes to the stream. - * - * @see https://php.net/manual/en/streamwrapper.stream-write.php - * @param string $data Data to write - * @return integer The number of bytes written - */ - public function stream_write(string $data): int - { - if (! $this->stream instanceof WritableStream) { - return 0; - } - - return $this->stream->writeBytes($data); - } - - /** - * Returns a stat template with default values. - */ - private function getStatTemplate(): array - { - return [ - // phpcs:disable Squiz.Arrays.ArrayDeclaration.IndexNoNewline - 0 => 0, 'dev' => 0, - 1 => 0, 'ino' => 0, - 2 => 0, 'mode' => 0, - 3 => 0, 'nlink' => 0, - 4 => 0, 'uid' => 0, - 5 => 0, 'gid' => 0, - 6 => -1, 'rdev' => -1, - 7 => 0, 'size' => 0, - 8 => 0, 'atime' => 0, - 9 => 0, 'mtime' => 0, - 10 => 0, 'ctime' => 0, - 11 => -1, 'blksize' => -1, - 12 => -1, 'blocks' => -1, - // phpcs:enable - ]; - } - - /** - * Initialize the protocol from the given path. - * - * @see StreamWrapper::stream_open() - */ - private function initProtocol(string $path): void - { - $parts = explode('://', $path, 2); - $this->protocol = $parts[0] ?: 'gridfs'; - } - - /** - * Initialize the internal stream for reading. - * - * @see StreamWrapper::stream_open() - */ - private function initReadableStream(): bool - { - assert(is_resource($this->context)); - $context = stream_context_get_options($this->context); - - assert($this->protocol !== null); - $this->stream = new ReadableStream( - $context[$this->protocol]['collectionWrapper'], - $context[$this->protocol]['file'] - ); - - return true; - } - - /** - * Initialize the internal stream for writing. - * - * @see StreamWrapper::stream_open() - */ - private function initWritableStream(): bool - { - assert(is_resource($this->context)); - $context = stream_context_get_options($this->context); - - assert($this->protocol !== null); - $this->stream = new WritableStream( - $context[$this->protocol]['collectionWrapper'], - $context[$this->protocol]['filename'], - $context[$this->protocol]['options'] - ); - - return true; - } -} diff --git a/cache/stores/mongodb/MongoDB/GridFS/WritableStream.php b/cache/stores/mongodb/MongoDB/GridFS/WritableStream.php deleted file mode 100644 index 43519e09bf5..00000000000 --- a/cache/stores/mongodb/MongoDB/GridFS/WritableStream.php +++ /dev/null @@ -1,311 +0,0 @@ - new ObjectId(), - 'chunkSizeBytes' => self::$defaultChunkSizeBytes, - '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'); - } - - if ($options['chunkSizeBytes'] < 1) { - 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_array($options['metadata']) && ! is_object($options['metadata'])) { - throw InvalidArgumentException::invalidType('"metadata" option', $options['metadata'], 'array or object'); - } - - $this->chunkSize = $options['chunkSizeBytes']; - $this->collectionWrapper = $collectionWrapper; - $this->disableMD5 = $options['disableMD5']; - - if (! $this->disableMD5) { - $this->hashCtx = hash_init('md5'); - } - - $this->file = [ - '_id' => $options['_id'], - 'chunkSize' => $this->chunkSize, - 'filename' => $filename, - ] + array_intersect_key($options, ['aliases' => 1, 'contentType' => 1, 'metadata' => 1]); - } - - /** - * Return internal properties for debugging purposes. - * - * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - */ - public function __debugInfo(): array - { - return [ - 'bucketName' => $this->collectionWrapper->getBucketName(), - 'databaseName' => $this->collectionWrapper->getDatabaseName(), - 'file' => $this->file, - ]; - } - - /** - * Closes an active stream and flushes all buffered data to GridFS. - */ - public function close(): void - { - if ($this->isClosed) { - // TODO: Should this be an error condition? e.g. BadMethodCallException - return; - } - - if (strlen($this->buffer) > 0) { - $this->insertChunkFromBuffer(); - } - - $this->fileCollectionInsert(); - $this->isClosed = true; - } - - /** - * Return the stream's file document. - */ - public function getFile(): object - { - return (object) $this->file; - } - - /** - * Return the stream's size in bytes. - * - * Note: this value will increase as more data is written to the stream. - */ - public function getSize(): int - { - return $this->length + strlen($this->buffer); - } - - /** - * Return the current position of the stream. - * - * This is the offset within the stream where the next byte would be - * written. Since seeking is not supported and writes are appended, this is - * always the end of the stream. - * - * @see WritableStream::getSize() - */ - public function tell(): int - { - return $this->getSize(); - } - - /** - * Inserts binary data into GridFS via chunks. - * - * Data will be buffered internally until chunkSizeBytes are accumulated, at - * which point a chunk document will be inserted and the buffer reset. - * - * @param string $data Binary data to write - */ - public function writeBytes(string $data): int - { - if ($this->isClosed) { - // TODO: Should this be an error condition? e.g. BadMethodCallException - return 0; - } - - $bytesRead = 0; - - while ($bytesRead != strlen($data)) { - $initialBufferLength = strlen($this->buffer); - $this->buffer .= substr($data, $bytesRead, $this->chunkSize - $initialBufferLength); - $bytesRead += strlen($this->buffer) - $initialBufferLength; - - if (strlen($this->buffer) == $this->chunkSize) { - $this->insertChunkFromBuffer(); - } - } - - return $bytesRead; - } - - private function abort(): void - { - try { - $this->collectionWrapper->deleteChunksByFilesId($this->file['_id']); - } catch (DriverRuntimeException $e) { - // We are already handling an error if abort() is called, so suppress this - } - - $this->isClosed = true; - } - - /** - * @return mixed - */ - private function fileCollectionInsert() - { - $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) { - $this->abort(); - - throw $e; - } - - return $this->file['_id']; - } - - private function insertChunkFromBuffer(): void - { - if (strlen($this->buffer) == 0) { - return; - } - - $data = $this->buffer; - $this->buffer = ''; - - $chunk = [ - 'files_id' => $this->file['_id'], - 'n' => $this->chunkOffset, - 'data' => new Binary($data, Binary::TYPE_GENERIC), - ]; - - if (! $this->disableMD5 && $this->hashCtx) { - hash_update($this->hashCtx, $data); - } - - try { - $this->collectionWrapper->insertChunk($chunk); - } catch (DriverRuntimeException $e) { - $this->abort(); - - throw $e; - } - - $this->length += strlen($data); - $this->chunkOffset++; - } -} diff --git a/cache/stores/mongodb/MongoDB/InsertManyResult.php b/cache/stores/mongodb/MongoDB/InsertManyResult.php deleted file mode 100644 index d1aabe55893..00000000000 --- a/cache/stores/mongodb/MongoDB/InsertManyResult.php +++ /dev/null @@ -1,90 +0,0 @@ -writeResult = $writeResult; - $this->insertedIds = $insertedIds; - $this->isAcknowledged = $writeResult->isAcknowledged(); - } - - /** - * Return the number of documents that were inserted. - * - * This method should only be called if the write was acknowledged. - * - * @see InsertManyResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getInsertedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return a map of the inserted documents' IDs. - * - * The index of each ID in the map corresponds to each document's position - * in the bulk operation. If a document had an ID prior to inserting (i.e. - * 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() - { - return $this->insertedIds; - } - - /** - * Return whether this insert result was acknowledged by the server. - * - * If the insert was not acknowledged, other fields from the WriteResult - * (e.g. insertedCount) will be undefined. - * - * @return boolean - */ - public function isAcknowledged() - { - return $this->writeResult->isAcknowledged(); - } -} diff --git a/cache/stores/mongodb/MongoDB/InsertOneResult.php b/cache/stores/mongodb/MongoDB/InsertOneResult.php deleted file mode 100644 index 6a74eb90054..00000000000 --- a/cache/stores/mongodb/MongoDB/InsertOneResult.php +++ /dev/null @@ -1,95 +0,0 @@ -writeResult = $writeResult; - $this->insertedId = $insertedId; - $this->isAcknowledged = $writeResult->isAcknowledged(); - } - - /** - * Return the number of documents that were inserted. - * - * This method should only be called if the write was acknowledged. - * - * @see InsertOneResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getInsertedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return the inserted document's ID. - * - * 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() - { - return $this->insertedId; - } - - /** - * Return whether this insert was acknowledged by the server. - * - * If the insert was not acknowledged, other fields from the WriteResult - * (e.g. insertedCount) will be undefined. - * - * 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() - { - return $this->writeResult->isAcknowledged(); - } -} diff --git a/cache/stores/mongodb/MongoDB/MapReduceResult.php b/cache/stores/mongodb/MongoDB/MapReduceResult.php deleted file mode 100644 index d7ef777a356..00000000000 --- a/cache/stores/mongodb/MongoDB/MapReduceResult.php +++ /dev/null @@ -1,109 +0,0 @@ -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 : []; - } - - /** - * 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; - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/BSONArray.php b/cache/stores/mongodb/MongoDB/Model/BSONArray.php deleted file mode 100644 index a87505f29c1..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/BSONArray.php +++ /dev/null @@ -1,105 +0,0 @@ - $value) { - $this[$key] = recursive_copy($value); - } - } - - /** - * Factory method for var_export(). - * - * @see https://php.net/oop5.magic#object.set-state - * @see https://php.net/var-export - * @return self - */ - public static function __set_state(array $properties) - { - $array = new static(); - $array->exchangeArray($properties); - - return $array; - } - - /** - * Serialize the array to BSON. - * - * The array data will be numerically reindexed to ensure that it is stored - * as a BSON array. - * - * @see https://php.net/mongodb-bson-serializable.bsonserialize - * @return array - */ - #[ReturnTypeWillChange] - public function bsonSerialize() - { - return array_values($this->getArrayCopy()); - } - - /** - * Unserialize the document to BSON. - * - * @see https://php.net/mongodb-bson-unserializable.bsonunserialize - * @param array $data Array data - */ - #[ReturnTypeWillChange] - public function bsonUnserialize(array $data) - { - self::__construct($data); - } - - /** - * Serialize the array to JSON. - * - * The array data will be numerically reindexed to ensure that it is stored - * as a JSON array. - * - * @see https://php.net/jsonserializable.jsonserialize - * @return array - */ - #[ReturnTypeWillChange] - public function jsonSerialize() - { - return array_values($this->getArrayCopy()); - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/BSONDocument.php b/cache/stores/mongodb/MongoDB/Model/BSONDocument.php deleted file mode 100644 index 8fc04e84f55..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/BSONDocument.php +++ /dev/null @@ -1,109 +0,0 @@ - $value) { - $this[$key] = recursive_copy($value); - } - } - - /** - * This overrides the parent constructor to allow property access of entries - * by default. - * - * @see https://php.net/arrayobject.construct - */ - public function __construct(array $input = [], int $flags = ArrayObject::ARRAY_AS_PROPS, string $iteratorClass = 'ArrayIterator') - { - parent::__construct($input, $flags, $iteratorClass); - } - - /** - * Factory method for var_export(). - * - * @see https://php.net/oop5.magic#object.set-state - * @see https://php.net/var-export - * @return self - */ - public static function __set_state(array $properties) - { - $document = new static(); - $document->exchangeArray($properties); - - return $document; - } - - /** - * Serialize the document to BSON. - * - * @see https://php.net/mongodb-bson-serializable.bsonserialize - * @return object - */ - #[ReturnTypeWillChange] - public function bsonSerialize() - { - return (object) $this->getArrayCopy(); - } - - /** - * Unserialize the document to BSON. - * - * @see https://php.net/mongodb-bson-unserializable.bsonunserialize - * @param array $data Array data - */ - #[ReturnTypeWillChange] - public function bsonUnserialize(array $data) - { - parent::__construct($data, ArrayObject::ARRAY_AS_PROPS); - } - - /** - * Serialize the array to JSON. - * - * @see https://php.net/jsonserializable.jsonserialize - * @return object - */ - #[ReturnTypeWillChange] - public function jsonSerialize() - { - return (object) $this->getArrayCopy(); - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/BSONIterator.php b/cache/stores/mongodb/MongoDB/Model/BSONIterator.php deleted file mode 100644 index 2bddc264cd4..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/BSONIterator.php +++ /dev/null @@ -1,159 +0,0 @@ -buffer = $data; - $this->bufferLength = strlen($data); - $this->options = $options; - } - - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() - { - return $this->current; - } - - /** - * @see https://php.net/iterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() - { - return $this->key; - } - - /** - * @see https://php.net/iterator.next - * @return void - */ - #[ReturnTypeWillChange] - public function next() - { - $this->key++; - $this->current = null; - $this->advance(); - } - - /** - * @see https://php.net/iterator.rewind - * @return void - */ - #[ReturnTypeWillChange] - public function rewind() - { - $this->key = 0; - $this->position = 0; - $this->current = null; - $this->advance(); - } - - /** - * @see https://php.net/iterator.valid - */ - #[ReturnTypeWillChange] - public function valid(): bool - { - return $this->current !== null; - } - - private function advance(): void - { - if ($this->position === $this->bufferLength) { - return; - } - - if ($this->bufferLength - $this->position < self::$bsonSize) { - throw new UnexpectedValueException(sprintf('Expected at least %d bytes; %d remaining', self::$bsonSize, $this->bufferLength - $this->position)); - } - - [, $documentLength] = unpack('V', substr($this->buffer, $this->position, self::$bsonSize)); - - if ($this->bufferLength - $this->position < $documentLength) { - throw new UnexpectedValueException(sprintf('Expected %d bytes; %d remaining', $documentLength, $this->bufferLength - $this->position)); - } - - $this->current = toPHP(substr($this->buffer, $this->position, $documentLength), $this->options['typeMap']); - $this->position += $documentLength; - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/CachingIterator.php b/cache/stores/mongodb/MongoDB/Model/CachingIterator.php deleted file mode 100644 index f2a7dd25b98..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/CachingIterator.php +++ /dev/null @@ -1,172 +0,0 @@ -iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable); - - $this->iterator->rewind(); - $this->storeCurrentItem(); - } - - /** - * @see https://php.net/countable.count - */ - public function count(): int - { - $this->exhaustIterator(); - - return count($this->items); - } - - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() - { - $currentItem = current($this->items); - - return $currentItem !== false ? $currentItem[self::FIELD_VALUE] : false; - } - - /** - * @see https://php.net/iterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() - { - $currentItem = current($this->items); - - return $currentItem !== false ? $currentItem[self::FIELD_KEY] : null; - } - - /** - * @see https://php.net/iterator.next - */ - public function next(): void - { - if (! $this->iteratorExhausted) { - $this->iteratorAdvanced = true; - $this->iterator->next(); - - $this->storeCurrentItem(); - - $this->iteratorExhausted = ! $this->iterator->valid(); - } - - next($this->items); - } - - /** - * @see https://php.net/iterator.rewind - */ - public function rewind(): void - { - /* If the iterator has advanced, exhaust it now so that future iteration - * can rely on the cache. - */ - if ($this->iteratorAdvanced) { - $this->exhaustIterator(); - } - - reset($this->items); - } - - /** - * @see https://php.net/iterator.valid - */ - public function valid(): bool - { - return $this->key() !== null; - } - - /** - * Ensures that the inner iterator is fully consumed and cached. - */ - private function exhaustIterator(): void - { - while (! $this->iteratorExhausted) { - $this->next(); - } - } - - /** - * Stores the current item in the cache. - */ - private function storeCurrentItem(): void - { - if (! $this->iterator->valid()) { - return; - } - - // Storing a new item in the internal cache - $this->items[] = [ - self::FIELD_KEY => $this->iterator->key(), - self::FIELD_VALUE => $this->iterator->current(), - ]; - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/CallbackIterator.php b/cache/stores/mongodb/MongoDB/Model/CallbackIterator.php deleted file mode 100644 index 8e3c68d0c17..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/CallbackIterator.php +++ /dev/null @@ -1,88 +0,0 @@ -iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable); - $this->callback = $callback; - } - - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() - { - return ($this->callback)($this->iterator->current()); - } - - /** - * @see https://php.net/iterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() - { - return $this->iterator->key(); - } - - /** - * @see https://php.net/iterator.next - */ - public function next(): void - { - $this->iterator->next(); - } - - /** - * @see https://php.net/iterator.rewind - */ - public function rewind(): void - { - $this->iterator->rewind(); - } - - /** - * @see https://php.net/iterator.valid - */ - public function valid(): bool - { - return $this->iterator->valid(); - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/ChangeStreamIterator.php b/cache/stores/mongodb/MongoDB/Model/ChangeStreamIterator.php deleted file mode 100644 index ceb31f9fc96..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/ChangeStreamIterator.php +++ /dev/null @@ -1,308 +0,0 @@ -batchSize = $firstBatchSize; - $this->isRewindNop = ($firstBatchSize === 0); - $this->postBatchResumeToken = $postBatchResumeToken; - $this->resumeToken = $initialResumeToken; - $this->server = $cursor->getServer(); - } - - /** @internal */ - final public function commandFailed(CommandFailedEvent $event): void - { - } - - /** @internal */ - final public function commandStarted(CommandStartedEvent $event): void - { - if ($event->getCommandName() !== 'getMore') { - return; - } - - $this->batchPosition = 0; - $this->batchSize = 0; - $this->postBatchResumeToken = null; - } - - /** @internal */ - final public function commandSucceeded(CommandSucceededEvent $event): void - { - if ($event->getCommandName() !== 'getMore') { - return; - } - - $reply = $event->getReply(); - - if (! isset($reply->cursor->nextBatch) || ! is_array($reply->cursor->nextBatch)) { - throw new UnexpectedValueException('getMore command did not return a "cursor.nextBatch" array'); - } - - $this->batchSize = count($reply->cursor->nextBatch); - - if (isset($reply->cursor->postBatchResumeToken) && is_object($reply->cursor->postBatchResumeToken)) { - $this->postBatchResumeToken = $reply->cursor->postBatchResumeToken; - } - } - - /** - * @see https://php.net/iteratoriterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() - { - return $this->isValid ? 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. - */ - final public function getInnerIterator(): Cursor - { - $cursor = parent::getInnerIterator(); - assert($cursor instanceof Cursor); - - return $cursor; - } - - /** - * Returns the resume token for the iterator's current position. - * - * 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() - { - return $this->resumeToken; - } - - /** - * Returns the server the cursor is running on. - */ - public function getServer(): Server - { - return $this->server; - } - - /** - * @see https://php.net/iteratoriterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() - { - return $this->isValid ? parent::key() : null; - } - - /** - * @see https://php.net/iteratoriterator.rewind - */ - public function next(): void - { - /* Determine if advancing the iterator will execute a getMore command - * (i.e. we are already positioned at the end of the current batch). If - * so, rely on the APM callbacks to reset $batchPosition and update - * $batchSize. Otherwise, we can forgo APM and manually increment - * $batchPosition after calling next(). */ - $getMore = $this->isAtEndOfBatch(); - - if ($getMore) { - addSubscriber($this); - } - - try { - parent::next(); - $this->onIteration(! $getMore); - } finally { - if ($getMore) { - removeSubscriber($this); - } - } - } - - /** - * @see https://php.net/iteratoriterator.rewind - */ - public function rewind(): void - { - if ($this->isRewindNop) { - return; - } - - parent::rewind(); - $this->onIteration(false); - } - - /** - * @see https://php.net/iteratoriterator.valid - */ - public function valid(): bool - { - return $this->isValid; - } - - /** - * 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($document) - { - if (! is_array($document) && ! is_object($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); - } - - if ($document instanceof Serializable) { - return $this->extractResumeToken($document->bsonSerialize()); - } - - $resumeToken = is_array($document) - ? ($document['_id'] ?? null) - : ($document->_id ?? null); - - if (! isset($resumeToken)) { - $this->isValid = false; - - throw ResumeTokenException::notFound(); - } - - if (! is_array($resumeToken) && ! is_object($resumeToken)) { - $this->isValid = false; - - throw ResumeTokenException::invalidType($resumeToken); - } - - return $resumeToken; - } - - /** - * Return whether the iterator is positioned at the end of the batch. - */ - private function isAtEndOfBatch(): bool - { - return $this->batchPosition + 1 >= $this->batchSize; - } - - /** - * Perform housekeeping after an iteration event. - * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#updating-the-cached-resume-token - */ - private function onIteration(bool $incrementBatchPosition): void - { - $this->isValid = parent::valid(); - - /* Disable rewind()'s NOP behavior once we advance to a valid position. - * This will allow the driver to throw a LogicException if rewind() is - * called after the cursor has advanced past its first element. */ - if ($this->isRewindNop && $this->isValid) { - $this->isRewindNop = false; - } - - if ($incrementBatchPosition && $this->isValid) { - $this->batchPosition++; - } - - /* If the iterator is positioned at the end of the batch, apply the - * postBatchResumeToken if it's available. This handles both the case - * where the current batch is empty (since onIteration() will be called - * after a successful getMore) and when the iterator has advanced to the - * last document in its current batch. Otherwise, extract a resume token - * from the current document if possible. */ - if ($this->isAtEndOfBatch() && $this->postBatchResumeToken !== null) { - $this->resumeToken = $this->postBatchResumeToken; - } elseif ($this->isValid) { - $this->resumeToken = $this->extractResumeToken($this->current()); - } - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/CollectionInfo.php b/cache/stores/mongodb/MongoDB/Model/CollectionInfo.php deleted file mode 100644 index 6722978bc65..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/CollectionInfo.php +++ /dev/null @@ -1,203 +0,0 @@ -info = $info; - } - - /** - * Return the collection info as an array. - * - * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array - */ - public function __debugInfo() - { - return $this->info; - } - - /** - * 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() - { - /* The MongoDB server might return this number as an integer or float */ - return isset($this->info['options']['max']) ? (integer) $this->info['options']['max'] : null; - } - - /** - * 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() - { - /* The MongoDB server might return this number as an integer or float */ - return isset($this->info['options']['size']) ? (integer) $this->info['options']['size'] : null; - } - - /** - * Return information about the _id index for the collection. - */ - public function getIdIndex(): array - { - return (array) ($this->info['idIndex'] ?? []); - } - - /** - * Return the "info" property of the server response. - * - * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - */ - public function getInfo(): array - { - return (array) ($this->info['info'] ?? []); - } - - /** - * Return the collection name. - * - * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - * @return string - */ - public function getName() - { - return (string) $this->info['name']; - } - - /** - * Return the collection options. - * - * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - * @return array - */ - public function getOptions() - { - return (array) ($this->info['options'] ?? []); - } - - /** - * Return the collection type. - * - * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - */ - public function getType(): string - { - return (string) $this->info['type']; - } - - /** - * Return whether the collection is a capped collection. - * - * @deprecated 1.0 Deprecated in favor of using getOptions - * - * @return boolean - */ - public function isCapped() - { - return ! empty($this->info['options']['capped']); - } - - /** - * Check whether a field exists in the collection information. - * - * @see https://php.net/arrayaccess.offsetexists - * @param mixed $key - * @return boolean - */ - #[ReturnTypeWillChange] - public function offsetExists($key) - { - return array_key_exists($key, $this->info); - } - - /** - * Return the field's value from the collection information. - * - * @see https://php.net/arrayaccess.offsetget - * @param mixed $key - * @return mixed - */ - #[ReturnTypeWillChange] - public function offsetGet($key) - { - return $this->info[$key]; - } - - /** - * Not supported. - * - * @see https://php.net/arrayaccess.offsetset - * @param mixed $key - * @param mixed $value - * @throws BadMethodCallException - * @return void - */ - #[ReturnTypeWillChange] - public function offsetSet($key, $value) - { - throw BadMethodCallException::classIsImmutable(self::class); - } - - /** - * Not supported. - * - * @see https://php.net/arrayaccess.offsetunset - * @param mixed $key - * @throws BadMethodCallException - * @return void - */ - #[ReturnTypeWillChange] - public function offsetUnset($key) - { - throw BadMethodCallException::classIsImmutable(self::class); - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/CollectionInfoCommandIterator.php b/cache/stores/mongodb/MongoDB/Model/CollectionInfoCommandIterator.php deleted file mode 100644 index 96c2d73a97f..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/CollectionInfoCommandIterator.php +++ /dev/null @@ -1,62 +0,0 @@ -databaseName = $databaseName; - } - - /** - * 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/cache/stores/mongodb/MongoDB/Model/CollectionInfoIterator.php b/cache/stores/mongodb/MongoDB/Model/CollectionInfoIterator.php deleted file mode 100644 index aa6cb3afb35..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/CollectionInfoIterator.php +++ /dev/null @@ -1,40 +0,0 @@ -info = $info; - } - - /** - * Return the database info as an array. - * - * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array - */ - public function __debugInfo() - { - return $this->info; - } - - /** - * Return the database name. - * - * @return string - */ - public function getName() - { - return (string) $this->info['name']; - } - - /** - * Return the databases size on disk (in bytes). - * - * @return integer - */ - public function getSizeOnDisk() - { - /* The MongoDB server might return this number as an integer or float */ - return (integer) $this->info['sizeOnDisk']; - } - - /** - * Return whether the database is empty. - * - * @return boolean - */ - public function isEmpty() - { - return (boolean) $this->info['empty']; - } - - /** - * Check whether a field exists in the database information. - * - * @see https://php.net/arrayaccess.offsetexists - * @param mixed $key - * @return boolean - */ - #[ReturnTypeWillChange] - public function offsetExists($key) - { - return array_key_exists($key, $this->info); - } - - /** - * Return the field's value from the database information. - * - * @see https://php.net/arrayaccess.offsetget - * @param mixed $key - * @return mixed - */ - #[ReturnTypeWillChange] - public function offsetGet($key) - { - return $this->info[$key]; - } - - /** - * Not supported. - * - * @see https://php.net/arrayaccess.offsetset - * @param mixed $key - * @param mixed $value - * @throws BadMethodCallException - * @return void - */ - #[ReturnTypeWillChange] - public function offsetSet($key, $value) - { - throw BadMethodCallException::classIsImmutable(self::class); - } - - /** - * Not supported. - * - * @see https://php.net/arrayaccess.offsetunset - * @param mixed $key - * @throws BadMethodCallException - * @return void - */ - #[ReturnTypeWillChange] - public function offsetUnset($key) - { - throw BadMethodCallException::classIsImmutable(self::class); - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/DatabaseInfoIterator.php b/cache/stores/mongodb/MongoDB/Model/DatabaseInfoIterator.php deleted file mode 100644 index cb613575051..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/DatabaseInfoIterator.php +++ /dev/null @@ -1,40 +0,0 @@ -databases = $databases; - } - - /** - * Return the current element as a DatabaseInfo instance. - * - * @see DatabaseInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): DatabaseInfo - { - return new DatabaseInfo(current($this->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/cache/stores/mongodb/MongoDB/Model/IndexInfo.php b/cache/stores/mongodb/MongoDB/Model/IndexInfo.php deleted file mode 100644 index 5e49568d2f9..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/IndexInfo.php +++ /dev/null @@ -1,238 +0,0 @@ -info = $info; - } - - /** - * Return the collection info as an array. - * - * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array - */ - public function __debugInfo() - { - return $this->info; - } - - /** - * Return the index name to allow casting IndexInfo to string. - * - * @return string - */ - public function __toString() - { - return $this->getName(); - } - - /** - * Return the index key. - * - * @return array - */ - public function getKey() - { - return (array) $this->info['key']; - } - - /** - * Return the index name. - * - * @return string - */ - public function getName() - { - return (string) $this->info['name']; - } - - /** - * Return the index namespace (e.g. "db.collection"). - * - * @return string - */ - public function getNamespace() - { - return (string) $this->info['ns']; - } - - /** - * Return the index version. - * - * @return integer - */ - public function getVersion() - { - return (integer) $this->info['v']; - } - - /** - * Return whether or not this index is of type 2dsphere. - * - * @return boolean - */ - public function is2dSphere() - { - return array_search('2dsphere', $this->getKey(), true) !== false; - } - - /** - * Return whether or not this index is of type geoHaystack. - * - * @return boolean - */ - public function isGeoHaystack() - { - 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() - { - return ! empty($this->info['sparse']); - } - - /** - * Return whether or not this index is of type text. - * - * @return boolean - */ - public function isText() - { - return array_search('text', $this->getKey(), true) !== false; - } - - /** - * Return whether this is a TTL index. - * - * @see https://mongodb.com/docs/manual/core/index-ttl/ - * @return boolean - */ - public function isTtl() - { - return array_key_exists('expireAfterSeconds', $this->info); - } - - /** - * Return whether this is a unique index. - * - * @see https://mongodb.com/docs/manual/core/index-unique/ - * @return boolean - */ - public function isUnique() - { - return ! empty($this->info['unique']); - } - - /** - * Check whether a field exists in the index information. - * - * @see https://php.net/arrayaccess.offsetexists - * @param mixed $key - * @return boolean - */ - #[ReturnTypeWillChange] - public function offsetExists($key) - { - return array_key_exists($key, $this->info); - } - - /** - * Return the field's value from the index information. - * - * This method satisfies the Enumerating Indexes specification's requirement - * that index fields be made accessible under their original names. It may - * also be used to access fields that do not have a helper method. - * - * @see https://php.net/arrayaccess.offsetget - * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst#getting-full-index-information - * @param mixed $key - * @return mixed - */ - #[ReturnTypeWillChange] - public function offsetGet($key) - { - return $this->info[$key]; - } - - /** - * Not supported. - * - * @see https://php.net/arrayaccess.offsetset - * @param mixed $key - * @param mixed $value - * @throws BadMethodCallException - * @return void - */ - #[ReturnTypeWillChange] - public function offsetSet($key, $value) - { - throw BadMethodCallException::classIsImmutable(self::class); - } - - /** - * Not supported. - * - * @see https://php.net/arrayaccess.offsetunset - * @param mixed $key - * @throws BadMethodCallException - * @return void - */ - #[ReturnTypeWillChange] - public function offsetUnset($key) - { - throw BadMethodCallException::classIsImmutable(self::class); - } -} diff --git a/cache/stores/mongodb/MongoDB/Model/IndexInfoIterator.php b/cache/stores/mongodb/MongoDB/Model/IndexInfoIterator.php deleted file mode 100644 index 342fac6a86d..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/IndexInfoIterator.php +++ /dev/null @@ -1,40 +0,0 @@ -ns = $ns; - } - - /** - * 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/cache/stores/mongodb/MongoDB/Model/IndexInput.php b/cache/stores/mongodb/MongoDB/Model/IndexInput.php deleted file mode 100644 index 65c3fc2a2e1..00000000000 --- a/cache/stores/mongodb/MongoDB/Model/IndexInput.php +++ /dev/null @@ -1,95 +0,0 @@ - $order) { - if (! is_int($order) && ! is_float($order) && ! is_string($order)) { - throw InvalidArgumentException::invalidType(sprintf('order value for "%s" field within "key" option', $fieldName), $order, 'numeric or string'); - } - } - - if (! isset($index['name'])) { - $index['name'] = generate_index_name($index['key']); - } - - if (! is_string($index['name'])) { - throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string'); - } - - $this->index = $index; - } - - /** - * Return the index name. - */ - public function __toString(): string - { - return $this->index['name']; - } - - /** - * Serialize the index information to BSON for index creation. - * - * @see \MongoDB\Collection::createIndexes() - * @see https://php.net/mongodb-bson-serializable.bsonserialize - */ - public function bsonSerialize(): array - { - return $this->index; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Aggregate.php b/cache/stores/mongodb/MongoDB/Operation/Aggregate.php deleted file mode 100644 index 040e3c9c9f6..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Aggregate.php +++ /dev/null @@ -1,396 +0,0 @@ - $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$pipeline is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($operation) && ! is_object($operation)) { - throw InvalidArgumentException::invalidType(sprintf('$pipeline[%d]', $i), $operation, 'array or object'); - } - - $expectedIndex += 1; - } - - $options += ['useCursor' => true]; - - if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) { - throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean'); - } - - if (isset($options['batchSize']) && ! is_integer($options['batchSize'])) { - throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer'); - } - - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); - } - - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); - } - - if (isset($options['explain']) && ! is_bool($options['explain'])) { - throw InvalidArgumentException::invalidType('"explain" option', $options['explain'], 'boolean'); - } - - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object'); - } - - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], ['array', 'object']); - } - - if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer'); - } - - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); - } - - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); - } - - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); - } - - if (! is_bool($options['useCursor'])) { - throw InvalidArgumentException::invalidType('"useCursor" option', $options['useCursor'], 'boolean'); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - if (isset($options['batchSize']) && ! $options['useCursor']) { - throw new InvalidArgumentException('"batchSize" option should not be used if "useCursor" is false'); - } - - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); - } - - if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { - unset($options['readConcern']); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->isExplain = ! empty($options['explain']); - $this->isWrite = is_last_pipeline_operator_write($pipeline) && ! $this->isExplain; - - // Explain does not use a cursor - if ($this->isExplain) { - $options['useCursor'] = false; - unset($options['batchSize']); - } - - /* Ignore batchSize for writes, since no documents are returned and a - * batchSize of zero could prevent the pipeline from executing. */ - if ($this->isWrite) { - unset($options['batchSize']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->pipeline = $pipeline; - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return ArrayIterator|Cursor - * @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(); - } - } - - $command = new Command( - $this->createCommandDocument(), - $this->createCommandOptions() - ); - - $cursor = $this->executeCommand($server, $command); - - if ($this->options['useCursor'] || $this->isExplain) { - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return $cursor; - } - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'result.$')); - } - - $result = current($cursor->toArray()); - - if (! is_object($result) || ! isset($result->result) || ! is_array($result->result)) { - throw new UnexpectedValueException('aggregate command did not return a "result" array'); - } - - return new ArrayIterator($result->result); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->createCommandDocument(); - } - - /** - * Create the aggregate command document. - */ - private function createCommandDocument(): array - { - $cmd = [ - 'aggregate' => $this->collectionName ?? 1, - 'pipeline' => $this->pipeline, - ]; - - foreach (['allowDiskUse', 'bypassDocumentValidation', 'comment', 'explain', 'maxTimeMS'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - foreach (['collation', 'let'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = (object) $this->options[$option]; - } - } - - if (isset($this->options['hint'])) { - $cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint']; - } - - if ($this->options['useCursor']) { - $cmd['cursor'] = isset($this->options["batchSize"]) - ? ['batchSize' => $this->options["batchSize"]] - : new stdClass(); - } - - return $cmd; - } - - private function createCommandOptions(): array - { - $cmdOptions = []; - - if (isset($this->options['maxAwaitTimeMS'])) { - $cmdOptions['maxAwaitTimeMS'] = $this->options['maxAwaitTimeMS']; - } - - return $cmdOptions; - } - - /** - * Execute the aggregate command using the appropriate Server method. - * - * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php - * @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 - { - $options = []; - - foreach (['readConcern', 'readPreference', 'session'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = $this->options[$option]; - } - } - - if ($this->isWrite && isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - if (! $this->isWrite) { - return $server->executeReadCommand($this->databaseName, $command, $options); - } - - /* Server::executeReadWriteCommand() does not support a "readPreference" - * option, so fall back to executeCommand(). This means that libmongoc - * will not apply any client-level options (e.g. writeConcern), but that - * should not be an issue as PHPLIB handles inheritance on its own. */ - if (isset($options['readPreference'])) { - return $server->executeCommand($this->databaseName, $command, $options); - } - - return $server->executeReadWriteCommand($this->databaseName, $command, $options); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/BulkWrite.php b/cache/stores/mongodb/MongoDB/Operation/BulkWrite.php deleted file mode 100644 index 28568de2505..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/BulkWrite.php +++ /dev/null @@ -1,391 +0,0 @@ - [ $filter, $options ] ], - * [ 'deleteOne' => [ $filter, $options ] ], - * [ 'insertOne' => [ $document ] ], - * [ 'replaceOne' => [ $filter, $replacement, $options ] ], - * [ 'updateMany' => [ $filter, $update, $options ] ], - * [ 'updateOne' => [ $filter, $update, $options ] ], - * ] - * - * Arguments correspond to the respective Operation classes; however, the - * writeConcern option is specified for the top-level bulk write operation - * instead of each individual operation. - * - * Supported options for deleteMany and deleteOne operations: - * - * * collation (document): Collation specification. - * - * Supported options for replaceOne, updateMany, and updateOne operations: - * - * * collation (document): Collation specification. - * - * * upsert (boolean): When true, a new document is created if no document - * matches the query. The default is false. - * - * Supported options for updateMany and updateOne operations: - * - * * arrayFilters (document array): A set of filters specifying to which - * array elements an update should apply. - * - * Supported options for the bulk write operation: - * - * * bypassDocumentValidation (boolean): If true, allows the write to - * circumvent document level validation. The default is false. - * - * * comment (mixed): BSON value to attach as a comment to this command(s) - * associated with this bulk write. - * - * This is not supported for servers versions < 4.4. - * - * * ordered (boolean): If true, when an insert fails, return without - * performing the remaining writes. If false, when a write fails, - * continue with the remaining writes, if any. The default is true. - * - * * let (document): Map of parameter names and values. Values must be - * constant or closed expressions that do not reference document fields. - * Parameters can then be accessed as variables in an aggregate - * expression context (e.g. "$$var"). - * - * * session (MongoDB\Driver\Session): Client session. - * - * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. - * - * @param string $databaseName Database name - * @param string $collectionName Collection name - * @param array[] $operations List of write operations - * @param array $options Command options - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function __construct(string $databaseName, string $collectionName, array $operations, array $options = []) - { - if (empty($operations)) { - throw new InvalidArgumentException('$operations is empty'); - } - - $expectedIndex = 0; - - foreach ($operations as $i => $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$operations is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($operation)) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]', $i), $operation, 'array'); - } - - if (count($operation) !== 1) { - throw new InvalidArgumentException(sprintf('Expected one element in $operation[%d], actually: %d', $i, count($operation))); - } - - $type = key($operation); - $args = current($operation); - - if (! isset($args[0]) && ! array_key_exists(0, $args)) { - throw new InvalidArgumentException(sprintf('Missing first argument for $operations[%d]["%s"]', $i, $type)); - } - - if (! is_array($args[0]) && ! is_object($args[0])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][0]', $i, $type), $args[0], 'array or object'); - } - - switch ($type) { - case self::INSERT_ONE: - break; - - case self::DELETE_MANY: - case self::DELETE_ONE: - if (! isset($args[1])) { - $args[1] = []; - } - - if (! is_array($args[1])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array'); - } - - $args[1]['limit'] = ($type === self::DELETE_ONE ? 1 : 0); - - if (isset($args[1]['collation']) && ! is_array($args[1]['collation']) && ! is_object($args[1]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation'], 'array or object'); - } - - $operations[$i][$type][1] = $args[1]; - - break; - - case self::REPLACE_ONE: - if (! isset($args[1]) && ! array_key_exists(1, $args)) { - throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type)); - } - - if (! is_array($args[1]) && ! is_object($args[1])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array or object'); - } - - if (is_first_key_operator($args[1])) { - throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is an update operator', $i, $type)); - } - - if (! isset($args[2])) { - $args[2] = []; - } - - if (! is_array($args[2])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array'); - } - - $args[2]['multi'] = false; - $args[2] += ['upsert' => false]; - - if (isset($args[2]['collation']) && ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); - } - - if (! is_bool($args[2]['upsert'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); - } - - $operations[$i][$type][2] = $args[2]; - - break; - - case self::UPDATE_MANY: - case self::UPDATE_ONE: - if (! isset($args[1]) && ! array_key_exists(1, $args)) { - throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type)); - } - - if (! is_array($args[1]) && ! is_object($args[1])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array or object'); - } - - if (! is_first_key_operator($args[1]) && ! is_pipeline($args[1])) { - throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is neither an update operator nor a pipeline', $i, $type)); - } - - if (! isset($args[2])) { - $args[2] = []; - } - - if (! is_array($args[2])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array'); - } - - $args[2]['multi'] = ($type === self::UPDATE_MANY); - $args[2] += ['upsert' => false]; - - if (isset($args[2]['arrayFilters']) && ! is_array($args[2]['arrayFilters'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["arrayFilters"]', $i, $type), $args[2]['arrayFilters'], 'array'); - } - - if (isset($args[2]['collation']) && ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); - } - - if (! is_bool($args[2]['upsert'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); - } - - $operations[$i][$type][2] = $args[2]; - - break; - - default: - throw new InvalidArgumentException(sprintf('Unknown operation type "%s" in $operations[%d]', $type, $i)); - } - - $expectedIndex += 1; - } - - $options += ['ordered' => true]; - - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); - } - - if (! is_bool($options['ordered'])) { - throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean'); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); - } - - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->operations = $operations; - $this->options = $options; - } - - /** - * 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) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $bulk = new Bulk($this->createBulkWriteOptions()); - $insertedIds = []; - - foreach ($this->operations as $i => $operation) { - $type = key($operation); - $args = current($operation); - - switch ($type) { - case self::DELETE_MANY: - case self::DELETE_ONE: - $bulk->delete($args[0], $args[1]); - break; - - case self::INSERT_ONE: - $insertedIds[$i] = $bulk->insert($args[0]); - break; - - case self::REPLACE_ONE: - case self::UPDATE_MANY: - case self::UPDATE_ONE: - $bulk->update($args[0], $args[1], $args[2]); - } - } - - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); - - return new BulkWriteResult($writeResult, $insertedIds); - } - - /** - * Create options for constructing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php - */ - private function createBulkWriteOptions(): array - { - $options = ['ordered' => $this->options['ordered']]; - - foreach (['bypassDocumentValidation', 'comment'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = $this->options[$option]; - } - } - - if (isset($this->options['let'])) { - $options['let'] = (object) $this->options['let']; - } - - return $options; - } - - /** - * Create options for executing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - */ - private function createExecuteOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Count.php b/cache/stores/mongodb/MongoDB/Operation/Count.php deleted file mode 100644 index 7e427313c0c..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Count.php +++ /dev/null @@ -1,230 +0,0 @@ -isDefault()) { - unset($options['readConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->options = $options; - } - - /** - * 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) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['readConcern'])) { - throw UnsupportedException::readConcernNotSupportedInTransaction(); - } - - $cursor = $server->executeReadCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions()); - $result = current($cursor->toArray()); - - // Older server versions may return a float - if (! is_object($result) || ! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) { - throw new UnexpectedValueException('count command did not return a numeric "n" value'); - } - - return (integer) $result->n; - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->createCommandDocument(); - } - - /** - * Create the count command document. - */ - private function createCommandDocument(): array - { - $cmd = ['count' => $this->collectionName]; - - if (! empty($this->filter)) { - $cmd['query'] = (object) $this->filter; - } - - if (isset($this->options['collation'])) { - $cmd['collation'] = (object) $this->options['collation']; - } - - if (isset($this->options['hint'])) { - $cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint']; - } - - foreach (['comment', 'limit', 'maxTimeMS', 'skip'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return $cmd; - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['readConcern'])) { - $options['readConcern'] = $this->options['readConcern']; - } - - if (isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/CountDocuments.php b/cache/stores/mongodb/MongoDB/Operation/CountDocuments.php deleted file mode 100644 index 57b04544b29..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/CountDocuments.php +++ /dev/null @@ -1,170 +0,0 @@ -databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - - $this->aggregateOptions = array_intersect_key($options, ['collation' => 1, 'comment' => 1, 'hint' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]); - $this->countOptions = array_intersect_key($options, ['limit' => 1, 'skip' => 1]); - - $this->aggregate = $this->createAggregate(); - } - - /** - * 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) - { - $cursor = $this->aggregate->execute($server); - assert($cursor instanceof Cursor); - - $allResults = $cursor->toArray(); - - /* If there are no documents to count, the aggregation pipeline has no items to group, and - * hence the result is an empty array (PHPLIB-376) */ - if (count($allResults) == 0) { - return 0; - } - - $result = current($allResults); - if (! is_object($result) || ! isset($result->n) || ! (is_integer($result->n) || is_float($result->n))) { - throw new UnexpectedValueException('count command did not return a numeric "n" value'); - } - - return (integer) $result->n; - } - - private function createAggregate(): Aggregate - { - $pipeline = [ - ['$match' => (object) $this->filter], - ]; - - if (isset($this->countOptions['skip'])) { - $pipeline[] = ['$skip' => $this->countOptions['skip']]; - } - - if (isset($this->countOptions['limit'])) { - $pipeline[] = ['$limit' => $this->countOptions['limit']]; - } - - $pipeline[] = ['$group' => ['_id' => 1, 'n' => ['$sum' => 1]]]; - - return new Aggregate($this->databaseName, $this->collectionName, $pipeline, $this->aggregateOptions); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/CreateCollection.php b/cache/stores/mongodb/MongoDB/Operation/CreateCollection.php deleted file mode 100644 index 8860a28897b..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/CreateCollection.php +++ /dev/null @@ -1,323 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - if (isset($options['autoIndexId'])) { - trigger_error('The "autoIndexId" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); - } - - if (isset($options['pipeline'])) { - $expectedIndex = 0; - - foreach ($options['pipeline'] as $i => $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('The "pipeline" option is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($operation) && ! is_object($operation)) { - throw InvalidArgumentException::invalidType(sprintf('$options["pipeline"][%d]', $i), $operation, 'array or object'); - } - - $expectedIndex += 1; - } - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $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) - { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); - } - - /** - * Create the create command. - */ - private function createCommand(): Command - { - $cmd = ['create' => $this->collectionName]; - - foreach (['autoIndexId', 'capped', 'comment', 'expireAfterSeconds', 'flags', 'max', 'maxTimeMS', 'pipeline', 'size', 'validationAction', 'validationLevel', 'viewOn'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - foreach (['changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'encryptedFields', 'indexOptionDefaults', 'storageEngine', 'timeseries', 'validator'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = (object) $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/CreateIndexes.php b/cache/stores/mongodb/MongoDB/Operation/CreateIndexes.php deleted file mode 100644 index 351f2511652..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/CreateIndexes.php +++ /dev/null @@ -1,207 +0,0 @@ - $index) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$indexes is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($index)) { - throw InvalidArgumentException::invalidType(sprintf('$index[%d]', $i), $index, 'array'); - } - - $this->indexes[] = new IndexInput($index); - - $expectedIndex += 1; - } - - if (isset($options['commitQuorum']) && ! is_string($options['commitQuorum']) && ! is_integer($options['commitQuorum'])) { - throw InvalidArgumentException::invalidType('"commitQuorum" option', $options['commitQuorum'], ['integer', 'string']); - } - - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; - } - - /** - * 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) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $this->executeCommand($server); - - return array_map(function (IndexInput $index) { - return (string) $index; - }, $this->indexes); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } - - /** - * Create one or more indexes for the collection using the createIndexes - * command. - * - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - private function executeCommand(Server $server): void - { - $cmd = [ - 'createIndexes' => $this->collectionName, - 'indexes' => $this->indexes, - ]; - - if (isset($this->options['commitQuorum'])) { - /* Drivers MUST manually raise an error if this option is specified - * when creating an index on a pre 4.4 server. */ - if (! server_supports_feature($server, self::$wireVersionForCommitQuorum)) { - throw UnsupportedException::commitQuorumNotSupported(); - } - - $cmd['commitQuorum'] = $this->options['commitQuorum']; - } - - foreach (['comment', 'maxTimeMS'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - $server->executeWriteCommand($this->databaseName, new Command($cmd), $this->createOptions()); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/DatabaseCommand.php b/cache/stores/mongodb/MongoDB/Operation/DatabaseCommand.php deleted file mode 100644 index 4e6fac52268..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/DatabaseCommand.php +++ /dev/null @@ -1,127 +0,0 @@ -databaseName = $databaseName; - $this->command = $command instanceof Command ? $command : new Command($command); - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return Cursor - */ - public function execute(Server $server) - { - $cursor = $server->executeCommand($this->databaseName, $this->command, $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return $cursor; - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Delete.php b/cache/stores/mongodb/MongoDB/Operation/Delete.php deleted file mode 100644 index 7ca197f7af3..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Delete.php +++ /dev/null @@ -1,250 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->limit = $limit; - $this->options = $options; - } - - /** - * 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) - { - /* CRUD spec requires a client-side error when using "hint" with an - * unacknowledged write concern on an unsupported server. */ - if ( - isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && - isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint) - ) { - throw UnsupportedException::hintNotSupported(); - } - - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $bulk = new Bulk($this->createBulkWriteOptions()); - $bulk->delete($this->filter, $this->createDeleteOptions()); - - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); - - return new DeleteResult($writeResult); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - $cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]]; - - if (isset($this->options['writeConcern'])) { - $cmd['writeConcern'] = $this->options['writeConcern']; - } - - return $cmd; - } - - /** - * Create options for constructing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php - */ - private function createBulkWriteOptions(): array - { - $options = []; - - if (isset($this->options['comment'])) { - $options['comment'] = $this->options['comment']; - } - - if (isset($this->options['let'])) { - $options['let'] = (object) $this->options['let']; - } - - return $options; - } - - /** - * Create options for the delete command. - * - * Note that these options are different from the bulk write options, which - * are created in createExecuteOptions(). - */ - private function createDeleteOptions(): array - { - $deleteOptions = ['limit' => $this->limit]; - - if (isset($this->options['collation'])) { - $deleteOptions['collation'] = (object) $this->options['collation']; - } - - if (isset($this->options['hint'])) { - $deleteOptions['hint'] = $this->options['hint']; - } - - return $deleteOptions; - } - - /** - * Create options for executing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - */ - private function createExecuteOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/DeleteMany.php b/cache/stores/mongodb/MongoDB/Operation/DeleteMany.php deleted file mode 100644 index 33497f3f471..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/DeleteMany.php +++ /dev/null @@ -1,99 +0,0 @@ -delete = new Delete($databaseName, $collectionName, $filter, 0, $options); - } - - /** - * 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) - { - return $this->delete->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->delete->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/DeleteOne.php b/cache/stores/mongodb/MongoDB/Operation/DeleteOne.php deleted file mode 100644 index a91d67bb57a..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/DeleteOne.php +++ /dev/null @@ -1,99 +0,0 @@ -delete = new Delete($databaseName, $collectionName, $filter, 1, $options); - } - - /** - * 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) - { - return $this->delete->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->delete->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Distinct.php b/cache/stores/mongodb/MongoDB/Operation/Distinct.php deleted file mode 100644 index 3c2f93dbd8c..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Distinct.php +++ /dev/null @@ -1,222 +0,0 @@ -isDefault()) { - unset($options['readConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->fieldName = $fieldName; - $this->filter = $filter; - $this->options = $options; - } - - /** - * 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) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['readConcern'])) { - throw UnsupportedException::readConcernNotSupportedInTransaction(); - } - - $cursor = $server->executeReadCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'values.$')); - } - - $result = current($cursor->toArray()); - - if (! is_object($result) || ! isset($result->values) || ! is_array($result->values)) { - throw new UnexpectedValueException('distinct command did not return a "values" array'); - } - - return $result->values; - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->createCommandDocument(); - } - - /** - * Create the distinct command document. - */ - private function createCommandDocument(): array - { - $cmd = [ - 'distinct' => $this->collectionName, - 'key' => $this->fieldName, - ]; - - if (! empty($this->filter)) { - $cmd['query'] = (object) $this->filter; - } - - if (isset($this->options['collation'])) { - $cmd['collation'] = (object) $this->options['collation']; - } - - foreach (['comment', 'maxTimeMS'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return $cmd; - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['readConcern'])) { - $options['readConcern'] = $this->options['readConcern']; - } - - if (isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/DropCollection.php b/cache/stores/mongodb/MongoDB/Operation/DropCollection.php deleted file mode 100644 index 11e85bdd79b..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/DropCollection.php +++ /dev/null @@ -1,166 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; - } - - /** - * 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) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - try { - $cursor = $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. */ - if ($e->getCode() === self::$errorCodeNamespaceNotFound) { - return $e->getResultDocument(); - } - - throw $e; - } - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); - } - - /** - * Create the drop command. - */ - private function createCommand(): Command - { - $cmd = ['drop' => $this->collectionName]; - - if (isset($this->options['comment'])) { - $cmd['comment'] = $this->options['comment']; - } - - return new Command($cmd); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/DropDatabase.php b/cache/stores/mongodb/MongoDB/Operation/DropDatabase.php deleted file mode 100644 index c909b2436c2..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/DropDatabase.php +++ /dev/null @@ -1,139 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->options = $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) - { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); - } - - /** - * Create the dropDatabase command. - */ - private function createCommand(): Command - { - $cmd = ['dropDatabase' => 1]; - - if (isset($this->options['comment'])) { - $cmd['comment'] = $this->options['comment']; - } - - return new Command($cmd); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/DropIndexes.php b/cache/stores/mongodb/MongoDB/Operation/DropIndexes.php deleted file mode 100644 index ef0e2f4d3c8..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/DropIndexes.php +++ /dev/null @@ -1,174 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->indexName = $indexName; - $this->options = $options; - } - - /** - * 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) - { - $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()); - } - - /** - * Create the dropIndexes command. - */ - private function createCommand(): Command - { - $cmd = [ - 'dropIndexes' => $this->collectionName, - 'index' => $this->indexName, - ]; - - foreach (['comment', 'maxTimeMS'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/EstimatedDocumentCount.php b/cache/stores/mongodb/MongoDB/Operation/EstimatedDocumentCount.php deleted file mode 100644 index 6deeef3e535..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/EstimatedDocumentCount.php +++ /dev/null @@ -1,133 +0,0 @@ -databaseName = $databaseName; - $this->collectionName = $collectionName; - - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); - } - - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); - } - - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - $this->options = array_intersect_key($options, ['comment' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]); - } - - /** - * 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) - { - return $this->createCount()->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->createCount()->getCommandDocument($server); - } - - private function createCount(): Count - { - return new Count($this->databaseName, $this->collectionName, [], $this->options); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Executable.php b/cache/stores/mongodb/MongoDB/Operation/Executable.php deleted file mode 100644 index 5bd8c68b385..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Executable.php +++ /dev/null @@ -1,38 +0,0 @@ -databaseName = $databaseName; - $this->explainable = $explainable; - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return array|object - * @throws UnsupportedException if the server does not support explaining the operation - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) - { - if ($this->explainable instanceof Aggregate && ! server_supports_feature($server, self::$wireVersionForAggregate)) { - throw UnsupportedException::explainNotSupported(); - } - - $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); - } - - /** - * Create the explain command. - */ - private function createCommand(Server $server): Command - { - $cmd = ['explain' => $this->explainable->getCommandDocument($server)]; - - foreach (['comment', 'verbosity'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } - - private function isFindAndModify(Explainable $explainable): bool - { - if ($explainable instanceof FindAndModify || $explainable instanceof FindOneAndDelete || $explainable instanceof FindOneAndReplace || $explainable instanceof FindOneAndUpdate) { - return true; - } - - return false; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Explainable.php b/cache/stores/mongodb/MongoDB/Operation/Explainable.php deleted file mode 100644 index 7647b51ec00..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Explainable.php +++ /dev/null @@ -1,36 +0,0 @@ -isDefault()) { - unset($options['readConcern']); - } - - if (isset($options['snapshot'])) { - trigger_error('The "snapshot" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); - } - - if (isset($options['maxScan'])) { - trigger_error('The "maxScan" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return Cursor - * @throws UnsupportedException if read 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 && isset($this->options['readConcern'])) { - throw UnsupportedException::readConcernNotSupportedInTransaction(); - } - - $cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, new Query($this->filter, $this->createQueryOptions()), $this->createExecuteOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return $cursor; - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->createCommandDocument(); - } - - /** - * Construct a command document for Find - */ - private function createCommandDocument(): array - { - $cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter]; - - $options = $this->createQueryOptions(); - - if (empty($options)) { - return $cmd; - } - - // 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; - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executequery.php - */ - private function createExecuteOptions(): array - { - $options = []; - - if (isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } - - /** - * Create options for the find query. - * - * Note that these are separate from the options for executing the command, - * which are created in createExecuteOptions(). - */ - private function createQueryOptions(): array - { - $options = []; - - if (isset($this->options['cursorType'])) { - if ($this->options['cursorType'] === self::TAILABLE) { - $options['tailable'] = true; - } - - if ($this->options['cursorType'] === self::TAILABLE_AWAIT) { - $options['tailable'] = true; - $options['awaitData'] = true; - } - } - - foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = $this->options[$option]; - } - } - - foreach (['collation', 'let', 'max', 'min'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = (object) $this->options[$option]; - } - } - - $modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers']; - - if (! empty($modifiers)) { - $options['modifiers'] = $modifiers; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/FindAndModify.php b/cache/stores/mongodb/MongoDB/Operation/FindAndModify.php deleted file mode 100644 index d4fc2b3a4c1..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/FindAndModify.php +++ /dev/null @@ -1,329 +0,0 @@ -= 4.4. Using this option in - * other contexts will result in an exception at execution time. - * - * * maxTimeMS (integer): The maximum amount of time to allow the query to - * run. - * - * * new (boolean): When true, returns the modified document rather than - * the original. This option is ignored for remove operations. The - * The default is false. - * - * * query (document): Query by which to filter documents. - * - * * remove (boolean): When true, removes the matched document. This option - * cannot be true if the update option is set. The default is false. - * - * * session (MongoDB\Driver\Session): Client session. - * - * * sort (document): Determines which document the operation modifies if - * the query selects multiple documents. - * - * * typeMap (array): Type map for BSON deserialization. - * - * * update (document): Update or replacement to apply to the matched - * document. This option cannot be set if the remove option is true. - * - * * upsert (boolean): When true, a new document is created if no document - * matches the query. This option is ignored for remove operations. The - * default is false. - * - * * let (document): Map of parameter names and values. Values must be - * constant or closed expressions that do not reference document fields. - * Parameters can then be accessed as variables in an aggregate - * expression context (e.g. "$$var"). - * - * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. - * - * @param string $databaseName Database name - * @param string $collectionName Collection name - * @param array $options Command options - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function __construct(string $databaseName, string $collectionName, array $options) - { - $options += ['remove' => false]; - - if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { - throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); - } - - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); - } - - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); - } - - if (isset($options['fields']) && ! is_array($options['fields']) && ! is_object($options['fields'])) { - throw InvalidArgumentException::invalidType('"fields" option', $options['fields'], 'array or object'); - } - - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']); - } - - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); - } - - if (array_key_exists('new', $options) && ! is_bool($options['new'])) { - throw InvalidArgumentException::invalidType('"new" option', $options['new'], 'boolean'); - } - - if (isset($options['query']) && ! is_array($options['query']) && ! is_object($options['query'])) { - throw InvalidArgumentException::invalidType('"query" option', $options['query'], 'array or object'); - } - - if (! is_bool($options['remove'])) { - throw InvalidArgumentException::invalidType('"remove" option', $options['remove'], 'boolean'); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) { - throw InvalidArgumentException::invalidType('"sort" option', $options['sort'], 'array or object'); - } - - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); - } - - if (isset($options['update']) && ! is_array($options['update']) && ! is_object($options['update'])) { - throw InvalidArgumentException::invalidType('"update" option', $options['update'], 'array or object'); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - if (array_key_exists('upsert', $options) && ! is_bool($options['upsert'])) { - throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); - } - - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); - } - - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); - } - - if (! (isset($options['update']) xor $options['remove'])) { - throw new InvalidArgumentException('The "remove" option must be true or an "update" document must be specified, but not both'); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; - } - - /** - * 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) - { - /* Server versions >= 4.2.0 raise errors for unsupported update options. - * For previous versions, the CRUD spec requires a client-side error. */ - if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForUnsupportedOptionServerSideError)) { - throw UnsupportedException::hintNotSupported(); - } - - /* CRUD spec requires a client-side error when using "hint" with an - * unacknowledged write concern on an unsupported server. */ - if ( - isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && - isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint) - ) { - throw UnsupportedException::hintNotSupported(); - } - - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $cursor = $server->executeWriteCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'value')); - } - - $result = current($cursor->toArray()); - - return is_object($result) ? ($result->value ?? null) : null; - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->createCommandDocument(); - } - - /** - * Create the findAndModify command document. - */ - private function createCommandDocument(): array - { - $cmd = ['findAndModify' => $this->collectionName]; - - if ($this->options['remove']) { - $cmd['remove'] = true; - } else { - if (isset($this->options['new'])) { - $cmd['new'] = $this->options['new']; - } - - if (isset($this->options['upsert'])) { - $cmd['upsert'] = $this->options['upsert']; - } - } - - foreach (['collation', 'fields', 'let', 'query', 'sort'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = (object) $this->options[$option]; - } - } - - if (isset($this->options['update'])) { - $cmd['update'] = is_pipeline($this->options['update']) - ? $this->options['update'] - : (object) $this->options['update']; - } - - foreach (['arrayFilters', 'bypassDocumentValidation', 'comment', 'hint', 'maxTimeMS'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return $cmd; - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/FindOne.php b/cache/stores/mongodb/MongoDB/Operation/FindOne.php deleted file mode 100644 index 3de70dae6ea..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/FindOne.php +++ /dev/null @@ -1,142 +0,0 @@ -find = new Find( - $databaseName, - $collectionName, - $filter, - ['limit' => 1] + $options - ); - } - - /** - * 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) - { - $cursor = $this->find->execute($server); - $document = current($cursor->toArray()); - - return $document === false ? null : $document; - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->find->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/FindOneAndDelete.php b/cache/stores/mongodb/MongoDB/Operation/FindOneAndDelete.php deleted file mode 100644 index c4b54dadfe0..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/FindOneAndDelete.php +++ /dev/null @@ -1,130 +0,0 @@ -findAndModify = new FindAndModify( - $databaseName, - $collectionName, - ['query' => $filter, 'remove' => true] + $options - ); - } - - /** - * 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) - { - return $this->findAndModify->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->findAndModify->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/FindOneAndReplace.php b/cache/stores/mongodb/MongoDB/Operation/FindOneAndReplace.php deleted file mode 100644 index 0e619b86d21..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/FindOneAndReplace.php +++ /dev/null @@ -1,173 +0,0 @@ -findAndModify = new FindAndModify( - $databaseName, - $collectionName, - ['query' => $filter, 'update' => $replacement] + $options - ); - } - - /** - * 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) - { - return $this->findAndModify->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->findAndModify->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/FindOneAndUpdate.php b/cache/stores/mongodb/MongoDB/Operation/FindOneAndUpdate.php deleted file mode 100644 index e9a592473e5..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/FindOneAndUpdate.php +++ /dev/null @@ -1,177 +0,0 @@ -findAndModify = new FindAndModify( - $databaseName, - $collectionName, - ['query' => $filter, 'update' => $update] + $options - ); - } - - /** - * 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) - { - return $this->findAndModify->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->findAndModify->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/InsertMany.php b/cache/stores/mongodb/MongoDB/Operation/InsertMany.php deleted file mode 100644 index b8b3c12553c..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/InsertMany.php +++ /dev/null @@ -1,198 +0,0 @@ - $document) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$documents is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($document) && ! is_object($document)) { - throw InvalidArgumentException::invalidType(sprintf('$documents[%d]', $i), $document, 'array or object'); - } - - $expectedIndex += 1; - } - - $options += ['ordered' => true]; - - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); - } - - if (! is_bool($options['ordered'])) { - throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean'); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->documents = $documents; - $this->options = $options; - } - - /** - * 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) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $bulk = new Bulk($this->createBulkWriteOptions()); - $insertedIds = []; - - foreach ($this->documents as $i => $document) { - $insertedIds[$i] = $bulk->insert($document); - } - - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); - - return new InsertManyResult($writeResult, $insertedIds); - } - - /** - * Create options for constructing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php - */ - private function createBulkWriteOptions(): array - { - $options = ['ordered' => $this->options['ordered']]; - - foreach (['bypassDocumentValidation', 'comment'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = $this->options[$option]; - } - } - - return $options; - } - - /** - * Create options for executing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - */ - private function createExecuteOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/InsertOne.php b/cache/stores/mongodb/MongoDB/Operation/InsertOne.php deleted file mode 100644 index d0690a5d57a..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/InsertOne.php +++ /dev/null @@ -1,168 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->document = $document; - $this->options = $options; - } - - /** - * 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) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if (isset($this->options['writeConcern']) && $inTransaction) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $bulk = new Bulk($this->createBulkWriteOptions()); - $insertedId = $bulk->insert($this->document); - - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); - - return new InsertOneResult($writeResult, $insertedId); - } - - /** - * Create options for constructing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php - */ - private function createBulkWriteOptions(): array - { - $options = []; - - foreach (['bypassDocumentValidation', 'comment'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = $this->options[$option]; - } - } - - return $options; - } - - /** - * Create options for executing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - */ - private function createExecuteOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/ListCollectionNames.php b/cache/stores/mongodb/MongoDB/Operation/ListCollectionNames.php deleted file mode 100644 index d0700887bf1..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/ListCollectionNames.php +++ /dev/null @@ -1,85 +0,0 @@ -listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => true] + $options); - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @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), - function (array $collectionInfo) { - return $collectionInfo['name']; - } - ); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/ListCollections.php b/cache/stores/mongodb/MongoDB/Operation/ListCollections.php deleted file mode 100644 index e5702509b49..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/ListCollections.php +++ /dev/null @@ -1,84 +0,0 @@ -databaseName = $databaseName; - $this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => false] + $options); - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return CollectionInfoIterator - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) - { - return new CollectionInfoCommandIterator($this->listCollections->execute($server), $this->databaseName); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/ListDatabaseNames.php b/cache/stores/mongodb/MongoDB/Operation/ListDatabaseNames.php deleted file mode 100644 index ccace38f8dc..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/ListDatabaseNames.php +++ /dev/null @@ -1,84 +0,0 @@ -listDatabases = new ListDatabasesCommand(['nameOnly' => true] + $options); - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @throws UnexpectedValueException if the command response was malformed - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server): Iterator - { - $result = $this->listDatabases->execute($server); - - return new ArrayIterator(array_column($result, 'name')); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/ListDatabases.php b/cache/stores/mongodb/MongoDB/Operation/ListDatabases.php deleted file mode 100644 index a19cf07bb3b..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/ListDatabases.php +++ /dev/null @@ -1,81 +0,0 @@ -listDatabases = new ListDatabasesCommand(['nameOnly' => false] + $options); - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return DatabaseInfoIterator - * @throws UnexpectedValueException if the command response was malformed - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) - { - return new DatabaseInfoLegacyIterator($this->listDatabases->execute($server)); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/ListIndexes.php b/cache/stores/mongodb/MongoDB/Operation/ListIndexes.php deleted file mode 100644 index e762d9ca2f3..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/ListIndexes.php +++ /dev/null @@ -1,156 +0,0 @@ -databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return IndexInfoIterator - * @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 - { - $cmd = ['listIndexes' => $this->collectionName]; - - foreach (['comment', 'maxTimeMS'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - try { - $cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions()); - } 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::$errorCodeNamespaceNotFound || $e->getCode() === self::$errorCodeDatabaseNotFound) { - return new IndexInfoIteratorIterator(new EmptyIterator()); - } - - throw $e; - } - - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - - return new IndexInfoIteratorIterator(new CachingIterator($cursor), $this->databaseName . '.' . $this->collectionName); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/MapReduce.php b/cache/stores/mongodb/MongoDB/Operation/MapReduce.php deleted file mode 100644 index 7b97dd0a9cc..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/MapReduce.php +++ /dev/null @@ -1,418 +0,0 @@ -isDefault()) { - unset($options['readConcern']); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($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($options['finalize']) && $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->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->map = $map; - $this->reduce = $reduce; - $this->out = $out; - $this->options = $options; - } - - /** - * 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); - } - - /** - * @param string|array|object $out - */ - private function checkOutDeprecations($out): void - { - if (is_string($out)) { - return; - } - - $out = (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(). - * - * @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 function () use ($results) { - return 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 function () use ($find, $server) { - return $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/cache/stores/mongodb/MongoDB/Operation/ModifyCollection.php b/cache/stores/mongodb/MongoDB/Operation/ModifyCollection.php deleted file mode 100644 index 7933859bfda..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/ModifyCollection.php +++ /dev/null @@ -1,149 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->collectionOptions = $collectionOptions; - $this->options = $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) - { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); - } - - private function createCommand(): Command - { - $cmd = ['collMod' => $this->collectionName] + $this->collectionOptions; - - if (isset($this->options['comment'])) { - $cmd['comment'] = $this->options['comment']; - } - - return new Command($cmd); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/RenameCollection.php b/cache/stores/mongodb/MongoDB/Operation/RenameCollection.php deleted file mode 100644 index 64758b0a788..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/RenameCollection.php +++ /dev/null @@ -1,166 +0,0 @@ -isDefault()) { - unset($options['writeConcern']); - } - - if (isset($options['dropTarget']) && ! is_bool($options['dropTarget'])) { - throw InvalidArgumentException::invalidType('"dropTarget" option', $options['dropTarget'], 'boolean'); - } - - $this->fromNamespace = $fromDatabaseName . '.' . $fromCollectionName; - $this->toNamespace = $toDatabaseName . '.' . $toCollectionName; - $this->options = $options; - } - - /** - * 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) - { - $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()); - } - - /** - * Create the renameCollection command. - */ - private function createCommand(): Command - { - $cmd = [ - 'renameCollection' => $this->fromNamespace, - 'to' => $this->toNamespace, - ]; - - foreach (['comment', 'dropTarget'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executewritecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/ReplaceOne.php b/cache/stores/mongodb/MongoDB/Operation/ReplaceOne.php deleted file mode 100644 index d5d117b91d8..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/ReplaceOne.php +++ /dev/null @@ -1,118 +0,0 @@ -update = new Update( - $databaseName, - $collectionName, - $filter, - $replacement, - ['multi' => false] + $options - ); - } - - /** - * 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) - { - return $this->update->execute($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Update.php b/cache/stores/mongodb/MongoDB/Operation/Update.php deleted file mode 100644 index 96a51c53ff7..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Update.php +++ /dev/null @@ -1,305 +0,0 @@ - false, - 'upsert' => false, - ]; - - if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { - throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); - } - - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); - } - - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); - } - - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']); - } - - if (! is_bool($options['multi'])) { - throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean'); - } - - if ($options['multi'] && ! is_first_key_operator($update) && ! is_pipeline($update)) { - throw new InvalidArgumentException('"multi" option cannot be true if $update is a replacement document'); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (! is_bool($options['upsert'])) { - throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); - } - - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->update = $update; - $this->options = $options; - } - - /** - * 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) - { - /* CRUD spec requires a client-side error when using "hint" with an - * unacknowledged write concern on an unsupported server. */ - if ( - isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && - isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint) - ) { - throw UnsupportedException::hintNotSupported(); - } - - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $bulk = new Bulk($this->createBulkWriteOptions()); - $bulk->update($this->filter, $this->update, $this->createUpdateOptions()); - - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); - - return new UpdateResult($writeResult); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - $cmd = ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]]; - - if (isset($this->options['bypassDocumentValidation'])) { - $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; - } - - if (isset($this->options['writeConcern'])) { - $cmd['writeConcern'] = $this->options['writeConcern']; - } - - return $cmd; - } - - /** - * Create options for constructing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php - */ - private function createBulkWriteOptions(): array - { - $options = []; - - foreach (['bypassDocumentValidation', 'comment'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = $this->options[$option]; - } - } - - if (isset($this->options['let'])) { - $options['let'] = (object) $this->options['let']; - } - - return $options; - } - - /** - * Create options for executing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - */ - private function createExecuteOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } - - /** - * Create options for the update command. - * - * Note that these options are different from the bulk write options, which - * are created in createExecuteOptions(). - */ - private function createUpdateOptions(): array - { - $updateOptions = [ - 'multi' => $this->options['multi'], - 'upsert' => $this->options['upsert'], - ]; - - foreach (['arrayFilters', 'hint'] as $option) { - if (isset($this->options[$option])) { - $updateOptions[$option] = $this->options[$option]; - } - } - - if (isset($this->options['collation'])) { - $updateOptions['collation'] = (object) $this->options['collation']; - } - - return $updateOptions; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/UpdateMany.php b/cache/stores/mongodb/MongoDB/Operation/UpdateMany.php deleted file mode 100644 index e442365a1bf..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/UpdateMany.php +++ /dev/null @@ -1,128 +0,0 @@ -update = new Update( - $databaseName, - $collectionName, - $filter, - $update, - ['multi' => true] + $options - ); - } - - /** - * 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) - { - return $this->update->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->update->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/UpdateOne.php b/cache/stores/mongodb/MongoDB/Operation/UpdateOne.php deleted file mode 100644 index a7bfab51827..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/UpdateOne.php +++ /dev/null @@ -1,128 +0,0 @@ -update = new Update( - $databaseName, - $collectionName, - $filter, - $update, - ['multi' => false] + $options - ); - } - - /** - * 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) - { - return $this->update->execute($server); - } - - /** - * Returns the command document for this operation. - * - * @see Explainable::getCommandDocument() - * @return array - */ - public function getCommandDocument(Server $server) - { - return $this->update->getCommandDocument($server); - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/Watch.php b/cache/stores/mongodb/MongoDB/Operation/Watch.php deleted file mode 100644 index 9306a5312a6..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/Watch.php +++ /dev/null @@ -1,478 +0,0 @@ - new ReadPreference(ReadPreference::RP_PRIMARY), - ]; - - if (array_key_exists('fullDocument', $options) && ! is_string($options['fullDocument'])) { - throw InvalidArgumentException::invalidType('"fullDocument" option', $options['fullDocument'], 'string'); - } - - if (isset($options['fullDocumentBeforeChange']) && ! is_string($options['fullDocumentBeforeChange'])) { - throw InvalidArgumentException::invalidType('"fullDocumentBeforeChange" option', $options['fullDocumentBeforeChange'], 'string'); - } - - if (! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); - } - - if (isset($options['resumeAfter']) && ! is_array($options['resumeAfter']) && ! is_object($options['resumeAfter'])) { - throw InvalidArgumentException::invalidType('"resumeAfter" option', $options['resumeAfter'], 'array or object'); - } - - if (isset($options['startAfter']) && ! is_array($options['startAfter']) && ! is_object($options['startAfter'])) { - throw InvalidArgumentException::invalidType('"startAfter" option', $options['startAfter'], 'array or object'); - } - - if (isset($options['startAtOperationTime']) && ! $options['startAtOperationTime'] instanceof TimestampInterface) { - throw InvalidArgumentException::invalidType('"startAtOperationTime" option', $options['startAtOperationTime'], TimestampInterface::class); - } - - if (isset($options['showExpandedEvents']) && ! is_bool($options['showExpandedEvents'])) { - throw InvalidArgumentException::invalidType('"showExpandedEvents" option', $options['showExpandedEvents'], 'bool'); - } - - /* In the absence of an explicit session, create one to ensure that the - * initial aggregation and any resume attempts can use the same session - * ("implicit from the user's perspective" per PHPLIB-342). Since this - * is filling in for an implicit session, we default "causalConsistency" - * to false. */ - if (! isset($options['session'])) { - try { - $options['session'] = $manager->startSession(['causalConsistency' => false]); - } catch (RuntimeException $e) { - /* We can ignore the exception, as libmongoc likely cannot - * create its own session and there is no risk of a mismatch. */ - } - } - - $this->aggregateOptions = array_intersect_key($options, ['batchSize' => 1, 'collation' => 1, 'comment' => 1, 'maxAwaitTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1, 'typeMap' => 1]); - $this->changeStreamOptions = array_intersect_key($options, ['fullDocument' => 1, 'fullDocumentBeforeChange' => 1, 'resumeAfter' => 1, 'showExpandedEvents' => 1, 'startAfter' => 1, 'startAtOperationTime' => 1]); - - // Null database name implies a cluster-wide change stream - if ($databaseName === null) { - $databaseName = 'admin'; - $this->changeStreamOptions['allChangesForCluster'] = true; - } - - $this->manager = $manager; - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->pipeline = $pipeline; - - $this->aggregate = $this->createAggregate(); - } - - /** @internal */ - final public function commandFailed(CommandFailedEvent $event): void - { - } - - /** @internal */ - final public function commandStarted(CommandStartedEvent $event): void - { - if ($event->getCommandName() !== 'aggregate') { - return; - } - - $this->firstBatchSize = 0; - $this->postBatchResumeToken = null; - } - - /** @internal */ - final public function commandSucceeded(CommandSucceededEvent $event): void - { - if ($event->getCommandName() !== 'aggregate') { - return; - } - - $reply = $event->getReply(); - - if (! isset($reply->cursor->firstBatch) || ! is_array($reply->cursor->firstBatch)) { - throw new UnexpectedValueException('aggregate command did not return a "cursor.firstBatch" array'); - } - - $this->firstBatchSize = count($reply->cursor->firstBatch); - - if (isset($reply->cursor->postBatchResumeToken) && is_object($reply->cursor->postBatchResumeToken)) { - $this->postBatchResumeToken = $reply->cursor->postBatchResumeToken; - } - - if ( - $this->shouldCaptureOperationTime($event->getServer()) && - isset($reply->operationTime) && $reply->operationTime instanceof TimestampInterface - ) { - $this->operationTime = $reply->operationTime; - } - } - - /** - * 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) - { - return new ChangeStream( - $this->createChangeStreamIterator($server), - function ($resumeToken, $hasAdvanced): ChangeStreamIterator { - return $this->resume($resumeToken, $hasAdvanced); - } - ); - } - - /** - * Create the aggregate command for a change stream. - * - * This method is also used to recreate the aggregate command when resuming. - */ - private function createAggregate(): Aggregate - { - $pipeline = $this->pipeline; - array_unshift($pipeline, ['$changeStream' => (object) $this->changeStreamOptions]); - - return new Aggregate($this->databaseName, $this->collectionName, $pipeline, $this->aggregateOptions); - } - - /** - * Create a ChangeStreamIterator by executing the aggregate command. - */ - private function createChangeStreamIterator(Server $server): ChangeStreamIterator - { - return new ChangeStreamIterator( - $this->executeAggregate($server), - $this->firstBatchSize, - $this->getInitialResumeToken(), - $this->postBatchResumeToken - ); - } - - /** - * Execute the aggregate command. - * - * The command will be executed using APM so that we can capture data from - * its response (e.g. firstBatch size, postBatchResumeToken). - */ - private function executeAggregate(Server $server): Cursor - { - addSubscriber($this); - - try { - $cursor = $this->aggregate->execute($server); - assert($cursor instanceof Cursor); - - return $cursor; - } finally { - removeSubscriber($this); - } - } - - /** - * 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() - { - if ($this->firstBatchSize === 0 && isset($this->postBatchResumeToken)) { - return $this->postBatchResumeToken; - } - - if (isset($this->changeStreamOptions['startAfter'])) { - return $this->changeStreamOptions['startAfter']; - } - - if (isset($this->changeStreamOptions['resumeAfter'])) { - return $this->changeStreamOptions['resumeAfter']; - } - - return null; - } - - /** - * Resumes a change stream. - * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resume-process - * @param array|object|null $resumeToken - * @throws InvalidArgumentException - */ - private function resume($resumeToken = null, bool $hasAdvanced = false): ChangeStreamIterator - { - if (isset($resumeToken) && ! is_array($resumeToken) && ! is_object($resumeToken)) { - throw InvalidArgumentException::invalidType('$resumeToken', $resumeToken, 'array or object'); - } - - $this->hasResumed = true; - - /* Select a new server using the original read preference. While watch - * is not usable within transactions, we still check if there is a - * pinned session. This is to avoid an ambiguous error message about - * running a command on the wrong server. */ - $server = select_server($this->manager, $this->aggregateOptions); - - $resumeOption = isset($this->changeStreamOptions['startAfter']) && ! $hasAdvanced ? 'startAfter' : 'resumeAfter'; - - unset($this->changeStreamOptions['resumeAfter']); - unset($this->changeStreamOptions['startAfter']); - unset($this->changeStreamOptions['startAtOperationTime']); - - if ($resumeToken !== null) { - $this->changeStreamOptions[$resumeOption] = $resumeToken; - } - - if ($resumeToken === null && $this->operationTime !== null) { - $this->changeStreamOptions['startAtOperationTime'] = $this->operationTime; - } - - // Recreate the aggregate command and return a new ChangeStreamIterator - $this->aggregate = $this->createAggregate(); - - return $this->createChangeStreamIterator($server); - } - - /** - * Determine whether to capture operation time from an aggregate response. - * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#startatoperationtime - */ - private function shouldCaptureOperationTime(Server $server): bool - { - if ($this->hasResumed) { - return false; - } - - if ( - isset($this->changeStreamOptions['resumeAfter']) || - isset($this->changeStreamOptions['startAfter']) || - isset($this->changeStreamOptions['startAtOperationTime']) - ) { - return false; - } - - if ($this->firstBatchSize > 0) { - return false; - } - - if ($this->postBatchResumeToken !== null) { - return false; - } - - if (! server_supports_feature($server, self::$wireVersionForStartAtOperationTime)) { - return false; - } - - return true; - } -} diff --git a/cache/stores/mongodb/MongoDB/Operation/WithTransaction.php b/cache/stores/mongodb/MongoDB/Operation/WithTransaction.php deleted file mode 100644 index e31b01b9ee5..00000000000 --- a/cache/stores/mongodb/MongoDB/Operation/WithTransaction.php +++ /dev/null @@ -1,130 +0,0 @@ -callback = $callback; - $this->transactionOptions = $transactionOptions; - } - - /** - * Execute the operation in the given session - * - * This helper takes care of retrying the commit operation or the entire - * transaction if an error occurs. - * - * If the commit fails because of an UnknownTransactionCommitResult error, the - * commit is retried without re-invoking the callback. - * If the commit fails because of a TransientTransactionError, the entire - * transaction will be retried. In this case, the callback will be invoked - * again. It is important that the logic inside the callback is idempotent. - * - * In case of failures, the commit or transaction are retried until 120 seconds - * from the initial call have elapsed. After that, no retries will happen and - * the helper will throw the last exception received from the driver. - * - * @see Client::startSession - * - * @param Session $session A session object as retrieved by Client::startSession - * @throws RuntimeException for driver errors while committing the transaction - * @throws Exception for any other errors, including those thrown in the callback - */ - public function execute(Session $session): void - { - $startTime = time(); - - while (true) { - $session->startTransaction($this->transactionOptions); - - try { - call_user_func($this->callback, $session); - } catch (Throwable $e) { - if ($session->isInTransaction()) { - $session->abortTransaction(); - } - - if ( - $e instanceof RuntimeException && - $e->hasErrorLabel('TransientTransactionError') && - ! $this->isTransactionTimeLimitExceeded($startTime) - ) { - continue; - } - - throw $e; - } - - if (! $session->isInTransaction()) { - // Assume callback intentionally ended the transaction - return; - } - - while (true) { - try { - $session->commitTransaction(); - } catch (RuntimeException $e) { - if ( - $e->getCode() !== 50 /* MaxTimeMSExpired */ && - $e->hasErrorLabel('UnknownTransactionCommitResult') && - ! $this->isTransactionTimeLimitExceeded($startTime) - ) { - // Retry committing the transaction - continue; - } - - if ( - $e->hasErrorLabel('TransientTransactionError') && - ! $this->isTransactionTimeLimitExceeded($startTime) - ) { - // Restart the transaction, invoking the callback again - continue 2; - } - - throw $e; - } - - // Commit was successful - break; - } - - // Transaction was successful - break; - } - } - - /** - * Returns whether the time limit for retrying transactions in the convenient transaction API has passed - * - * @param int $startTime The time the transaction was started - */ - private function isTransactionTimeLimitExceeded(int $startTime): bool - { - return time() - $startTime >= 120; - } -} diff --git a/cache/stores/mongodb/MongoDB/UpdateResult.php b/cache/stores/mongodb/MongoDB/UpdateResult.php deleted file mode 100644 index 1fbbab600fb..00000000000 --- a/cache/stores/mongodb/MongoDB/UpdateResult.php +++ /dev/null @@ -1,138 +0,0 @@ -writeResult = $writeResult; - $this->isAcknowledged = $writeResult->isAcknowledged(); - } - - /** - * Return the number of documents that were matched by the filter. - * - * This method should only be called if the write was acknowledged. - * - * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getMatchedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getMatchedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return the number of documents that were modified. - * - * This value is undefined (i.e. null) if the write executed as a legacy - * operation instead of command. - * - * This method should only be called if the write was acknowledged. - * - * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getModifiedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getModifiedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return the number of documents that were upserted. - * - * This method should only be called if the write was acknowledged. - * - * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getUpsertedCount() - { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return the ID of the document inserted by an upsert operation. - * - * If the document had an ID prior to upserting (i.e. the server did not - * need to generate an ID), this will contain its "_id". Any - * server-generated ID will be a MongoDB\BSON\ObjectId instance. - * - * This value is undefined (i.e. null) if an upsert did not take place. - * - * This method should only be called if the write was acknowledged. - * - * @see UpdateResult::isAcknowledged() - * @return mixed|null - * @throws BadMethodCallException is the write result is unacknowledged - */ - public function getUpsertedId() - { - if ($this->isAcknowledged) { - foreach ($this->writeResult->getUpsertedIds() as $id) { - return $id; - } - - return null; - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); - } - - /** - * Return whether this update was acknowledged by the server. - * - * 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() - { - return $this->isAcknowledged; - } -} diff --git a/cache/stores/mongodb/MongoDB/functions.php b/cache/stores/mongodb/MongoDB/functions.php deleted file mode 100644 index dd2d94021e5..00000000000 --- a/cache/stores/mongodb/MongoDB/functions.php +++ /dev/null @@ -1,569 +0,0 @@ -getType() === Server::TYPE_LOAD_BALANCER) { - continue; - } - - if (! server_supports_feature($server, $wireVersionForWriteStageOnSecondary)) { - return false; - } - } - - return true; -} - -/** - * Applies a type map to a document. - * - * This function is used by operations where it is not possible to apply a type - * map to the cursor directly because the root document is a command response - * (e.g. findAndModify). - * - * @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($document, array $typeMap) -{ - if (! is_array($document) && ! is_object($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); - } - - return toPHP(fromPHP($document), $typeMap); -} - -/** - * Generate an index name from a key specification. - * - * @internal - * @param array|object $document Document containing fields mapped to values, - * which denote order or an index type - * @throws InvalidArgumentException - */ -function generate_index_name($document): string -{ - if ($document instanceof Serializable) { - $document = $document->bsonSerialize(); - } - - if (is_object($document)) { - $document = get_object_vars($document); - } - - if (! is_array($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); - } - - $name = ''; - - foreach ($document as $field => $type) { - $name .= ($name != '' ? '_' : '') . $field . '_' . $type; - } - - return $name; -} - -/** - * Return a collection's encryptedFields from the encryptedFieldsMap - * autoEncryption driver option (if available). - * - * @internal - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#drop-collection-helper - * @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) -{ - $encryptedFieldsMap = (array) $manager->getEncryptedFieldsMap(); - - return $encryptedFieldsMap[$databaseName . '.' . $collectionName] ?? null; -} - -/** - * Return a collection's encryptedFields option from the server (if any). - * - * @internal - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#drop-collection-helper - * @see Collection::drop - * @see Database::dropCollection - * @return array|object|null - */ -function get_encrypted_fields_from_server(string $databaseName, string $collectionName, Manager $manager, Server $server) -{ - // No-op if the encryptedFieldsMap autoEncryption driver option was omitted - if ($manager->getEncryptedFieldsMap() === null) { - return null; - } - - $collectionInfoIterator = (new ListCollections($databaseName, ['filter' => ['name' => $collectionName]]))->execute($server); - - foreach ($collectionInfoIterator as $collectionInfo) { - /* Note: ListCollections applies a typeMap that converts BSON documents - * to PHP arrays. This should not be problematic as encryptedFields here - * is only used by drop helpers to obtain names of supporting encryption - * collections. */ - return $collectionInfo['options']['encryptedFields'] ?? null; - } - - return null; -} - -/** - * Return whether the first key in the document starts with a "$" character. - * - * This is used for differentiating update and replacement documents. - * - * @internal - * @param array|object $document Update or replacement document - * @throws InvalidArgumentException - */ -function is_first_key_operator($document): bool -{ - if ($document instanceof Serializable) { - $document = $document->bsonSerialize(); - } - - if (is_object($document)) { - $document = get_object_vars($document); - } - - if (! is_array($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); - } - - reset($document); - $firstKey = (string) key($document); - - return isset($firstKey[0]) && $firstKey[0] === '$'; -} - -/** - * Returns whether an update specification is a valid aggregation pipeline. - * - * @internal - * @param mixed $pipeline - */ -function is_pipeline($pipeline): bool -{ - if (! is_array($pipeline)) { - return false; - } - - if ($pipeline === []) { - return false; - } - - $expectedKey = 0; - - foreach ($pipeline as $key => $stage) { - if (! is_array($stage) && ! is_object($stage)) { - return false; - } - - if ($expectedKey !== $key) { - return false; - } - - $expectedKey++; - $stage = (array) $stage; - reset($stage); - $key = key($stage); - - if (! is_string($key) || substr($key, 0, 1) !== '$') { - return false; - } - } - - return true; -} - -/** - * Returns whether we are currently in a transaction. - * - * @internal - * @param array $options Command options - */ -function is_in_transaction(array $options): bool -{ - if (isset($options['session']) && $options['session'] instanceof Session && $options['session']->isInTransaction()) { - return true; - } - - return false; -} - -/** - * Return whether the aggregation pipeline ends with an $out or $merge operator. - * - * This is used for determining whether the aggregation pipeline must be - * executed against a primary server. - * - * @internal - * @param array $pipeline List of pipeline operations - */ -function is_last_pipeline_operator_write(array $pipeline): bool -{ - $lastOp = end($pipeline); - - if ($lastOp === false) { - return false; - } - - $lastOp = (array) $lastOp; - - return in_array(key($lastOp), ['$out', '$merge'], true); -} - -/** - * 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 - * @throws InvalidArgumentException - */ -function is_mapreduce_output_inline($out): bool -{ - if (! is_array($out) && ! is_object($out)) { - return false; - } - - if ($out instanceof Serializable) { - $out = $out->bsonSerialize(); - } - - if (is_object($out)) { - $out = get_object_vars($out); - } - - if (! is_array($out)) { - throw InvalidArgumentException::invalidType('$out', $out, 'array or object'); - } - - reset($out); - - return key($out) === 'inline'; -} - -/** - * Return whether the write concern is acknowledged. - * - * This function is similar to mongoc_write_concern_is_acknowledged but does not - * check the fsync option since that was never supported in the PHP driver. - * - * @internal - * @see https://mongodb.com/docs/manual/reference/write-concern/ - */ -function is_write_concern_acknowledged(WriteConcern $writeConcern): bool -{ - /* Note: -1 corresponds to MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED, which is - * deprecated synonym of MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED and slated - * for removal in libmongoc 2.0. */ - return ($writeConcern->getW() !== 0 && $writeConcern->getW() !== -1) || $writeConcern->getJournal() === true; -} - -/** - * Return whether the server supports a particular feature. - * - * @internal - * @param Server $server Server to check - * @param integer $feature Feature constant (i.e. wire protocol version) - */ -function server_supports_feature(Server $server, int $feature): bool -{ - $info = $server->getInfo(); - $maxWireVersion = isset($info['maxWireVersion']) ? (integer) $info['maxWireVersion'] : 0; - $minWireVersion = isset($info['minWireVersion']) ? (integer) $info['minWireVersion'] : 0; - - return $minWireVersion <= $feature && $maxWireVersion >= $feature; -} - -/** - * Return whether the input is an array of strings. - * - * @internal - * @param mixed $input - */ -function is_string_array($input): bool -{ - if (! is_array($input)) { - return false; - } - - foreach ($input as $item) { - if (! is_string($item)) { - return false; - } - } - - return true; -} - -/** - * Performs a deep copy of a value. - * - * This function will clone objects and recursively copy values within arrays. - * - * @internal - * @see https://bugs.php.net/bug.php?id=49664 - * @param mixed $element Value to be copied - * @return mixed - * @throws ReflectionException - */ -function recursive_copy($element) -{ - if (is_array($element)) { - foreach ($element as $key => $value) { - $element[$key] = recursive_copy($value); - } - - return $element; - } - - if (! is_object($element)) { - return $element; - } - - if (! (new ReflectionClass($element))->isCloneable()) { - return $element; - } - - return clone $element; -} - -/** - * Creates a type map to apply to a field type - * - * This is used in the Aggregate, Distinct, and FindAndModify operations to - * apply the root-level type map to the document that will be returned. It also - * replaces the root type with object for consistency within these operations - * - * An existing type map for the given field path will not be overwritten - * - * @internal - * @param array $typeMap The existing typeMap - * @param string $fieldPath The field path to apply the root type to - */ -function create_field_path_type_map(array $typeMap, string $fieldPath): array -{ - // If some field paths already exist, we prefix them with the field path we are assuming as the new root - if (isset($typeMap['fieldPaths']) && is_array($typeMap['fieldPaths'])) { - $fieldPaths = $typeMap['fieldPaths']; - - $typeMap['fieldPaths'] = []; - foreach ($fieldPaths as $existingFieldPath => $type) { - $typeMap['fieldPaths'][$fieldPath . '.' . $existingFieldPath] = $type; - } - } - - // If a root typemap was set, apply this to the field object - if (isset($typeMap['root'])) { - $typeMap['fieldPaths'][$fieldPath] = $typeMap['root']; - } - - /* Special case if we want to convert an array, in which case we need to - * ensure that the field containing the array is exposed as an array, - * instead of the type given in the type map's array key. */ - if (substr($fieldPath, -2, 2) === '.$') { - $typeMap['fieldPaths'][substr($fieldPath, 0, -2)] = 'array'; - } - - $typeMap['root'] = 'object'; - - return $typeMap; -} - -/** - * Execute a callback within a transaction in the given session - * - * This helper takes care of retrying the commit operation or the entire - * transaction if an error occurs. - * - * If the commit fails because of an UnknownTransactionCommitResult error, the - * commit is retried without re-invoking the callback. - * If the commit fails because of a TransientTransactionError, the entire - * transaction will be retried. In this case, the callback will be invoked - * again. It is important that the logic inside the callback is idempotent. - * - * In case of failures, the commit or transaction are retried until 120 seconds - * from the initial call have elapsed. After that, no retries will happen and - * the helper will throw the last exception received from the driver. - * - * @see Client::startSession - * @see Session::startTransaction for supported transaction options - * - * @param Session $session A session object as retrieved by Client::startSession - * @param callable $callback A callback that will be invoked within the transaction - * @param array $transactionOptions Additional options that are passed to Session::startTransaction - * @throws RuntimeException for driver errors while committing the transaction - * @throws Exception for any other errors, including those thrown in the callback - */ -function with_transaction(Session $session, callable $callback, array $transactionOptions = []): void -{ - $operation = new WithTransaction($callback, $transactionOptions); - $operation->execute($session); -} - -/** - * Returns the session option if it is set and valid. - * - * @internal - */ -function extract_session_from_options(array $options): ?Session -{ - if (! isset($options['session']) || ! $options['session'] instanceof Session) { - return null; - } - - return $options['session']; -} - -/** - * Returns the readPreference option if it is set and valid. - * - * @internal - */ -function extract_read_preference_from_options(array $options): ?ReadPreference -{ - if (! isset($options['readPreference']) || ! $options['readPreference'] instanceof ReadPreference) { - return null; - } - - return $options['readPreference']; -} - -/** - * Performs server selection, respecting the readPreference and session options - * (if given) - * - * @internal - */ -function select_server(Manager $manager, array $options): Server -{ - $session = extract_session_from_options($options); - $server = $session instanceof Session ? $session->getServer() : null; - if ($server !== null) { - return $server; - } - - $readPreference = extract_read_preference_from_options($options); - if (! $readPreference instanceof ReadPreference) { - // TODO: PHPLIB-476: Read transaction read preference once PHPC-1439 is implemented - $readPreference = new ReadPreference(ReadPreference::RP_PRIMARY); - } - - return $manager->selectServer($readPreference); -} - -/** - * Performs server selection for an aggregate operation with a write stage. The - * $options parameter may be modified by reference if a primary read preference - * must be forced due to the existence of pre-5.0 servers in the topology. - * - * @internal - * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#aggregation-pipelines-with-write-stages - */ -function select_server_for_aggregate_write_stage(Manager $manager, array &$options): Server -{ - $readPreference = extract_read_preference_from_options($options); - - /* If there is either no read preference or a primary read preference, there - * is no special server selection logic to apply. */ - if ($readPreference === null || $readPreference->getMode() === ReadPreference::RP_PRIMARY) { - return select_server($manager, $options); - } - - $server = null; - $serverSelectionError = null; - - try { - $server = select_server($manager, $options); - } catch (DriverRuntimeException $serverSelectionError) { - } - - /* If any pre-5.0 servers exist in the topology, force a primary read - * preference and repeat server selection if it previously failed or - * selected a secondary. */ - if (! all_servers_support_write_stage_on_secondary($manager->getServers())) { - $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); - - if ($server === null || $server->isSecondary()) { - return select_server($manager, $options); - } - } - - /* If the topology only contains 5.0+ servers, we should either return the - * previously selected server or propagate the server selection error. */ - if ($serverSelectionError !== null) { - throw $serverSelectionError; - } - - assert($server instanceof Server); - - return $server; -} diff --git a/cache/stores/mongodb/addinstanceform.php b/cache/stores/mongodb/addinstanceform.php deleted file mode 100644 index 467e1c91f21..00000000000 --- a/cache/stores/mongodb/addinstanceform.php +++ /dev/null @@ -1,101 +0,0 @@ -. - -/** - * The MongoDB plugin form for adding an instance. - * - * The following settings are provided: - * - server - * - username - * - password - * - database - * - replicaset - * - usesafe - * - extendedmode - * - * @package cachestore_mongodb - * @copyright 2012 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -// Include the necessary evils. -require_once($CFG->dirroot.'/cache/forms.php'); - -/** - * The form to add an instance of the MongoDB store to the system. - * - * @copyright 2012 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class cachestore_mongodb_addinstance_form extends cachestore_addinstance_form { - - /** - * The forms custom definitions. - */ - protected function configuration_definition() { - global $OUTPUT; - $form = $this->_form; - - if (!version_compare(phpversion('mongodb'), '1.5', 'ge')) { - $form->addElement('html', $OUTPUT->notification(get_string('pleaseupgrademongo', 'cachestore_mongodb'))); - } - - $form->addElement('text', 'server', get_string('server', 'cachestore_mongodb'), array('size' => 72)); - $form->addHelpButton('server', 'server', 'cachestore_mongodb'); - $form->addRule('server', get_string('required'), 'required'); - $form->setDefault('server', 'mongodb://127.0.0.1:27017'); - $form->setType('server', PARAM_RAW); - - $form->addElement('text', 'database', get_string('database', 'cachestore_mongodb')); - $form->addHelpButton('database', 'database', 'cachestore_mongodb'); - $form->addRule('database', get_string('required'), 'required'); - $form->setType('database', PARAM_ALPHANUMEXT); - $form->setDefault('database', 'mcache'); - - $form->addElement('text', 'username', get_string('username', 'cachestore_mongodb')); - $form->addHelpButton('username', 'username', 'cachestore_mongodb'); - $form->setType('username', PARAM_ALPHANUMEXT); - - $form->addElement('passwordunmask', 'password', get_string('password', 'cachestore_mongodb')); - $form->addHelpButton('password', 'password', 'cachestore_mongodb'); - $form->setType('password', PARAM_TEXT); - - $form->addElement('text', 'replicaset', get_string('replicaset', 'cachestore_mongodb')); - $form->addHelpButton('replicaset', 'replicaset', 'cachestore_mongodb'); - $form->setType('replicaset', PARAM_ALPHANUMEXT); - $form->setAdvanced('replicaset'); - - $form->addElement('checkbox', 'usesafe', get_string('usesafe', 'cachestore_mongodb')); - $form->addHelpButton('usesafe', 'usesafe', 'cachestore_mongodb'); - $form->setDefault('usesafe', 1); - $form->setAdvanced('usesafe'); - $form->setType('usesafe', PARAM_BOOL); - - $form->addElement('text', 'usesafevalue', get_string('usesafevalue', 'cachestore_mongodb')); - $form->addHelpButton('usesafevalue', 'usesafevalue', 'cachestore_mongodb'); - $form->disabledIf('usesafevalue', 'usesafe', 'notchecked'); - $form->setType('usesafevalue', PARAM_INT); - $form->setAdvanced('usesafevalue'); - - $form->addElement('checkbox', 'extendedmode', get_string('extendedmode', 'cachestore_mongodb')); - $form->addHelpButton('extendedmode', 'extendedmode', 'cachestore_mongodb'); - $form->setDefault('extendedmode', 0); - $form->setAdvanced('extendedmode'); - $form->setType('extendedmode', PARAM_BOOL); - } -} diff --git a/cache/stores/mongodb/classes/privacy/provider.php b/cache/stores/mongodb/classes/privacy/provider.php deleted file mode 100644 index c6fa487e1a3..00000000000 --- a/cache/stores/mongodb/classes/privacy/provider.php +++ /dev/null @@ -1,109 +0,0 @@ -. - -/** - * Privacy Subsystem implementation for cachestore_mongodb. - * - * @package cachestore_mongodb - * @category privacy - * @copyright 2018 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace cachestore_mongodb\privacy; - -use core_privacy\local\metadata\collection; -use core_privacy\local\request\approved_contextlist; -use core_privacy\local\request\approved_userlist; -use core_privacy\local\request\contextlist; -use core_privacy\local\request\userlist; - -defined('MOODLE_INTERNAL') || die(); - -/** - * Privacy Subsystem for cachestore_mongodb. - * - * @copyright 2018 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class provider implements - \core_privacy\local\metadata\provider, - \core_privacy\local\request\plugin\provider, - \core_privacy\local\request\core_userlist_provider { - - /** - * Returns meta data about this system. - * - * @param collection $collection The initialised collection to add items to. - * @return collection A listing of user data stored through this system. - */ - public static function get_metadata(collection $collection) : collection { - $collection->add_external_location_link('mongodb', [ - 'data' => 'privacy:metadata:mongodb:data', - ], 'privacy:metadata:mongodb'); - return $collection; - } - - /** - * Get the list of contexts that contain user information for the specified user. - * - * @param int $userid The user to search. - * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. - */ - public static function get_contexts_for_userid(int $userid) : contextlist { - return new contextlist(); - } - - /** - * Get the list of users who have data within a context. - * - * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. - */ - public static function get_users_in_context(userlist $userlist) { - } - - /** - * Export all user data for the specified user, in the specified contexts. - * - * @param approved_contextlist $contextlist The approved contexts to export information for. - */ - public static function export_user_data(approved_contextlist $contextlist) { - } - - /** - * Delete all use data which matches the specified deletion_criteria. - * - * @param \context $context A user context. - */ - public static function delete_data_for_all_users_in_context(\context $context) { - } - - /** - * Delete all user data for the specified user, in the specified contexts. - * - * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. - */ - public static function delete_data_for_user(approved_contextlist $contextlist) { - } - - /** - * Delete multiple users within a single context. - * - * @param approved_userlist $userlist The approved context and user information to delete information for. - */ - public static function delete_data_for_users(approved_userlist $userlist) { - } -} diff --git a/cache/stores/mongodb/lang/en/cachestore_mongodb.php b/cache/stores/mongodb/lang/en/cachestore_mongodb.php deleted file mode 100644 index 84343b928f4..00000000000 --- a/cache/stores/mongodb/lang/en/cachestore_mongodb.php +++ /dev/null @@ -1,47 +0,0 @@ -. - -/** - * The language strings for the MongoDB store plugin. - * - * @package cachestore_mongodb - * @copyright 2012 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -$string['database'] = 'Database'; -$string['database_help'] = 'The name of the database to make use of.'; -$string['extendedmode'] = 'Use extended keys'; -$string['extendedmode_help'] = 'If enabled full key sets will be used when working with the plugin. This isn\'t used internally yet but would allow you to easily search and investigate the MongoDB plugin manually if you so choose. Turning this on will add a small overhead so should only be done if you require it.'; -$string['password'] = 'Password'; -$string['password_help'] = 'The password of the user being used for the connection.'; -$string['pleaseupgrademongo'] = 'You are using an old version of the PHP Mongo extension (< 1.3). Support for old versions of the Mongo extension will be dropped in the future. Please consider upgrading.'; -$string['pluginname'] = 'MongoDB'; -$string['privacy:metadata:mongodb'] = 'The MongoDB cachestore plugin stores data briefly as part of its caching functionality. This data is stored on an MongoDB server where data is regularly removed.'; -$string['privacy:metadata:mongodb:data'] = 'The various data stored in the cache'; -$string['replicaset'] = 'Replica set'; -$string['replicaset_help'] = 'The name of the replica set to connect to. If this is given the master will be determined by using the ismaster database command on the seeds, so the driver may end up connecting to a server that was not even listed.'; -$string['server'] = 'Server'; -$string['server_help'] = 'This is the connection string for the server you want to use. Multiple servers can be specified using a comma-separated list.'; -$string['testserver'] = 'Test server'; -$string['testserver_desc'] = 'The connection string for a server to use for testing. If a test server has been specified then MongoDB performance can be tested using the cache performance page in the administration block. -As an example: mongodb://127.0.0.1:27017'; -$string['username'] = 'Username'; -$string['username_help'] = 'The username to use when making a connection.'; -$string['usesafe'] = 'Use safe'; -$string['usesafe_help'] = 'If enabled the usesafe option will be used during insert, get, and remove operations. If you\'ve specified a replica set this will be forced on anyway.'; -$string['usesafevalue'] = 'Use safe value'; -$string['usesafevalue_help'] = 'You can choose to provide a specific value for use safe. This will determine the number of servers that operations must be completed on before they are deemed to have been completed.'; diff --git a/cache/stores/mongodb/lib.php b/cache/stores/mongodb/lib.php deleted file mode 100644 index 99af8be6eb2..00000000000 --- a/cache/stores/mongodb/lib.php +++ /dev/null @@ -1,585 +0,0 @@ -. - -/** - * The library file for the MongoDB store plugin. - * - * This file is part of the MongoDB store plugin, it contains the API for interacting with an instance of the store. - * - * @package cachestore_mongodb - * @copyright 2012 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -require_once('MongoDB/functions.php'); - -/** - * The MongoDB Cache store. - * - * This cache store uses the MongoDB Native Driver and the MongoDB PHP Library. - * For installation instructions have a look at the following two links: - * - {@link http://php.net/manual/en/set.mongodb.php} - * - {@link https://docs.mongodb.com/ecosystem/drivers/php/} - * - * @copyright 2012 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class cachestore_mongodb extends cache_store implements cache_is_configurable { - - /** - * The name of the store - * @var string - */ - protected $name; - - /** - * The server connection string. Comma separated values. - * @var string - */ - protected $server = 'mongodb://127.0.0.1:27017'; - - /** - * The database connection options - * @var array - */ - protected $options = array(); - - /** - * The name of the database to use. - * @var string - */ - protected $databasename = 'mcache'; - - /** - * The Connection object - * @var MongoDB/Client - */ - protected $connection = false; - - /** - * The Database Object - * @var MongoDB/Database - */ - protected $database; - - /** - * The Collection object - * @var MongoDB/Collection - */ - protected $collection; - - /** - * Determines if and what safe setting is to be used. - * @var bool|int - */ - protected $usesafe = true; - - /** - * If set to true then multiple identifiers will be requested and used. - * @var bool - */ - protected $extendedmode = false; - - /** - * The definition has which is used in the construction of the collection. - * @var string - */ - protected $definitionhash = null; - - /** - * Set to true once this store is ready to be initialised and used. - * @var bool - */ - protected $isready = false; - - /** - * Constructs a new instance of the Mongo store. - * - * Noting that this function is not an initialisation. It is used to prepare the store for use. - * The store will be initialised when required and will be provided with a cache_definition at that time. - * - * @param string $name - * @param array $configuration - */ - public function __construct($name, array $configuration = array()) { - $this->name = $name; - - if (array_key_exists('server', $configuration)) { - $this->server = $configuration['server']; - } - - if (array_key_exists('replicaset', $configuration)) { - $this->options['replicaSet'] = (string)$configuration['replicaset']; - } - if (array_key_exists('username', $configuration) && !empty($configuration['username'])) { - $this->options['username'] = (string)$configuration['username']; - } - if (array_key_exists('password', $configuration) && !empty($configuration['password'])) { - $this->options['password'] = (string)$configuration['password']; - } - if (array_key_exists('database', $configuration)) { - $this->databasename = (string)$configuration['database']; - } - if (array_key_exists('usesafe', $configuration)) { - $this->usesafe = $configuration['usesafe']; - } - if (array_key_exists('extendedmode', $configuration)) { - $this->extendedmode = $configuration['extendedmode']; - } - - try { - $this->connection = new MongoDB\Client($this->server, $this->options); - // Required because MongoDB\Client does not try to connect to the server - $rp = new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY); - $this->connection->getManager()->selectServer($rp); - $this->isready = true; - } catch (MongoDB\Driver\Exception\RuntimeException $e) { - // We only want to catch RuntimeException here. - } - } - - /** - * Returns true if the requirements of this store have been met. - * @return bool - */ - public static function are_requirements_met() { - return version_compare(phpversion('mongodb'), '1.15', 'ge'); - } - - /** - * Returns the supported features. - * @param array $configuration - * @return int - */ - public static function get_supported_features(array $configuration = array()) { - $supports = self::SUPPORTS_DATA_GUARANTEE + self::DEREFERENCES_OBJECTS; - if (array_key_exists('extendedmode', $configuration) && $configuration['extendedmode']) { - $supports += self::SUPPORTS_MULTIPLE_IDENTIFIERS; - } - return $supports; - } - - /** - * Returns an int describing the supported modes. - * @param array $configuration - * @return int - */ - public static function get_supported_modes(array $configuration = array()) { - return self::MODE_APPLICATION; - } - - /** - * Initialises the store instance for use. - * - * Once this has been done the cache is all set to be used. - * - * @param cache_definition $definition - * @throws coding_exception - */ - public function initialise(cache_definition $definition) { - if ($this->is_initialised()) { - throw new coding_exception('This mongodb instance has already been initialised.'); - } - $this->database = $this->connection->selectDatabase($this->databasename); - $this->definitionhash = 'm'.$definition->generate_definition_hash(); - $this->collection = $this->database->selectCollection($this->definitionhash); - - $options = array('name' => 'idx_key'); - - $w = $this->usesafe ? 1 : 0; - $wc = new MongoDB\Driver\WriteConcern($w); - - $options['writeConcern'] = $wc; - - $this->collection->createIndex(array('key' => 1), $options); - } - - /** - * Returns true if this store instance has been initialised. - * @return bool - */ - public function is_initialised() { - return ($this->database instanceof MongoDB\Database); - } - - /** - * Returns true if this store instance is ready to use. - * @return bool - */ - public function is_ready() { - return $this->isready; - } - - /** - * Returns true if the given mode is supported by this store. - * @param int $mode - * @return bool - */ - public static function is_supported_mode($mode) { - return ($mode == self::MODE_APPLICATION || $mode == self::MODE_SESSION); - } - - /** - * Returns true if this store is making use of multiple identifiers. - * @return bool - */ - public function supports_multiple_identifiers() { - return $this->extendedmode; - } - - /** - * Retrieves an item from the cache store given its key. - * - * @param string $key The key to retrieve - * @return mixed The data that was associated with the key, or false if the key did not exist. - */ - public function get($key) { - if (!is_array($key)) { - $key = array('key' => $key); - } - - $result = $this->collection->findOne($key); - // Note $result is really an object, BSONDocument extending ArrayObject, - // which implements ArrayAccess. That enables access to its information - // using square brackets and some array operations. But, it seems that - // it's not enough for array_key_exists() to operate on it. Hence, we - // are explicitly casting to array, after having checked that the operation - // doesn't incur into any performance penalty. - if ($result === null || !array_key_exists('data', (array)$result)) { - return false; - } - $data = @unserialize($result['data']); - return $data; - } - - /** - * Retrieves several items from the cache store in a single transaction. - * - * If not all of the items are available in the cache then the data value for those that are missing will be set to false. - * - * @param array $keys The array of keys to retrieve - * @return array An array of items from the cache. - */ - public function get_many($keys) { - if ($this->extendedmode) { - $query = $this->get_many_extendedmode_query($keys); - $keyarray = array(); - foreach ($keys as $key) { - $keyarray[] = $key['key']; - } - $keys = $keyarray; - $query = array('key' => array('$in' => $keys)); - } else { - $query = array('key' => array('$in' => $keys)); - } - $cursor = $this->collection->find($query); - $results = array(); - foreach ($cursor as $result) { - $id = (string)$result['key']; - $results[$id] = unserialize($result['data']); - } - foreach ($keys as $key) { - if (!array_key_exists($key, $results)) { - $results[$key] = false; - } - } - return $results; - } - - /** - * Sets an item in the cache given its key and data value. - * - * @param string $key The key to use. - * @param mixed $data The data to set. - * @return bool True if the operation was a success false otherwise. - */ - public function set($key, $data) { - if (!is_array($key)) { - $record = array( - 'key' => $key - ); - } else { - $record = $key; - } - $record['data'] = serialize($data); - $options = array('upsert' => true); - - $w = $this->usesafe ? 1 : 0; - $wc = new MongoDB\Driver\WriteConcern($w); - - $options['writeConcern'] = $wc; - - $this->delete($key); - try { - $this->collection->insertOne($record, $options); - } catch (MongoDB\Exception\Exception $e) { - return false; - } - - return true; - } - - /** - * Sets many items in the cache in a single transaction. - * - * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two - * keys, 'key' and 'value'. - * @return int The number of items successfully set. It is up to the developer to check this matches the number of items - * sent ... if they care that is. - */ - public function set_many(array $keyvaluearray) { - $count = 0; - foreach ($keyvaluearray as $pair) { - $result = $this->set($pair['key'], $pair['value']); - if ($result === true) { - $count++; - } - } - return $count; - } - - /** - * Deletes an item from the cache store. - * - * @param string $key The key to delete. - * @return bool Returns true if the operation was a success, false otherwise. - */ - public function delete($key) { - if (!is_array($key)) { - $criteria = array( - 'key' => $key - ); - } else { - $criteria = $key; - } - $options = array('justOne' => false); - - $w = $this->usesafe ? 1 : 0; - $wc = new MongoDB\Driver\WriteConcern($w); - - $options['writeConcern'] = $wc; - - try { - $result = $this->collection->deleteOne($criteria, $options); - } catch (\MongoDB\Exception $e) { - return false; - } - - if (empty($result->getDeletedCount())) { - return false; - } - - return true; - } - - /** - * Deletes several keys from the cache in a single action. - * - * @param array $keys The keys to delete - * @return int The number of items successfully deleted. - */ - public function delete_many(array $keys) { - $count = 0; - foreach ($keys as $key) { - if ($this->delete($key)) { - $count++; - } - } - return $count; - } - - /** - * Purges the cache deleting all items within it. - * - * @return boolean True on success. False otherwise. - */ - public function purge() { - if ($this->isready) { - $this->collection->drop(); - $this->collection = $this->database->selectCollection($this->definitionhash); - } - - return true; - } - - /** - * Takes the object from the add instance store and creates a configuration array that can be used to initialise an instance. - * - * @param stdClass $data - * @return array - */ - public static function config_get_configuration_array($data) { - $return = array( - 'server' => $data->server, - 'database' => $data->database, - 'extendedmode' => (!empty($data->extendedmode)) - ); - if (!empty($data->username)) { - $return['username'] = $data->username; - } - if (!empty($data->password)) { - $return['password'] = $data->password; - } - if (!empty($data->replicaset)) { - $return['replicaset'] = $data->replicaset; - } - if (!empty($data->usesafe)) { - $return['usesafe'] = true; - if (!empty($data->usesafevalue)) { - $return['usesafe'] = (int)$data->usesafevalue; - $return['usesafevalue'] = $return['usesafe']; - } - } - return $return; - } - - /** - * Allows the cache store to set its data against the edit form before it is shown to the user. - * - * @param moodleform $editform - * @param array $config - */ - public static function config_set_edit_form_data(moodleform $editform, array $config) { - $data = array(); - if (!empty($config['server'])) { - $data['server'] = $config['server']; - } - if (!empty($config['database'])) { - $data['database'] = $config['database']; - } - if (isset($config['extendedmode'])) { - $data['extendedmode'] = (bool)$config['extendedmode']; - } - if (!empty($config['username'])) { - $data['username'] = $config['username']; - } - if (!empty($config['password'])) { - $data['password'] = $config['password']; - } - if (!empty($config['replicaset'])) { - $data['replicaset'] = $config['replicaset']; - } - if (isset($config['usesafevalue'])) { - $data['usesafe'] = true; - $data['usesafevalue'] = (int)$data['usesafe']; - } else if (isset($config['usesafe'])) { - $data['usesafe'] = (bool)$config['usesafe']; - } - $editform->set_data($data); - } - - /** - * Performs any necessary clean up when the store instance is being deleted. - */ - public function instance_deleted() { - // We can't use purge here that acts upon a collection. - // Instead we must drop the named database. - if (!$this->is_ready()) { - return; - } - $database = $this->connection->selectDatabase($this->databasename); - $database->drop(); - $connection = null; - $database = null; - // Explicitly unset things to cause a close. - $this->collection = null; - $this->database = null; - $this->connection = null; - } - - /** - * Generates an instance of the cache store that can be used for testing. - * - * @param cache_definition $definition - * @return false - */ - public static function initialise_test_instance(cache_definition $definition) { - if (!self::are_requirements_met()) { - return false; - } - - $config = get_config('cachestore_mongodb'); - if (empty($config->testserver)) { - return false; - } - $configuration = array(); - $configuration['server'] = $config->testserver; - if (!empty($config->testreplicaset)) { - $configuration['replicaset'] = $config->testreplicaset; - } - if (!empty($config->testusername)) { - $configuration['username'] = $config->testusername; - } - if (!empty($config->testpassword)) { - $configuration['password'] = $config->testpassword; - } - if (!empty($config->testdatabase)) { - $configuration['database'] = $config->testdatabase; - } - $configuration['usesafe'] = 1; - if (!empty($config->testextendedmode)) { - $configuration['extendedmode'] = (bool)$config->testextendedmode; - } - - $store = new cachestore_mongodb('Test mongodb', $configuration); - if (!$store->is_ready()) { - return false; - } - $store->initialise($definition); - - return $store; - } - - /** - * Generates an instance of the cache store that can be used for testing. - * - * @param cache_definition $definition - * @return false - */ - public static function unit_test_configuration() { - $configuration = array(); - $configuration['usesafe'] = 1; - - // If the configuration is not defined correctly, return only the configuration know about. - if (defined('TEST_CACHESTORE_MONGODB_TESTSERVER')) { - $configuration['server'] = TEST_CACHESTORE_MONGODB_TESTSERVER; - } - - return $configuration; - } - - /** - * Returns the name of this instance. - * @return string - */ - public function my_name() { - return $this->name; - } - - /** - * Returns true if this cache store instance is both suitable for testing, and ready for testing. - * - * Cache stores that support being used as the default store for unit and acceptance testing should - * override this function and return true if there requirements have been met. - * - * @return bool - */ - public static function ready_to_be_used_for_testing() { - return defined('TEST_CACHESTORE_MONGODB_TESTSERVER'); - } -} diff --git a/cache/stores/mongodb/readme_moodle.txt b/cache/stores/mongodb/readme_moodle.txt deleted file mode 100644 index 13285ee0700..00000000000 --- a/cache/stores/mongodb/readme_moodle.txt +++ /dev/null @@ -1,13 +0,0 @@ -MongoDB PHP ------------ -Download from https://github.com/mongodb/mongo-php-library/releases - -Import procedure: - -- Copy all the files and folders from the folder mongodb/src in the cache/stores/mongodb/MongoDB directory. -- Copy the license file from the project root. -- Update thirdpartylibs.xml with the latest version. -- Check the minim php driver version in https://docs.mongodb.com/drivers/php#compatibility and change the - value in the "are_requirements_met" method if necessary. - -This version (1.15.0) requires PHP mongodb extension >= 1.15.0 diff --git a/cache/stores/mongodb/settings.php b/cache/stores/mongodb/settings.php deleted file mode 100644 index 83da3aaec83..00000000000 --- a/cache/stores/mongodb/settings.php +++ /dev/null @@ -1,33 +0,0 @@ -. - -/** - * The settings for the mongodb store. - * - * This file is part of the mongodb cache store, it contains the API for interacting with an instance of the store. - * - * @package cachestore_mongodb - * @copyright 2012 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die; - -$settings->add(new admin_setting_configtextarea( - 'cachestore_mongodb/testserver', - new lang_string('testserver', 'cachestore_mongodb'), - new lang_string('testserver_desc', 'cachestore_mongodb'), - '', PARAM_RAW, 60, 3)); \ No newline at end of file diff --git a/cache/stores/mongodb/tests/store_test.php b/cache/stores/mongodb/tests/store_test.php deleted file mode 100644 index 1cccfd6290a..00000000000 --- a/cache/stores/mongodb/tests/store_test.php +++ /dev/null @@ -1,73 +0,0 @@ -. - -namespace cachestore_mongodb; - -use cache_store; -use cache_definition; -use cachestore_mongodb; - -defined('MOODLE_INTERNAL') || die(); - -// Include the necessary evils. -global $CFG; -require_once($CFG->dirroot.'/cache/tests/fixtures/stores.php'); -require_once($CFG->dirroot.'/cache/stores/mongodb/lib.php'); - -/** - * MongoDB unit test class. - * - * If you wish to use these unit tests all you need to do is add the following definition to - * your config.php file. - * - * define('TEST_CACHESTORE_MONGODB_TESTSERVER', 'mongodb://localhost:27017'); - * - * @package cachestore_mongodb - * @copyright 2013 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class store_test extends \cachestore_tests { - /** - * Returns the MongoDB class name - * @return string - */ - protected function get_class_name() { - return 'cachestore_mongodb'; - } - - /** - * A small additional test to make sure definitions that hash a hash starting with a number work OK - */ - public function test_collection_name() { - // This generates a definition that has a hash starting with a number. MDL-46208. - $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_mongodb', 'abc'); - $instance = new cachestore_mongodb('MongoDB_Test', cachestore_mongodb::unit_test_configuration()); - - if (!$instance->is_ready()) { - $this->markTestSkipped(); - } - $instance->initialise($definition); - - $this->assertTrue($instance->set(1, 'alpha')); - $this->assertTrue($instance->set(2, 'beta')); - $this->assertEquals('alpha', $instance->get(1)); - $this->assertEquals('beta', $instance->get(2)); - $this->assertEquals(array( - 1 => 'alpha', - 2 => 'beta' - ), $instance->get_many(array(1, 2))); - } -} diff --git a/cache/stores/mongodb/thirdpartylibs.xml b/cache/stores/mongodb/thirdpartylibs.xml deleted file mode 100644 index 3e4e8bedc42..00000000000 --- a/cache/stores/mongodb/thirdpartylibs.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - MongoDB - MongoDB PHP Library - This library provides a high-level abstraction around the lower-level PHP driver, also known as the mongodb extension. - 1.15.0 - Apache - 2.0 - https://github.com/mongodb/mongo-php-driver - - diff --git a/cache/stores/mongodb/version.php b/cache/stores/mongodb/version.php deleted file mode 100644 index e95ae73000b..00000000000 --- a/cache/stores/mongodb/version.php +++ /dev/null @@ -1,29 +0,0 @@ -. - -/** - * Cache mongodb store version information. - * - * @package cachestore_mongodb - * @copyright 2012 Sam Hemelryk - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die; - -$plugin->version = 2022121000; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2022111800; // Requires this Moodle version. -$plugin->component = 'cachestore_mongodb'; // Full name of the plugin. diff --git a/cache/upgrade.txt b/cache/upgrade.txt index 239132df5d8..3b0b10d3646 100644 --- a/cache/upgrade.txt +++ b/cache/upgrade.txt @@ -3,6 +3,9 @@ Information provided here is intended especially for developers. === 4.2 === * The memcached cachestore has been removed. +=== 4.2 === +* The mongodb cachestore has been removed. + === 4.1 === * Added new `requirelockingbeforewrite` option for cache definitions. This will check that a lock for a given cache key already exists before it will perform a `set()` on that key. A `coding_exception` is thrown if the lock has not been acquired. diff --git a/lib/classes/component.php b/lib/classes/component.php index d297a131c32..c608f99c059 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -105,7 +105,6 @@ class core_component { 'Phpml' => 'lib/mlbackend/php/phpml/src/Phpml', 'PHPMailer\\PHPMailer' => 'lib/phpmailer/src', 'RedeyeVentures\\GeoPattern' => 'lib/geopattern-php/GeoPattern', - 'MongoDB' => 'cache/stores/mongodb/MongoDB', 'Firebase\\JWT' => 'lib/php-jwt/src', 'ZipStream' => 'lib/zipstream/src/', 'MyCLabs\\Enum' => 'lib/php-enum/src', diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index 183e2ed6b72..446ff044c0a 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1729,7 +1729,7 @@ class core_plugin_manager { 'qformat' => array('blackboard', 'learnwise', 'examview'), 'auth' => array('radius', 'fc', 'nntp', 'pam', 'pop3', 'imap'), 'block' => array('course_overview', 'messages', 'community', 'participants', 'quiz_results'), - 'cachestore' => array('memcache', 'memcached'), + 'cachestore' => array('memcache', 'memcached', 'mongodb'), 'enrol' => array('authorize'), 'filter' => array('censor'), 'media' => array('swf'), @@ -1821,7 +1821,7 @@ class core_plugin_manager { ), 'cachestore' => array( - 'file', 'mongodb', 'session', 'static', 'apcu', 'redis' + 'file', 'session', 'static', 'apcu', 'redis' ), 'calendartype' => array( diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index ac3ff1962f1..f110c7f619b 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -3019,5 +3019,16 @@ privatefiles,moodle|/user/files.php'; upgrade_main_savepoint(true, 2023022000.00); } + if ($oldversion < 2023022400.01) { + // If cachestore_mongodb is no longer present, remove it. + if (!file_exists($CFG->dirroot . '/cache/stores/mongodb/version.php')) { + // Clean config. + unset_all_config_for_plugin('cachestore_mongodb'); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2023022400.01); + } + return true; } diff --git a/version.php b/version.php index fb683246b27..26c91ae57fc 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2023022400.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2023022400.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.2dev (Build: 20230224)'; // Human-friendly version name