MDL-77163 cachestore_mongodb: Remve from core
This commit is contained in:
Vendored
-3
@@ -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:
|
||||
|
||||
Vendored
-202
@@ -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.
|
||||
-188
@@ -1,188 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use MongoDB\Driver\WriteResult;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Result class for a bulk write operation.
|
||||
*/
|
||||
class BulkWriteResult
|
||||
{
|
||||
/** @var WriteResult */
|
||||
private $writeResult;
|
||||
|
||||
/** @var array */
|
||||
private $insertedIds;
|
||||
|
||||
/** @var boolean */
|
||||
private $isAcknowledged;
|
||||
|
||||
public function __construct(WriteResult $writeResult, array $insertedIds)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
-284
@@ -1,284 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2017-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use Iterator;
|
||||
use MongoDB\Driver\CursorId;
|
||||
use MongoDB\Driver\Exception\ConnectionException;
|
||||
use MongoDB\Driver\Exception\RuntimeException;
|
||||
use MongoDB\Driver\Exception\ServerException;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
use MongoDB\Exception\ResumeTokenException;
|
||||
use MongoDB\Model\ChangeStreamIterator;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function call_user_func;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* Iterator for a change stream.
|
||||
*
|
||||
* @psalm-type ResumeCallable = callable(array|object|null, bool): ChangeStreamIterator
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::watch()
|
||||
* @see https://mongodb.com/docs/manual/reference/method/db.watch/#mongodb-method-db.watch
|
||||
*/
|
||||
class ChangeStream implements Iterator
|
||||
{
|
||||
/**
|
||||
* @deprecated 1.4
|
||||
* @todo Remove this in 2.0 (see: PHPLIB-360)
|
||||
*/
|
||||
public const CURSOR_NOT_FOUND = 43;
|
||||
|
||||
/** @var int */
|
||||
private static $cursorNotFound = 43;
|
||||
|
||||
/** @var int[] */
|
||||
private static $resumableErrorCodes = [
|
||||
6, // HostUnreachable
|
||||
7, // HostNotFound
|
||||
89, // NetworkTimeout
|
||||
91, // ShutdownInProgress
|
||||
189, // PrimarySteppedDown
|
||||
262, // ExceededTimeLimit
|
||||
9001, // SocketException
|
||||
10107, // NotPrimary
|
||||
11600, // InterruptedAtShutdown
|
||||
11602, // InterruptedDueToReplStateChange
|
||||
13435, // NotPrimaryNoSecondaryOk
|
||||
13436, // NotPrimaryOrSecondary
|
||||
63, // StaleShardVersion
|
||||
150, // StaleEpoch
|
||||
13388, // StaleConfig
|
||||
234, // RetryChangeStream
|
||||
133, // FailedToSatisfyReadPreference
|
||||
];
|
||||
|
||||
/** @var int */
|
||||
private static $wireVersionForResumableChangeStreamError = 9;
|
||||
|
||||
/** @var ResumeCallable|null */
|
||||
private $resumeCallable;
|
||||
|
||||
/** @var ChangeStreamIterator */
|
||||
private $iterator;
|
||||
|
||||
/** @var integer */
|
||||
private $key = 0;
|
||||
|
||||
/**
|
||||
* Whether the change stream has advanced to its first result. This is used
|
||||
* to determine whether $key should be incremented after an iteration event.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $hasAdvanced = false;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @param ResumeCallable $resumeCallable
|
||||
*/
|
||||
public function __construct(ChangeStreamIterator $iterator, callable $resumeCallable)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
-425
@@ -1,425 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use Iterator;
|
||||
use Jean85\PrettyVersions;
|
||||
use MongoDB\Driver\ClientEncryption;
|
||||
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Manager;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\Model\BSONArray;
|
||||
use MongoDB\Model\BSONDocument;
|
||||
use MongoDB\Model\DatabaseInfoIterator;
|
||||
use MongoDB\Operation\DropDatabase;
|
||||
use MongoDB\Operation\ListDatabaseNames;
|
||||
use MongoDB\Operation\ListDatabases;
|
||||
use MongoDB\Operation\Watch;
|
||||
use Throwable;
|
||||
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
|
||||
class Client
|
||||
{
|
||||
public const DEFAULT_URI = 'mongodb://127.0.0.1/';
|
||||
|
||||
/** @var array */
|
||||
private static $defaultTypeMap = [
|
||||
'array' => 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;
|
||||
}
|
||||
}
|
||||
-1169
File diff suppressed because it is too large
Load Diff
@@ -1,154 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2020-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Command;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Model\CachingIterator;
|
||||
use MongoDB\Operation\Executable;
|
||||
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Wrapper for the listCollections command.
|
||||
*
|
||||
* @internal
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listCollections/
|
||||
*/
|
||||
class ListCollections implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a listCollections command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * authorizedCollections (boolean): Determines which collections are
|
||||
* returned based on the user privileges.
|
||||
*
|
||||
* For servers < 4.0, this option is ignored.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * filter (document): Query by which to filter collections.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * nameOnly (boolean): A flag to indicate whether the command should
|
||||
* return just the collection/view names and type or return both the name
|
||||
* and other information.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, array $options = [])
|
||||
{
|
||||
if (isset($options['authorizedCollections']) && ! is_bool($options['authorizedCollections'])) {
|
||||
throw InvalidArgumentException::invalidType('"authorizedCollections" option', $options['authorizedCollections'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['filter']) && ! is_array($options['filter']) && ! is_object($options['filter'])) {
|
||||
throw InvalidArgumentException::invalidType('"filter" option', $options['filter'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
|
||||
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['nameOnly']) && ! is_bool($options['nameOnly'])) {
|
||||
throw InvalidArgumentException::invalidType('"nameOnly" option', $options['nameOnly'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['session']) && ! $options['session'] instanceof Session) {
|
||||
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
|
||||
}
|
||||
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2020-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Command;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Operation\Executable;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Wrapper for the ListDatabases command.
|
||||
*
|
||||
* @internal
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listDatabases/
|
||||
*/
|
||||
class ListDatabases implements Executable
|
||||
{
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a listDatabases command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * authorizedDatabases (boolean): Determines which databases are returned
|
||||
* based on the user privileges.
|
||||
*
|
||||
* For servers < 4.0.5, this option is ignored.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * filter (document): Query by which to filter databases.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * nameOnly (boolean): A flag to indicate whether the command should
|
||||
* return just the database names, or return both database names and size
|
||||
* information.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (isset($options['authorizedDatabases']) && ! is_bool($options['authorizedDatabases'])) {
|
||||
throw InvalidArgumentException::invalidType('"authorizedDatabases" option', $options['authorizedDatabases'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['filter']) && ! is_array($options['filter']) && ! is_object($options['filter'])) {
|
||||
throw InvalidArgumentException::invalidType('"filter" option', $options['filter'], ['array', 'object']);
|
||||
}
|
||||
|
||||
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
|
||||
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['nameOnly']) && ! is_bool($options['nameOnly'])) {
|
||||
throw InvalidArgumentException::invalidType('"nameOnly" option', $options['nameOnly'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['session']) && ! $options['session'] instanceof Session) {
|
||||
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
|
||||
}
|
||||
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
-618
@@ -1,618 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use Iterator;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Manager;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\GridFS\Bucket;
|
||||
use MongoDB\Model\BSONArray;
|
||||
use MongoDB\Model\BSONDocument;
|
||||
use MongoDB\Model\CollectionInfoIterator;
|
||||
use MongoDB\Operation\Aggregate;
|
||||
use MongoDB\Operation\CreateCollection;
|
||||
use MongoDB\Operation\CreateIndexes;
|
||||
use MongoDB\Operation\DatabaseCommand;
|
||||
use MongoDB\Operation\DropCollection;
|
||||
use MongoDB\Operation\DropDatabase;
|
||||
use MongoDB\Operation\ListCollectionNames;
|
||||
use MongoDB\Operation\ListCollections;
|
||||
use MongoDB\Operation\ModifyCollection;
|
||||
use MongoDB\Operation\RenameCollection;
|
||||
use MongoDB\Operation\Watch;
|
||||
use Traversable;
|
||||
|
||||
use function is_array;
|
||||
use function strlen;
|
||||
|
||||
class Database
|
||||
{
|
||||
/** @var array */
|
||||
private static $defaultTypeMap = [
|
||||
'array' => 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);
|
||||
}
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use MongoDB\Driver\WriteResult;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Result class for a delete operation.
|
||||
*/
|
||||
class DeleteResult
|
||||
{
|
||||
/** @var WriteResult */
|
||||
private $writeResult;
|
||||
|
||||
/** @var boolean */
|
||||
private $isAcknowledged;
|
||||
|
||||
public function __construct(WriteResult $writeResult)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
use BadMethodCallException as BaseBadMethodCallException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class BadMethodCallException extends BaseBadMethodCallException implements Exception
|
||||
{
|
||||
/**
|
||||
* Thrown when a mutable method is invoked on an immutable object.
|
||||
*
|
||||
* @param string $class Class name
|
||||
* @return self
|
||||
*/
|
||||
public static function classIsImmutable(string $class)
|
||||
{
|
||||
return new static(sprintf('%s is immutable', $class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when accessing a result field on an unacknowledged write result.
|
||||
*
|
||||
* @param string $method Method name
|
||||
* @return self
|
||||
*/
|
||||
public static function unacknowledgedWriteResultAccess(string $method)
|
||||
{
|
||||
return new static(sprintf('%s should not be called for an unacknowledged write result', $method));
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
use MongoDB\Driver\Exception\Exception as DriverException;
|
||||
|
||||
interface Exception extends DriverException
|
||||
{
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
|
||||
|
||||
use function array_pop;
|
||||
use function count;
|
||||
use function get_debug_type;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function sprintf;
|
||||
|
||||
class InvalidArgumentException extends DriverInvalidArgumentException implements Exception
|
||||
{
|
||||
/**
|
||||
* Thrown when an argument or option has an invalid type.
|
||||
*
|
||||
* @param string $name Name of the argument or option
|
||||
* @param mixed $value Actual value (used to derive the type)
|
||||
* @param string|string[] $expectedType Expected type
|
||||
* @return self
|
||||
*/
|
||||
public static function invalidType(string $name, $value, $expectedType)
|
||||
{
|
||||
if (is_array($expectedType)) {
|
||||
switch (count($expectedType)) {
|
||||
case 1:
|
||||
$typeString = array_pop($expectedType);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$typeString = implode('" or "', $expectedType);
|
||||
break;
|
||||
|
||||
default:
|
||||
$lastType = array_pop($expectedType);
|
||||
$typeString = sprintf('%s", or "%s', implode('", "', $expectedType), $lastType);
|
||||
break;
|
||||
}
|
||||
|
||||
$expectedType = $typeString;
|
||||
}
|
||||
|
||||
return new static(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, get_debug_type($value)));
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
use function get_debug_type;
|
||||
use function sprintf;
|
||||
|
||||
class ResumeTokenException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Thrown when a resume token has an invalid type.
|
||||
*
|
||||
* @param mixed $value Actual value (used to derive the type)
|
||||
* @return self
|
||||
*/
|
||||
public static function invalidType($value)
|
||||
{
|
||||
return new static(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a resume token is not found in a change document.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function notFound()
|
||||
{
|
||||
return new static('Resume token not found in change document');
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
|
||||
class RuntimeException extends DriverRuntimeException implements Exception
|
||||
{
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
use MongoDB\Driver\Exception\UnexpectedValueException as DriverUnexpectedValueException;
|
||||
|
||||
class UnexpectedValueException extends DriverUnexpectedValueException implements Exception
|
||||
{
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
class UnsupportedException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Thrown when a command's allowDiskUse option is not supported by a server.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function allowDiskUseNotSupported()
|
||||
{
|
||||
return new static('The "allowDiskUse" option is not supported by the server executing this operation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when array filters are not supported by a server.
|
||||
*
|
||||
* @deprecated 1.12
|
||||
* @todo Remove this in 2.0 (see: PHPLIB-797)
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function arrayFiltersNotSupported()
|
||||
{
|
||||
return new static('Array filters are not supported by the server executing this operation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when collations are not supported by a server.
|
||||
*
|
||||
* @deprecated 1.12
|
||||
* @todo Remove this in 2.0 (see: PHPLIB-797)
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function collationNotSupported()
|
||||
{
|
||||
return new static('Collations are not supported by the server executing this operation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the commitQuorum option for createIndexes is not supported
|
||||
* by a server.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function commitQuorumNotSupported()
|
||||
{
|
||||
return new static('The "commitQuorum" option is not supported by the server executing this operation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when explain is not supported by a server.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function explainNotSupported()
|
||||
{
|
||||
return new static('Explain is not supported by the server executing this operation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a command's hint option is not supported by a server.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function hintNotSupported()
|
||||
{
|
||||
return new static('Hint is not supported by the server executing this operation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a command's readConcern option is not supported by a server.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function readConcernNotSupported()
|
||||
{
|
||||
return new static('Read concern is not supported by the server executing this command');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a readConcern is used with a read operation in a transaction.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function readConcernNotSupportedInTransaction()
|
||||
{
|
||||
return new static('The "readConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a command's writeConcern option is not supported by a server.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function writeConcernNotSupported()
|
||||
{
|
||||
return new static('Write concern is not supported by the server executing this command');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a writeConcern is used with a write operation in a transaction.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function writeConcernNotSupportedInTransaction()
|
||||
{
|
||||
return new static('The "writeConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.');
|
||||
}
|
||||
}
|
||||
-738
@@ -1,738 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\GridFS;
|
||||
|
||||
use MongoDB\Collection;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Manager;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\GridFS\Exception\CorruptFileException;
|
||||
use MongoDB\GridFS\Exception\FileNotFoundException;
|
||||
use MongoDB\GridFS\Exception\StreamException;
|
||||
use MongoDB\Model\BSONArray;
|
||||
use MongoDB\Model\BSONDocument;
|
||||
use MongoDB\Operation\Find;
|
||||
|
||||
use function array_intersect_key;
|
||||
use function assert;
|
||||
use function fopen;
|
||||
use function get_resource_type;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_resource;
|
||||
use function is_string;
|
||||
use function method_exists;
|
||||
use function MongoDB\apply_type_map_to_document;
|
||||
use function MongoDB\BSON\fromPHP;
|
||||
use function MongoDB\BSON\toJSON;
|
||||
use function property_exists;
|
||||
use function sprintf;
|
||||
use function stream_context_create;
|
||||
use function stream_copy_to_stream;
|
||||
use function stream_get_meta_data;
|
||||
use function stream_get_wrappers;
|
||||
use function urlencode;
|
||||
|
||||
/**
|
||||
* Bucket provides a public API for interacting with the GridFS files and chunks
|
||||
* collections.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class Bucket
|
||||
{
|
||||
/** @var string */
|
||||
private static $defaultBucketName = 'fs';
|
||||
|
||||
/** @var integer */
|
||||
private static $defaultChunkSizeBytes = 261120;
|
||||
|
||||
/** @var array */
|
||||
private static $defaultTypeMap = [
|
||||
'array' => 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);
|
||||
}
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\GridFS;
|
||||
|
||||
use ArrayIterator;
|
||||
use MongoDB\Collection;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Manager;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\UpdateResult;
|
||||
use MultipleIterator;
|
||||
|
||||
use function abs;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function is_numeric;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* CollectionWrapper abstracts the GridFS files and chunks collections.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CollectionWrapper
|
||||
{
|
||||
/** @var string */
|
||||
private $bucketName;
|
||||
|
||||
/** @var Collection */
|
||||
private $chunksCollection;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var boolean */
|
||||
private $checkedIndexes = false;
|
||||
|
||||
/** @var Collection */
|
||||
private $filesCollection;
|
||||
|
||||
/**
|
||||
* Constructs a GridFS collection wrapper.
|
||||
*
|
||||
* @see Collection::__construct() for supported options
|
||||
* @param Manager $manager Manager instance from the driver
|
||||
* @param string $databaseName Database name
|
||||
* @param string $bucketName Bucket name
|
||||
* @param array $collectionOptions Collection options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(Manager $manager, string $databaseName, string $bucketName, array $collectionOptions = [])
|
||||
{
|
||||
$this->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' => [],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\GridFS\Exception;
|
||||
|
||||
use MongoDB\Exception\RuntimeException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class CorruptFileException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Thrown when a chunk doesn't contain valid data.
|
||||
*/
|
||||
public static function invalidChunkData(int $chunkIndex): self
|
||||
{
|
||||
return new static(sprintf('Invalid data found for index "%d"', $chunkIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a chunk is not found for an expected index.
|
||||
*
|
||||
* @param integer $expectedIndex Expected index number
|
||||
* @return self
|
||||
*/
|
||||
public static function missingChunk(int $expectedIndex)
|
||||
{
|
||||
return new static(sprintf('Chunk not found for index "%d"', $expectedIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a chunk has an unexpected index number.
|
||||
*
|
||||
* @param integer $index Actual index number (i.e. "n" field)
|
||||
* @param integer $expectedIndex Expected index number
|
||||
* @return self
|
||||
*/
|
||||
public static function unexpectedIndex(int $index, int $expectedIndex)
|
||||
{
|
||||
return new static(sprintf('Expected chunk to have index "%d" but found "%d"', $expectedIndex, $index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a chunk has an unexpected data size.
|
||||
*
|
||||
* @param integer $size Actual size (i.e. "data" field length)
|
||||
* @param integer $expectedSize Expected size
|
||||
* @return self
|
||||
*/
|
||||
public static function unexpectedSize(int $size, int $expectedSize)
|
||||
{
|
||||
return new static(sprintf('Expected chunk to have size "%d" but found "%d"', $expectedSize, $size));
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\GridFS\Exception;
|
||||
|
||||
use MongoDB\Exception\RuntimeException;
|
||||
|
||||
use function MongoDB\BSON\fromPHP;
|
||||
use function MongoDB\BSON\toJSON;
|
||||
use function sprintf;
|
||||
|
||||
class FileNotFoundException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Thrown when a file cannot be found by its filename and revision.
|
||||
*
|
||||
* @param string $filename Filename
|
||||
* @param integer $revision Revision
|
||||
* @param string $namespace Namespace for the files collection
|
||||
* @return self
|
||||
*/
|
||||
public static function byFilenameAndRevision(string $filename, int $revision, string $namespace)
|
||||
{
|
||||
return new static(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a file cannot be found by its ID.
|
||||
*
|
||||
* @param mixed $id File ID
|
||||
* @param string $namespace Namespace for the files collection
|
||||
* @return self
|
||||
*/
|
||||
public static function byId($id, string $namespace)
|
||||
{
|
||||
$json = toJSON(fromPHP(['_id' => $id]));
|
||||
|
||||
return new static(sprintf('File "%s" not found in "%s"', $json, $namespace));
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MongoDB\GridFS\Exception;
|
||||
|
||||
use MongoDB\Exception\RuntimeException;
|
||||
|
||||
use function MongoDB\BSON\fromPHP;
|
||||
use function MongoDB\BSON\toJSON;
|
||||
use function sprintf;
|
||||
use function stream_get_meta_data;
|
||||
|
||||
class StreamException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* @param resource $source
|
||||
* @param resource $destination
|
||||
*/
|
||||
public static function downloadFromFilenameFailed(string $filename, $source, $destination): self
|
||||
{
|
||||
$sourceMetadata = stream_get_meta_data($source);
|
||||
$destinationMetadata = stream_get_meta_data($destination);
|
||||
|
||||
return new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @param resource $source
|
||||
* @param resource $destination
|
||||
*/
|
||||
public static function downloadFromIdFailed($id, $source, $destination): self
|
||||
{
|
||||
$idString = toJSON(fromPHP(['_id' => $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));
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\GridFS;
|
||||
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\GridFS\Exception\CorruptFileException;
|
||||
|
||||
use function assert;
|
||||
use function ceil;
|
||||
use function floor;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function property_exists;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* ReadableStream abstracts the process of reading a GridFS file.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ReadableStream
|
||||
{
|
||||
/** @var string|null */
|
||||
private $buffer;
|
||||
|
||||
/** @var integer */
|
||||
private $bufferOffset = 0;
|
||||
|
||||
/** @var integer */
|
||||
private $chunkSize;
|
||||
|
||||
/** @var integer */
|
||||
private $chunkOffset = 0;
|
||||
|
||||
/** @var Cursor|null */
|
||||
private $chunksIterator;
|
||||
|
||||
/** @var CollectionWrapper */
|
||||
private $collectionWrapper;
|
||||
|
||||
/** @var integer */
|
||||
private $expectedLastChunkSize = 0;
|
||||
|
||||
/** @var object */
|
||||
private $file;
|
||||
|
||||
/** @var integer */
|
||||
private $length;
|
||||
|
||||
/** @var integer */
|
||||
private $numChunks = 0;
|
||||
|
||||
/**
|
||||
* Constructs a readable GridFS stream.
|
||||
*
|
||||
* @param CollectionWrapper $collectionWrapper GridFS collection wrapper
|
||||
* @param object $file GridFS file document
|
||||
* @throws CorruptFileException
|
||||
*/
|
||||
public function __construct(CollectionWrapper $collectionWrapper, object $file)
|
||||
{
|
||||
if (! isset($file->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();
|
||||
}
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\GridFS;
|
||||
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
use function assert;
|
||||
use function explode;
|
||||
use function in_array;
|
||||
use function is_integer;
|
||||
use function is_resource;
|
||||
use function stream_context_get_options;
|
||||
use function stream_get_wrappers;
|
||||
use function stream_wrapper_register;
|
||||
use function stream_wrapper_unregister;
|
||||
|
||||
use const SEEK_CUR;
|
||||
use const SEEK_END;
|
||||
use const SEEK_SET;
|
||||
use const STREAM_IS_URL;
|
||||
|
||||
/**
|
||||
* Stream wrapper for reading and writing a GridFS file.
|
||||
*
|
||||
* @internal
|
||||
* @see Bucket::openUploadStream()
|
||||
* @see Bucket::openDownloadStream()
|
||||
*/
|
||||
class StreamWrapper
|
||||
{
|
||||
/** @var resource|null Stream context (set by PHP) */
|
||||
public $context;
|
||||
|
||||
/** @var string|null */
|
||||
private $mode;
|
||||
|
||||
/** @var string|null */
|
||||
private $protocol;
|
||||
|
||||
/** @var ReadableStream|WritableStream|null */
|
||||
private $stream;
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
/* This destructor is a workaround for PHP trying to use the stream well
|
||||
* after all objects have been destructed. This can cause autoloading
|
||||
* issues and possibly segmentation faults during PHP shutdown. */
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\GridFS;
|
||||
|
||||
use HashContext;
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\BSON\ObjectId;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
|
||||
use function array_intersect_key;
|
||||
use function hash_final;
|
||||
use function hash_init;
|
||||
use function hash_update;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\is_string_array;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* WritableStream abstracts the process of writing a GridFS file.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class WritableStream
|
||||
{
|
||||
/** @var integer */
|
||||
private static $defaultChunkSizeBytes = 261120;
|
||||
|
||||
/** @var string */
|
||||
private $buffer = '';
|
||||
|
||||
/** @var integer */
|
||||
private $chunkOffset = 0;
|
||||
|
||||
/** @var integer */
|
||||
private $chunkSize;
|
||||
|
||||
/** @var boolean */
|
||||
private $disableMD5;
|
||||
|
||||
/** @var CollectionWrapper */
|
||||
private $collectionWrapper;
|
||||
|
||||
/** @var array */
|
||||
private $file;
|
||||
|
||||
/** @var HashContext|null */
|
||||
private $hashCtx;
|
||||
|
||||
/** @var boolean */
|
||||
private $isClosed = false;
|
||||
|
||||
/** @var integer */
|
||||
private $length = 0;
|
||||
|
||||
/**
|
||||
* Constructs a writable GridFS stream.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * _id (mixed): File document identifier. Defaults to a new ObjectId.
|
||||
*
|
||||
* * aliases (array of strings): DEPRECATED An array of aliases.
|
||||
* Applications wishing to store aliases should add an aliases field to
|
||||
* the metadata document instead.
|
||||
*
|
||||
* * chunkSizeBytes (integer): The chunk size in bytes. Defaults to
|
||||
* 261120 (i.e. 255 KiB).
|
||||
*
|
||||
* * disableMD5 (boolean): When true, no MD5 sum will be generated.
|
||||
* Defaults to "false".
|
||||
*
|
||||
* * contentType (string): DEPRECATED content type to be stored with the
|
||||
* file. This information should now be added to the metadata.
|
||||
*
|
||||
* * metadata (document): User data for the "metadata" field of the files
|
||||
* collection document.
|
||||
*
|
||||
* @param CollectionWrapper $collectionWrapper GridFS collection wrapper
|
||||
* @param string $filename Filename
|
||||
* @param array $options Upload options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(CollectionWrapper $collectionWrapper, string $filename, array $options = [])
|
||||
{
|
||||
$options += [
|
||||
'_id' => 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++;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use MongoDB\Driver\WriteResult;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Result class for a multi-document insert operation.
|
||||
*/
|
||||
class InsertManyResult
|
||||
{
|
||||
/** @var WriteResult */
|
||||
private $writeResult;
|
||||
|
||||
/** @var array */
|
||||
private $insertedIds;
|
||||
|
||||
/** @var boolean */
|
||||
private $isAcknowledged;
|
||||
|
||||
public function __construct(WriteResult $writeResult, array $insertedIds)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use MongoDB\Driver\WriteResult;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Result class for a single-document insert operation.
|
||||
*/
|
||||
class InsertOneResult
|
||||
{
|
||||
/** @var WriteResult */
|
||||
private $writeResult;
|
||||
|
||||
/** @var mixed */
|
||||
private $insertedId;
|
||||
|
||||
/** @var boolean */
|
||||
private $isAcknowledged;
|
||||
|
||||
/**
|
||||
* @param mixed $insertedId
|
||||
*/
|
||||
public function __construct(WriteResult $writeResult, $insertedId)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2017-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use IteratorAggregate;
|
||||
use ReturnTypeWillChange;
|
||||
use stdClass;
|
||||
use Traversable;
|
||||
|
||||
use function call_user_func;
|
||||
|
||||
/**
|
||||
* Result class for mapReduce command results.
|
||||
*
|
||||
* This class allows for iteration of mapReduce results irrespective of the
|
||||
* output method (e.g. inline, collection) via the IteratorAggregate interface.
|
||||
* It also provides access to command statistics.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::mapReduce()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/mapReduce/
|
||||
*/
|
||||
class MapReduceResult implements IteratorAggregate
|
||||
{
|
||||
/** @var callable */
|
||||
private $getIterator;
|
||||
|
||||
/** @var integer */
|
||||
private $executionTimeMS;
|
||||
|
||||
/** @var array */
|
||||
private $counts;
|
||||
|
||||
/** @var array */
|
||||
private $timing;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param callable $getIterator Callback that returns a Traversable for mapReduce results
|
||||
* @param stdClass $result Result document from the mapReduce command
|
||||
*/
|
||||
public function __construct(callable $getIterator, stdClass $result)
|
||||
{
|
||||
$this->getIterator = $getIterator;
|
||||
$this->executionTimeMS = isset($result->timeMillis) ? (integer) $result->timeMillis : 0;
|
||||
$this->counts = isset($result->counts) ? (array) $result->counts : [];
|
||||
$this->timing = isset($result->timing) ? (array) $result->timing : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use ArrayObject;
|
||||
use JsonSerializable;
|
||||
use MongoDB\BSON\Serializable;
|
||||
use MongoDB\BSON\Unserializable;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function array_values;
|
||||
use function MongoDB\recursive_copy;
|
||||
|
||||
/**
|
||||
* Model class for a BSON array.
|
||||
*
|
||||
* The internal data will be filtered through array_values() during BSON
|
||||
* serialization to ensure that it becomes a BSON array.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class BSONArray extends ArrayObject implements JsonSerializable, Serializable, Unserializable
|
||||
{
|
||||
/**
|
||||
* Clone this BSONArray.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
foreach ($this as $key => $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());
|
||||
}
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2016-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use ArrayObject;
|
||||
use JsonSerializable;
|
||||
use MongoDB\BSON\Serializable;
|
||||
use MongoDB\BSON\Unserializable;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function MongoDB\recursive_copy;
|
||||
|
||||
/**
|
||||
* Model class for a BSON document.
|
||||
*
|
||||
* The internal data will be cast to an object during BSON serialization to
|
||||
* ensure that it becomes a BSON document.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class BSONDocument extends ArrayObject implements JsonSerializable, Serializable, Unserializable
|
||||
{
|
||||
/**
|
||||
* Deep clone this BSONDocument.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
foreach ($this as $key => $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();
|
||||
}
|
||||
}
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2018-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use Iterator;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function is_array;
|
||||
use function MongoDB\BSON\toPHP;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function unpack;
|
||||
|
||||
/**
|
||||
* Iterator for BSON documents.
|
||||
*/
|
||||
class BSONIterator implements Iterator
|
||||
{
|
||||
/** @var integer */
|
||||
private static $bsonSize = 4;
|
||||
|
||||
/** @var string */
|
||||
private $buffer;
|
||||
|
||||
/** @var integer */
|
||||
private $bufferLength;
|
||||
|
||||
/** @var mixed */
|
||||
private $current;
|
||||
|
||||
/** @var integer */
|
||||
private $key = 0;
|
||||
|
||||
/** @var integer */
|
||||
private $position = 0;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a BSON Iterator.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization.
|
||||
*
|
||||
* @internal
|
||||
* @see https://php.net/manual/en/function.mongodb.bson-tophp.php
|
||||
* @param string $data Concatenated, valid, BSON-encoded documents
|
||||
* @param array $options Iterator options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $data, array $options = [])
|
||||
{
|
||||
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
|
||||
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
|
||||
}
|
||||
|
||||
if (! isset($options['typeMap'])) {
|
||||
$options['typeMap'] = [];
|
||||
}
|
||||
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2017-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use Countable;
|
||||
use Iterator;
|
||||
use IteratorIterator;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
use function count;
|
||||
use function current;
|
||||
use function next;
|
||||
use function reset;
|
||||
|
||||
/**
|
||||
* Iterator for wrapping a Traversable and caching its results.
|
||||
*
|
||||
* By caching results, this iterators allows a Traversable to be counted and
|
||||
* rewound multiple times, even if the wrapped object does not natively support
|
||||
* those operations (e.g. MongoDB\Driver\Cursor).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CachingIterator implements Countable, Iterator
|
||||
{
|
||||
private const FIELD_KEY = 0;
|
||||
private const FIELD_VALUE = 1;
|
||||
|
||||
/** @var array */
|
||||
private $items = [];
|
||||
|
||||
/** @var Iterator */
|
||||
private $iterator;
|
||||
|
||||
/** @var boolean */
|
||||
private $iteratorAdvanced = false;
|
||||
|
||||
/** @var boolean */
|
||||
private $iteratorExhausted = false;
|
||||
|
||||
/**
|
||||
* Initialize the iterator and stores the first item in the cache. This
|
||||
* effectively rewinds the Traversable and the wrapping IteratorIterator.
|
||||
* Additionally, this mimics behavior of the SPL iterators and allows users
|
||||
* to omit an explicit call to rewind() before using the other methods.
|
||||
*
|
||||
* @param Traversable $traversable
|
||||
*/
|
||||
public function __construct(Traversable $traversable)
|
||||
{
|
||||
$this->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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2017-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use Closure;
|
||||
use Iterator;
|
||||
use IteratorIterator;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Iterator to apply a callback before returning an element
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CallbackIterator implements Iterator
|
||||
{
|
||||
/** @var Closure */
|
||||
private $callback;
|
||||
|
||||
/** @var Iterator */
|
||||
private $iterator;
|
||||
|
||||
public function __construct(Traversable $traversable, Closure $callback)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2019-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use IteratorIterator;
|
||||
use MongoDB\BSON\Serializable;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Monitoring\CommandFailedEvent;
|
||||
use MongoDB\Driver\Monitoring\CommandStartedEvent;
|
||||
use MongoDB\Driver\Monitoring\CommandSubscriber;
|
||||
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\ResumeTokenException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function assert;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function MongoDB\Driver\Monitoring\addSubscriber;
|
||||
use function MongoDB\Driver\Monitoring\removeSubscriber;
|
||||
|
||||
/**
|
||||
* ChangeStreamIterator wraps a change stream's tailable cursor.
|
||||
*
|
||||
* This iterator tracks the size of each batch in order to determine when the
|
||||
* postBatchResumeToken is applicable. It also ensures that initial calls to
|
||||
* rewind() do not execute getMore commands.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber
|
||||
{
|
||||
/** @var integer */
|
||||
private $batchPosition = 0;
|
||||
|
||||
/** @var integer */
|
||||
private $batchSize;
|
||||
|
||||
/** @var boolean */
|
||||
private $isRewindNop;
|
||||
|
||||
/** @var boolean */
|
||||
private $isValid = false;
|
||||
|
||||
/** @var object|null */
|
||||
private $postBatchResumeToken;
|
||||
|
||||
/** @var array|object|null */
|
||||
private $resumeToken;
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param array|object|null $initialResumeToken
|
||||
*/
|
||||
public function __construct(Cursor $cursor, int $firstBatchSize, $initialResumeToken, ?object $postBatchResumeToken)
|
||||
{
|
||||
if (isset($initialResumeToken) && ! is_array($initialResumeToken) && ! is_object($initialResumeToken)) {
|
||||
throw InvalidArgumentException::invalidType('$initialResumeToken', $initialResumeToken, 'array or object');
|
||||
}
|
||||
|
||||
parent::__construct($cursor);
|
||||
|
||||
$this->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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use ArrayAccess;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Collection information model class.
|
||||
*
|
||||
* This class models the collection information returned by the listCollections
|
||||
* command or, for legacy servers, queries on the "system.namespaces"
|
||||
* collection. It provides methods to access options for the collection.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Database::listCollections()
|
||||
* @see https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.rst
|
||||
*/
|
||||
class CollectionInfo implements ArrayAccess
|
||||
{
|
||||
/** @var array */
|
||||
private $info;
|
||||
|
||||
/**
|
||||
* @param array $info Collection info
|
||||
*/
|
||||
public function __construct(array $info)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use IteratorIterator;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* CollectionInfoIterator for listCollections command results.
|
||||
*
|
||||
* This iterator may be used to wrap a Cursor returned by the listCollections
|
||||
* command.
|
||||
*
|
||||
* @internal
|
||||
* @see \MongoDB\Database::listCollections()
|
||||
* @see https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.rst
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listCollections/
|
||||
*/
|
||||
class CollectionInfoCommandIterator extends IteratorIterator implements CollectionInfoIterator
|
||||
{
|
||||
/** @var string|null */
|
||||
private $databaseName;
|
||||
|
||||
public function __construct(Traversable $iterator, ?string $databaseName = null)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use Iterator;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* CollectionInfoIterator interface.
|
||||
*
|
||||
* This iterator is used for enumerating collections in a database.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Database::listCollections()
|
||||
*/
|
||||
interface CollectionInfoIterator extends Iterator
|
||||
{
|
||||
/**
|
||||
* Return the current element as a CollectionInfo instance.
|
||||
*
|
||||
* @return CollectionInfo
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function current();
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use ArrayAccess;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Database information model class.
|
||||
*
|
||||
* This class models the database information returned by the listDatabases
|
||||
* command. It provides methods to access common database properties.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Client::listDatabases()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listDatabases/
|
||||
*/
|
||||
class DatabaseInfo implements ArrayAccess
|
||||
{
|
||||
/** @var array */
|
||||
private $info;
|
||||
|
||||
/**
|
||||
* @param array $info Database info
|
||||
*/
|
||||
public function __construct(array $info)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use Iterator;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* DatabaseInfoIterator interface.
|
||||
*
|
||||
* This iterator is used for enumerating databases on a server.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Client::listDatabases()
|
||||
*/
|
||||
interface DatabaseInfoIterator extends Iterator
|
||||
{
|
||||
/**
|
||||
* Return the current element as a DatabaseInfo instance.
|
||||
*
|
||||
* @return DatabaseInfo
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function current();
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use function current;
|
||||
use function key;
|
||||
use function next;
|
||||
use function reset;
|
||||
|
||||
/**
|
||||
* DatabaseInfoIterator for inline listDatabases command results.
|
||||
*
|
||||
* This iterator may be used to wrap the array returned within the listDatabases
|
||||
* command's single-document result.
|
||||
*
|
||||
* @internal
|
||||
* @see \MongoDB\Client::listDatabases()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listDatabases/
|
||||
*/
|
||||
class DatabaseInfoLegacyIterator implements DatabaseInfoIterator
|
||||
{
|
||||
/** @var array */
|
||||
private $databases;
|
||||
|
||||
public function __construct(array $databases)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
-238
@@ -1,238 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use ArrayAccess;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_search;
|
||||
|
||||
/**
|
||||
* Index information model class.
|
||||
*
|
||||
* This class models the index information returned by the listIndexes command
|
||||
* or, for legacy servers, queries on the "system.indexes" collection. It
|
||||
* provides methods to access common index options, and allows access to other
|
||||
* options through the ArrayAccess interface (write methods are not supported).
|
||||
* For information on keys and index options, see the referenced
|
||||
* db.collection.createIndex() documentation.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::listIndexes()
|
||||
* @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst
|
||||
* @see https://mongodb.com/docs/manual/reference/method/db.collection.createIndex/
|
||||
*/
|
||||
class IndexInfo implements ArrayAccess
|
||||
{
|
||||
/** @var array */
|
||||
private $info;
|
||||
|
||||
/**
|
||||
* @param array $info Index info
|
||||
*/
|
||||
public function __construct(array $info)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use Iterator;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* IndexInfoIterator interface.
|
||||
*
|
||||
* This iterator is used for enumerating indexes in a collection.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::listIndexes()
|
||||
*/
|
||||
interface IndexInfoIterator extends Iterator
|
||||
{
|
||||
/**
|
||||
* Return the current element as a IndexInfo instance.
|
||||
*
|
||||
* @return IndexInfo
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function current();
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use IteratorIterator;
|
||||
use Traversable;
|
||||
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* IndexInfoIterator for both listIndexes command and legacy query results.
|
||||
*
|
||||
* This common iterator may be used to wrap a Cursor returned by both the
|
||||
* listIndexes command and, for legacy servers, queries on the "system.indexes"
|
||||
* collection.
|
||||
*
|
||||
* @internal
|
||||
* @see \MongoDB\Collection::listIndexes()
|
||||
* @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listIndexes/
|
||||
* @see https://mongodb.com/docs/manual/reference/system-collections/
|
||||
*/
|
||||
class IndexInfoIteratorIterator extends IteratorIterator implements IndexInfoIterator
|
||||
{
|
||||
/** @var string|null $ns */
|
||||
private $ns;
|
||||
|
||||
public function __construct(Traversable $iterator, ?string $ns = null)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use MongoDB\BSON\Serializable;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
|
||||
use function is_array;
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\generate_index_name;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Index input model class.
|
||||
*
|
||||
* This class is used to validate user input for index creation.
|
||||
*
|
||||
* @internal
|
||||
* @see \MongoDB\Collection::createIndexes()
|
||||
* @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst
|
||||
* @see https://mongodb.com/docs/manual/reference/method/db.collection.createIndex/
|
||||
*/
|
||||
class IndexInput implements Serializable
|
||||
{
|
||||
/** @var array */
|
||||
private $index;
|
||||
|
||||
/**
|
||||
* @param array $index Index specification
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $index)
|
||||
{
|
||||
if (! isset($index['key'])) {
|
||||
throw new InvalidArgumentException('Required "key" document is missing from index specification');
|
||||
}
|
||||
|
||||
if (! is_array($index['key']) && ! is_object($index['key'])) {
|
||||
throw InvalidArgumentException::invalidType('"key" option', $index['key'], 'array or object');
|
||||
}
|
||||
|
||||
foreach ($index['key'] as $fieldName => $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;
|
||||
}
|
||||
}
|
||||
@@ -1,396 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use ArrayIterator;
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use stdClass;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\create_field_path_type_map;
|
||||
use function MongoDB\is_last_pipeline_operator_write;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Operation for the aggregate command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::aggregate()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/aggregate/
|
||||
*/
|
||||
class Aggregate implements Executable, Explainable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string|null */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $pipeline;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/** @var bool */
|
||||
private $isExplain;
|
||||
|
||||
/** @var bool */
|
||||
private $isWrite;
|
||||
|
||||
/**
|
||||
* Constructs an aggregate command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * allowDiskUse (boolean): Enables writing to temporary files. When set
|
||||
* to true, aggregation stages can write data to the _tmp sub-directory
|
||||
* in the dbPath directory.
|
||||
*
|
||||
* * batchSize (integer): The number of documents to return per batch.
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation. This only applies when an $out
|
||||
* or $merge stage is specified.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* Only string values are supported for server versions < 4.4.
|
||||
*
|
||||
* * explain (boolean): Specifies whether or not to return the information
|
||||
* on the processing of the pipeline.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* * 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").
|
||||
*
|
||||
* This is not supported for server versions < 5.0 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* This option is ignored if an $out or $merge stage is specified.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be
|
||||
* applied to the returned Cursor (it is not sent to the server).
|
||||
*
|
||||
* * useCursor (boolean): Indicates whether the command will request that
|
||||
* the server provide results using a cursor. The default is true.
|
||||
*
|
||||
* This option allows users to turn off cursors if necessary to aid in
|
||||
* mongod/mongos upgrades.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern. This only
|
||||
* applies when an $out or $merge stage is specified.
|
||||
*
|
||||
* Note: Collection-agnostic commands (e.g. $currentOp) may be executed by
|
||||
* specifying null for the collection name.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string|null $collectionName Collection name
|
||||
* @param array $pipeline List of pipeline operations
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, ?string $collectionName, array $pipeline, array $options = [])
|
||||
{
|
||||
$expectedIndex = 0;
|
||||
|
||||
foreach ($pipeline as $i => $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);
|
||||
}
|
||||
}
|
||||
@@ -1,391 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\BulkWriteResult;
|
||||
use MongoDB\Driver\BulkWrite as Bulk;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
use function key;
|
||||
use function MongoDB\is_first_key_operator;
|
||||
use function MongoDB\is_pipeline;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Operation for executing multiple write operations.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::bulkWrite()
|
||||
*/
|
||||
class BulkWrite implements Executable
|
||||
{
|
||||
public const DELETE_MANY = 'deleteMany';
|
||||
public const DELETE_ONE = 'deleteOne';
|
||||
public const INSERT_ONE = 'insertOne';
|
||||
public const REPLACE_ONE = 'replaceOne';
|
||||
public const UPDATE_MANY = 'updateMany';
|
||||
public const UPDATE_ONE = 'updateOne';
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array[] */
|
||||
private $operations;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a bulk write operation.
|
||||
*
|
||||
* Example array structure for all supported operation types:
|
||||
*
|
||||
* [
|
||||
* [ 'deleteMany' => [ $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;
|
||||
}
|
||||
}
|
||||
-230
@@ -1,230 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_float;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* Operation for the count command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::count()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/count/
|
||||
*/
|
||||
class Count implements Executable, Explainable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array|object */
|
||||
private $filter;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a count command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* * limit (integer): The maximum number of documents to count.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * skip (integer): The number of documents to skip before returning the
|
||||
* documents.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array|object $filter Query by which to filter documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter = [], array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
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 or array or object');
|
||||
}
|
||||
|
||||
if (isset($options['limit']) && ! is_integer($options['limit'])) {
|
||||
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], '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['skip']) && ! is_integer($options['skip'])) {
|
||||
throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['readConcern']) && $options['readConcern']->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;
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function array_intersect_key;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_float;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Operation for obtaining an exact count of documents in a collection
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::countDocuments()
|
||||
* @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#countdocuments
|
||||
*/
|
||||
class CountDocuments implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array|object */
|
||||
private $filter;
|
||||
|
||||
/** @var array */
|
||||
private $aggregateOptions;
|
||||
|
||||
/** @var array */
|
||||
private $countOptions;
|
||||
|
||||
/** @var Aggregate */
|
||||
private $aggregate;
|
||||
|
||||
/**
|
||||
* Constructs an aggregate command for counting documents
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* Only string values are supported for server versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* * limit (integer): The maximum number of documents to count.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * skip (integer): The number of documents to skip before returning the
|
||||
* documents.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array|object $filter Query by which to filter documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['limit']) && ! is_integer($options['limit'])) {
|
||||
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['skip']) && ! is_integer($options['skip'])) {
|
||||
throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer');
|
||||
}
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Operation for the create command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Database::createCollection()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/create/
|
||||
*/
|
||||
class CreateCollection implements Executable
|
||||
{
|
||||
public const USE_POWER_OF_2_SIZES = 1;
|
||||
public const NO_PADDING = 2;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Constructs a create command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * autoIndexId (boolean): Specify false to disable the automatic creation
|
||||
* of an index on the _id field. For replica sets, this option cannot be
|
||||
* false. The default is true.
|
||||
*
|
||||
* This option has been deprecated since MongoDB 3.2. As of MongoDB 4.0,
|
||||
* this option cannot be false when creating a replicated collection
|
||||
* (i.e. a collection outside of the local database in any mongod mode).
|
||||
*
|
||||
* * capped (boolean): Specify true to create a capped collection. If set,
|
||||
* the size option must also be specified. The default is false.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * changeStreamPreAndPostImages (document): Used to configure support for
|
||||
* pre- and post-images in change streams.
|
||||
*
|
||||
* This is not supported for server versions < 6.0.
|
||||
*
|
||||
* * clusteredIndex (document): A clustered index specification.
|
||||
*
|
||||
* This is not supported for server versions < 5.3.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * encryptedFields (document): CSFLE specification.
|
||||
*
|
||||
* * expireAfterSeconds: The TTL for documents in time series collections.
|
||||
*
|
||||
* This is not supported for servers versions < 5.0.
|
||||
*
|
||||
* * flags (integer): Options for the MMAPv1 storage engine only. Must be a
|
||||
* bitwise combination CreateCollection::USE_POWER_OF_2_SIZES and
|
||||
* CreateCollection::NO_PADDING. The default is
|
||||
* CreateCollection::USE_POWER_OF_2_SIZES.
|
||||
*
|
||||
* * indexOptionDefaults (document): Default configuration for indexes when
|
||||
* creating the collection.
|
||||
*
|
||||
* * max (integer): The maximum number of documents allowed in the capped
|
||||
* collection. The size option takes precedence over this limit.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * pipeline (array): An array that consists of the aggregation pipeline
|
||||
* stage(s), which will be applied to the collection or view specified by
|
||||
* viewOn.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * size (integer): The maximum number of bytes for a capped collection.
|
||||
*
|
||||
* * storageEngine (document): Storage engine options.
|
||||
*
|
||||
* * timeseries (document): Options for time series collections.
|
||||
*
|
||||
* This is not supported for servers versions < 5.0.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will only be
|
||||
* used for the returned command result document.
|
||||
*
|
||||
* * validationAction (string): Validation action.
|
||||
*
|
||||
* * validationLevel (string): Validation level.
|
||||
*
|
||||
* * validator (document): Validation rules or expressions.
|
||||
*
|
||||
* * viewOn (string): The name of the source collection or view from which
|
||||
* to create the view.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @see https://source.wiredtiger.com/2.4.1/struct_w_t___s_e_s_s_i_o_n.html#a358ca4141d59c345f401c58501276bbb
|
||||
* @see https://mongodb.com/docs/manual/core/schema-validation/
|
||||
* @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 = [])
|
||||
{
|
||||
if (isset($options['autoIndexId']) && ! is_bool($options['autoIndexId'])) {
|
||||
throw InvalidArgumentException::invalidType('"autoIndexId" option', $options['autoIndexId'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['capped']) && ! is_bool($options['capped'])) {
|
||||
throw InvalidArgumentException::invalidType('"capped" option', $options['capped'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['changeStreamPreAndPostImages']) && ! is_array($options['changeStreamPreAndPostImages']) && ! is_object($options['changeStreamPreAndPostImages'])) {
|
||||
throw InvalidArgumentException::invalidType('"changeStreamPreAndPostImages" option', $options['changeStreamPreAndPostImages'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['clusteredIndex']) && ! is_array($options['clusteredIndex']) && ! is_object($options['clusteredIndex'])) {
|
||||
throw InvalidArgumentException::invalidType('"clusteredIndex" option', $options['clusteredIndex'], 'array or object');
|
||||
}
|
||||
|
||||
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['encryptedFields']) && ! is_array($options['encryptedFields']) && ! is_object($options['encryptedFields'])) {
|
||||
throw InvalidArgumentException::invalidType('"encryptedFields" option', $options['encryptedFields'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['expireAfterSeconds']) && ! is_integer($options['expireAfterSeconds'])) {
|
||||
throw InvalidArgumentException::invalidType('"expireAfterSeconds" option', $options['expireAfterSeconds'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['flags']) && ! is_integer($options['flags'])) {
|
||||
throw InvalidArgumentException::invalidType('"flags" option', $options['flags'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['indexOptionDefaults']) && ! is_array($options['indexOptionDefaults']) && ! is_object($options['indexOptionDefaults'])) {
|
||||
throw InvalidArgumentException::invalidType('"indexOptionDefaults" option', $options['indexOptionDefaults'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['max']) && ! is_integer($options['max'])) {
|
||||
throw InvalidArgumentException::invalidType('"max" option', $options['max'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
|
||||
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['pipeline']) && ! is_array($options['pipeline'])) {
|
||||
throw InvalidArgumentException::invalidType('"pipeline" option', $options['pipeline'], 'array');
|
||||
}
|
||||
|
||||
if (isset($options['session']) && ! $options['session'] instanceof Session) {
|
||||
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
|
||||
}
|
||||
|
||||
if (isset($options['size']) && ! is_integer($options['size'])) {
|
||||
throw InvalidArgumentException::invalidType('"size" option', $options['size'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['storageEngine']) && ! is_array($options['storageEngine']) && ! is_object($options['storageEngine'])) {
|
||||
throw InvalidArgumentException::invalidType('"storageEngine" option', $options['storageEngine'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['timeseries']) && ! is_array($options['timeseries']) && ! is_object($options['timeseries'])) {
|
||||
throw InvalidArgumentException::invalidType('"timeseries" option', $options['timeseries'], ['array', 'object']);
|
||||
}
|
||||
|
||||
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
|
||||
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
|
||||
}
|
||||
|
||||
if (isset($options['validationAction']) && ! is_string($options['validationAction'])) {
|
||||
throw InvalidArgumentException::invalidType('"validationAction" option', $options['validationAction'], 'string');
|
||||
}
|
||||
|
||||
if (isset($options['validationLevel']) && ! is_string($options['validationLevel'])) {
|
||||
throw InvalidArgumentException::invalidType('"validationLevel" option', $options['validationLevel'], 'string');
|
||||
}
|
||||
|
||||
if (isset($options['validator']) && ! is_array($options['validator']) && ! is_object($options['validator'])) {
|
||||
throw InvalidArgumentException::invalidType('"validator" option', $options['validator'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['viewOn']) && ! is_string($options['viewOn'])) {
|
||||
throw InvalidArgumentException::invalidType('"viewOn" option', $options['viewOn'], 'string');
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\Model\IndexInput;
|
||||
|
||||
use function array_map;
|
||||
use function is_array;
|
||||
use function is_integer;
|
||||
use function is_string;
|
||||
use function MongoDB\server_supports_feature;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Operation for the createIndexes command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::createIndex()
|
||||
* @see \MongoDB\Collection::createIndexes()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/createIndexes/
|
||||
*/
|
||||
class CreateIndexes implements Executable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $wireVersionForCommitQuorum = 9;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $indexes = [];
|
||||
|
||||
/** @var array */
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Constructs a createIndexes command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * commitQuorum (integer|string): Specifies how many data-bearing members
|
||||
* of a replica set, including the primary, must complete the index
|
||||
* builds successfully before the primary marks the indexes as ready.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array[] $indexes List of index specifications
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, array $indexes, array $options = [])
|
||||
{
|
||||
if (empty($indexes)) {
|
||||
throw new InvalidArgumentException('$indexes is empty');
|
||||
}
|
||||
|
||||
$expectedIndex = 0;
|
||||
|
||||
foreach ($indexes as $i => $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());
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Operation for executing a database command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Database::command()
|
||||
*/
|
||||
class DatabaseCommand implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var Command */
|
||||
private $command;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): The read preference to
|
||||
* use when executing the command. This may be used when issuing the
|
||||
* command to a replica set or mongos node to ensure that the driver sets
|
||||
* the wire protocol accordingly or adds the read preference to the
|
||||
* command document, respectively.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be
|
||||
* applied to the returned Cursor (it is not sent to the server).
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param array|object $command Command document
|
||||
* @param array $options Options for command execution
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, $command, array $options = [])
|
||||
{
|
||||
if (! is_array($command) && ! is_object($command)) {
|
||||
throw InvalidArgumentException::invalidType('$command', $command, 'array or object');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
-250
@@ -1,250 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\DeleteResult;
|
||||
use MongoDB\Driver\BulkWrite as Bulk;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\is_write_concern_acknowledged;
|
||||
use function MongoDB\server_supports_feature;
|
||||
|
||||
/**
|
||||
* Operation for the delete command.
|
||||
*
|
||||
* This class is used internally by the DeleteMany and DeleteOne operation
|
||||
* classes.
|
||||
*
|
||||
* @internal
|
||||
* @see https://mongodb.com/docs/manual/reference/command/delete/
|
||||
*/
|
||||
class Delete implements Executable, Explainable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $wireVersionForHint = 9;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array|object */
|
||||
private $filter;
|
||||
|
||||
/** @var integer */
|
||||
private $limit;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a delete command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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|object $filter Query by which to delete documents
|
||||
* @param integer $limit The number of matching documents to
|
||||
* delete. Must be 0 or 1, for all or a
|
||||
* single document, respectively.
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, int $limit, array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
if ($limit !== 0 && $limit !== 1) {
|
||||
throw new InvalidArgumentException('$limit must be 0 or 1');
|
||||
}
|
||||
|
||||
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 (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['writeConcern']) && $options['writeConcern']->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;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\DeleteResult;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
/**
|
||||
* Operation for deleting multiple document with the delete command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::deleteOne()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/delete/
|
||||
*/
|
||||
class DeleteMany implements Executable, Explainable
|
||||
{
|
||||
/** @var Delete */
|
||||
private $delete;
|
||||
|
||||
/**
|
||||
* Constructs a delete command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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|object $filter Query by which to delete documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\DeleteResult;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
/**
|
||||
* Operation for deleting a single document with the delete command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::deleteOne()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/delete/
|
||||
*/
|
||||
class DeleteOne implements Executable, Explainable
|
||||
{
|
||||
/** @var Delete */
|
||||
private $delete;
|
||||
|
||||
/**
|
||||
* Constructs a delete command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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|object $filter Query by which to delete documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
-222
@@ -1,222 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function MongoDB\create_field_path_type_map;
|
||||
|
||||
/**
|
||||
* Operation for the distinct command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::distinct()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/distinct/
|
||||
*/
|
||||
class Distinct implements Executable, Explainable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var string */
|
||||
private $fieldName;
|
||||
|
||||
/** @var array|object */
|
||||
private $filter;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a distinct command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @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
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, string $fieldName, $filter = [], array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
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['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 (isset($options['readConcern']) && $options['readConcern']->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;
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\CommandException;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Operation for the drop command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::drop()
|
||||
* @see \MongoDB\Database::dropCollection()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/drop/
|
||||
*/
|
||||
class DropCollection implements Executable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $errorCodeNamespaceNotFound = 26;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a drop command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be used
|
||||
* for the returned command result document.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @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 = [])
|
||||
{
|
||||
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 (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 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;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Operation for the dropDatabase command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Client::dropDatabase()
|
||||
* @see \MongoDB\Database::drop()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/dropDatabase/
|
||||
*/
|
||||
class DropDatabase implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a dropDatabase command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be used
|
||||
* for the returned command result document.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, array $options = [])
|
||||
{
|
||||
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 (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->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;
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_integer;
|
||||
|
||||
/**
|
||||
* Operation for the dropIndexes command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::dropIndexes()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/dropIndexes/
|
||||
*/
|
||||
class DropIndexes implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var string */
|
||||
private $indexName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a dropIndexes command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be used
|
||||
* for the returned command result document.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param string $indexName Index name (use "*" to drop all indexes)
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, string $indexName, array $options = [])
|
||||
{
|
||||
$indexName = $indexName;
|
||||
|
||||
if ($indexName === '') {
|
||||
throw new InvalidArgumentException('$indexName cannot be empty');
|
||||
}
|
||||
|
||||
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['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);
|
||||
}
|
||||
|
||||
if (isset($options['writeConcern']) && $options['writeConcern']->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;
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function array_intersect_key;
|
||||
use function is_integer;
|
||||
|
||||
/**
|
||||
* Operation for obtaining an estimated count of documents in a collection
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::estimatedDocumentCount()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/count/
|
||||
*/
|
||||
class EstimatedDocumentCount implements Executable, Explainable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/** @var int */
|
||||
private static $errorCodeCollectionNotFound = 26;
|
||||
|
||||
/** @var int */
|
||||
private static $wireVersionForCollStats = 12;
|
||||
|
||||
/**
|
||||
* Constructs a command to get the estimated number of documents in a
|
||||
* collection.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @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 = [])
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Server;
|
||||
|
||||
/**
|
||||
* Executable interface for operation classes.
|
||||
*
|
||||
* This interface is reserved for internal use until PHPC-378 is implemented,
|
||||
* since execute() should ultimately be changed to use ServerInterface.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface Executable
|
||||
{
|
||||
/**
|
||||
* Execute the operation.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute(Server $server);
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2018-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function MongoDB\server_supports_feature;
|
||||
|
||||
/**
|
||||
* Operation for the explain command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::explain()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/explain/
|
||||
*/
|
||||
class Explain implements Executable
|
||||
{
|
||||
public const VERBOSITY_ALL_PLANS = 'allPlansExecution';
|
||||
public const VERBOSITY_EXEC_STATS = 'executionStats';
|
||||
public const VERBOSITY_QUERY = 'queryPlanner';
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForAggregate = 7;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var Explainable */
|
||||
private $explainable;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs an explain command for explainable operations.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be used
|
||||
* used for the returned command result document.
|
||||
*
|
||||
* * verbosity (string): The mode in which the explain command will be run.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param Explainable $explainable Operation to explain
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, Explainable $explainable, array $options = [])
|
||||
{
|
||||
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 (isset($options['verbosity']) && ! is_string($options['verbosity'])) {
|
||||
throw InvalidArgumentException::invalidType('"verbosity" option', $options['verbosity'], 'string');
|
||||
}
|
||||
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2018-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Server;
|
||||
|
||||
/**
|
||||
* Explainable interface for explainable operations (aggregate, count, distinct,
|
||||
* find, findAndModify, delete, and update).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface Explainable extends Executable
|
||||
{
|
||||
/**
|
||||
* Returns the command document for this operation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCommandDocument(Server $server);
|
||||
}
|
||||
-438
@@ -1,438 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Query;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Operation for the find command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::find()
|
||||
* @see https://mongodb.com/docs/manual/tutorial/query-documents/
|
||||
* @see https://mongodb.com/docs/manual/reference/operator/query-modifier/
|
||||
*/
|
||||
class Find implements Executable, Explainable
|
||||
{
|
||||
public const NON_TAILABLE = 1;
|
||||
public const TAILABLE = 2;
|
||||
public const TAILABLE_AWAIT = 3;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array|object */
|
||||
private $filter;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a find command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * allowDiskUse (boolean): Enables writing to temporary files. When set
|
||||
* to true, queries can write data to the _tmp sub-directory in the
|
||||
* dbPath directory.
|
||||
*
|
||||
* * allowPartialResults (boolean): Get partial results from a mongos if
|
||||
* some shards are inaccessible (instead of throwing an error).
|
||||
*
|
||||
* * batchSize (integer): The number of documents to return per batch.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* Only string values are supported for server versions < 4.4.
|
||||
*
|
||||
* * cursorType (enum): Indicates the type of cursor to use. Must be either
|
||||
* NON_TAILABLE, TAILABLE, or TAILABLE_AWAIT. The default is
|
||||
* NON_TAILABLE.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* * limit (integer): The maximum number of documents to return.
|
||||
*
|
||||
* * max (document): The exclusive upper bound for a specific index.
|
||||
*
|
||||
* * maxAwaitTimeMS (integer): The maxium amount of time for the server to wait
|
||||
* on new documents to satisfy a query, if cursorType is TAILABLE_AWAIT.
|
||||
*
|
||||
* * maxScan (integer): Maximum number of documents or index keys to scan
|
||||
* when executing the query.
|
||||
*
|
||||
* This option has been deprecated since version 1.4.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run. If "$maxTimeMS" also exists in the modifiers document, this
|
||||
* option will take precedence.
|
||||
*
|
||||
* * min (document): The inclusive upper bound for a specific index.
|
||||
*
|
||||
* * modifiers (document): Meta operators that modify the output or
|
||||
* behavior of a query. Use of these operators is deprecated in favor of
|
||||
* named options.
|
||||
*
|
||||
* * noCursorTimeout (boolean): The server normally times out idle cursors
|
||||
* after an inactivity period (10 minutes) to prevent excess memory use.
|
||||
* Set this option to prevent that.
|
||||
*
|
||||
* * oplogReplay (boolean): Internal replication use only. The driver
|
||||
* should not set this. This option is deprecated as of MongoDB 4.4.
|
||||
*
|
||||
* * projection (document): Limits the fields to return for the matching
|
||||
* document.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* * returnKey (boolean): If true, returns only the index keys in the
|
||||
* resulting documents.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * showRecordId (boolean): Determines whether to return the record
|
||||
* identifier for each document. If true, adds a field $recordId to the
|
||||
* returned documents.
|
||||
*
|
||||
* * skip (integer): The number of documents to skip before returning.
|
||||
*
|
||||
* * snapshot (boolean): Prevents the cursor from returning a document more
|
||||
* than once because of an intervening write operation.
|
||||
*
|
||||
* This options has been deprecated since version 1.4.
|
||||
*
|
||||
* * sort (document): The order in which to return matching documents. If
|
||||
* "$orderby" also exists in the modifiers document, this option will
|
||||
* take precedence.
|
||||
*
|
||||
* * 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").
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be
|
||||
* applied to the returned Cursor (it is not sent to the server).
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array|object $filter Query by which to filter documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) {
|
||||
throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['allowPartialResults']) && ! is_bool($options['allowPartialResults'])) {
|
||||
throw InvalidArgumentException::invalidType('"allowPartialResults" option', $options['allowPartialResults'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['batchSize']) && ! is_integer($options['batchSize'])) {
|
||||
throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer');
|
||||
}
|
||||
|
||||
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['cursorType'])) {
|
||||
if (! is_integer($options['cursorType'])) {
|
||||
throw InvalidArgumentException::invalidType('"cursorType" option', $options['cursorType'], 'integer');
|
||||
}
|
||||
|
||||
if (
|
||||
$options['cursorType'] !== self::NON_TAILABLE &&
|
||||
$options['cursorType'] !== self::TAILABLE &&
|
||||
$options['cursorType'] !== self::TAILABLE_AWAIT
|
||||
) {
|
||||
throw new InvalidArgumentException('Invalid value for "cursorType" option: ' . $options['cursorType']);
|
||||
}
|
||||
}
|
||||
|
||||
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['limit']) && ! is_integer($options['limit'])) {
|
||||
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['max']) && ! is_array($options['max']) && ! is_object($options['max'])) {
|
||||
throw InvalidArgumentException::invalidType('"max" option', $options['max'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) {
|
||||
throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['maxScan']) && ! is_integer($options['maxScan'])) {
|
||||
throw InvalidArgumentException::invalidType('"maxScan" option', $options['maxScan'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
|
||||
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['min']) && ! is_array($options['min']) && ! is_object($options['min'])) {
|
||||
throw InvalidArgumentException::invalidType('"min" option', $options['min'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['modifiers']) && ! is_array($options['modifiers']) && ! is_object($options['modifiers'])) {
|
||||
throw InvalidArgumentException::invalidType('"modifiers" option', $options['modifiers'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['noCursorTimeout']) && ! is_bool($options['noCursorTimeout'])) {
|
||||
throw InvalidArgumentException::invalidType('"noCursorTimeout" option', $options['noCursorTimeout'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['oplogReplay']) && ! is_bool($options['oplogReplay'])) {
|
||||
throw InvalidArgumentException::invalidType('"oplogReplay" option', $options['oplogReplay'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) {
|
||||
throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object');
|
||||
}
|
||||
|
||||
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['returnKey']) && ! is_bool($options['returnKey'])) {
|
||||
throw InvalidArgumentException::invalidType('"returnKey" option', $options['returnKey'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['session']) && ! $options['session'] instanceof Session) {
|
||||
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
|
||||
}
|
||||
|
||||
if (isset($options['showRecordId']) && ! is_bool($options['showRecordId'])) {
|
||||
throw InvalidArgumentException::invalidType('"showRecordId" option', $options['showRecordId'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['skip']) && ! is_integer($options['skip'])) {
|
||||
throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['snapshot']) && ! is_bool($options['snapshot'])) {
|
||||
throw InvalidArgumentException::invalidType('"snapshot" option', $options['snapshot'], 'boolean');
|
||||
}
|
||||
|
||||
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['let']) && ! is_array($options['let']) && ! is_object($options['let'])) {
|
||||
throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['readConcern']) && $options['readConcern']->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;
|
||||
}
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function array_key_exists;
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\create_field_path_type_map;
|
||||
use function MongoDB\is_pipeline;
|
||||
use function MongoDB\is_write_concern_acknowledged;
|
||||
use function MongoDB\server_supports_feature;
|
||||
|
||||
/**
|
||||
* Operation for the findAndModify command.
|
||||
*
|
||||
* This class is used internally by the FindOneAndDelete, FindOneAndReplace, and
|
||||
* FindOneAndUpdate operation classes.
|
||||
*
|
||||
* @internal
|
||||
* @see https://mongodb.com/docs/manual/reference/command/findAndModify/
|
||||
*/
|
||||
class FindAndModify implements Executable, Explainable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $wireVersionForHint = 9;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForUnsupportedOptionServerSideError = 8;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a findAndModify command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * arrayFilters (document array): A set of filters specifying to which
|
||||
* array elements an update should apply.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * fields (document): Limits the fields to return for the matching
|
||||
* document.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is only supported on server versions >= 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;
|
||||
}
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function current;
|
||||
|
||||
/**
|
||||
* Operation for finding a single document with the find command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::findOne()
|
||||
* @see https://mongodb.com/docs/manual/tutorial/query-documents/
|
||||
* @see https://mongodb.com/docs/manual/reference/operator/query-modifier/
|
||||
*/
|
||||
class FindOne implements Executable, Explainable
|
||||
{
|
||||
/** @var Find */
|
||||
private $find;
|
||||
|
||||
/**
|
||||
* Constructs a find command for finding a single document.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* Only string values are supported for server versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* * max (document): The exclusive upper bound for a specific index.
|
||||
*
|
||||
* * maxScan (integer): Maximum number of documents or index keys to scan
|
||||
* when executing the query.
|
||||
*
|
||||
* This option has been deprecated since version 1.4.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run. If "$maxTimeMS" also exists in the modifiers document, this
|
||||
* option will take precedence.
|
||||
*
|
||||
* * min (document): The inclusive upper bound for a specific index.
|
||||
*
|
||||
* * modifiers (document): Meta-operators modifying the output or behavior
|
||||
* of a query.
|
||||
*
|
||||
* * projection (document): Limits the fields to return for the matching
|
||||
* document.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* * returnKey (boolean): If true, returns only the index keys in the
|
||||
* resulting documents.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * showRecordId (boolean): Determines whether to return the record
|
||||
* identifier for each document. If true, adds a field $recordId to the
|
||||
* returned documents.
|
||||
*
|
||||
* * skip (integer): The number of documents to skip before returning.
|
||||
*
|
||||
* * sort (document): The order in which to return matching documents. If
|
||||
* "$orderby" also exists in the modifiers document, this option will
|
||||
* take precedence.
|
||||
*
|
||||
* * 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").
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array|object $filter Query by which to filter documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Operation for deleting a document with the findAndModify command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::findOneAndDelete()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/findAndModify/
|
||||
*/
|
||||
class FindOneAndDelete implements Executable, Explainable
|
||||
{
|
||||
/** @var FindAndModify */
|
||||
private $findAndModify;
|
||||
|
||||
/**
|
||||
* Constructs a findAndModify command for deleting a document.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * projection (document): Limits the fields to return for the matching
|
||||
* document.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * sort (document): Determines which document the operation modifies if
|
||||
* the query selects multiple documents.
|
||||
*
|
||||
* * 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").
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array|object $filter Query by which to filter documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) {
|
||||
throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['projection'])) {
|
||||
$options['fields'] = $options['projection'];
|
||||
}
|
||||
|
||||
unset($options['projection']);
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function array_key_exists;
|
||||
use function is_array;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function MongoDB\is_first_key_operator;
|
||||
|
||||
/**
|
||||
* Operation for replacing a document with the findAndModify command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::findOneAndReplace()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/findAndModify/
|
||||
*/
|
||||
class FindOneAndReplace implements Executable, Explainable
|
||||
{
|
||||
public const RETURN_DOCUMENT_BEFORE = 1;
|
||||
public const RETURN_DOCUMENT_AFTER = 2;
|
||||
|
||||
/** @var FindAndModify */
|
||||
private $findAndModify;
|
||||
|
||||
/**
|
||||
* Constructs a findAndModify command for replacing a document.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * projection (document): Limits the fields to return for the matching
|
||||
* document.
|
||||
*
|
||||
* * returnDocument (enum): Whether to return the document before or after
|
||||
* the update is applied. Must be either
|
||||
* FindOneAndReplace::RETURN_DOCUMENT_BEFORE or
|
||||
* FindOneAndReplace::RETURN_DOCUMENT_AFTER. The default is
|
||||
* FindOneAndReplace::RETURN_DOCUMENT_BEFORE.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * upsert (boolean): When true, a new document is created if no document
|
||||
* matches the query. 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|object $filter Query by which to filter documents
|
||||
* @param array|object $replacement Replacement document
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, $replacement, array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
if (! is_array($replacement) && ! is_object($replacement)) {
|
||||
throw InvalidArgumentException::invalidType('$replacement', $replacement, 'array or object');
|
||||
}
|
||||
|
||||
if (is_first_key_operator($replacement)) {
|
||||
throw new InvalidArgumentException('First key in $replacement argument is an update operator');
|
||||
}
|
||||
|
||||
if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) {
|
||||
throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object');
|
||||
}
|
||||
|
||||
if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) {
|
||||
throw InvalidArgumentException::invalidType('"returnDocument" option', $options['returnDocument'], 'integer');
|
||||
}
|
||||
|
||||
if (
|
||||
isset($options['returnDocument']) &&
|
||||
$options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER &&
|
||||
$options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE
|
||||
) {
|
||||
throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']);
|
||||
}
|
||||
|
||||
if (isset($options['projection'])) {
|
||||
$options['fields'] = $options['projection'];
|
||||
}
|
||||
|
||||
if (isset($options['returnDocument'])) {
|
||||
$options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER;
|
||||
}
|
||||
|
||||
unset($options['projection'], $options['returnDocument']);
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function array_key_exists;
|
||||
use function is_array;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function MongoDB\is_first_key_operator;
|
||||
use function MongoDB\is_pipeline;
|
||||
|
||||
/**
|
||||
* Operation for updating a document with the findAndModify command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::findOneAndUpdate()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/findAndModify/
|
||||
*/
|
||||
class FindOneAndUpdate implements Executable, Explainable
|
||||
{
|
||||
public const RETURN_DOCUMENT_BEFORE = 1;
|
||||
public const RETURN_DOCUMENT_AFTER = 2;
|
||||
|
||||
/** @var FindAndModify */
|
||||
private $findAndModify;
|
||||
|
||||
/**
|
||||
* Constructs a findAndModify command for updating a document.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * arrayFilters (document array): A set of filters specifying to which
|
||||
* array elements an update should apply.
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * projection (document): Limits the fields to return for the matching
|
||||
* document.
|
||||
*
|
||||
* * returnDocument (enum): Whether to return the document before or after
|
||||
* the update is applied. Must be either
|
||||
* FindOneAndUpdate::RETURN_DOCUMENT_BEFORE or
|
||||
* FindOneAndUpdate::RETURN_DOCUMENT_AFTER. The default is
|
||||
* FindOneAndUpdate::RETURN_DOCUMENT_BEFORE.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * upsert (boolean): When true, a new document is created if no document
|
||||
* matches the query. 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|object $filter Query by which to filter documents
|
||||
* @param array|object $update Update to apply to the matched document
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
if (! is_array($update) && ! is_object($update)) {
|
||||
throw InvalidArgumentException::invalidType('$update', $update, 'array or object');
|
||||
}
|
||||
|
||||
if (! is_first_key_operator($update) && ! is_pipeline($update)) {
|
||||
throw new InvalidArgumentException('Expected an update document with operator as first key or a pipeline');
|
||||
}
|
||||
|
||||
if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) {
|
||||
throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object');
|
||||
}
|
||||
|
||||
if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) {
|
||||
throw InvalidArgumentException::invalidType('"returnDocument" option', $options['returnDocument'], 'integer');
|
||||
}
|
||||
|
||||
if (
|
||||
isset($options['returnDocument']) &&
|
||||
$options['returnDocument'] !== self::RETURN_DOCUMENT_AFTER &&
|
||||
$options['returnDocument'] !== self::RETURN_DOCUMENT_BEFORE
|
||||
) {
|
||||
throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']);
|
||||
}
|
||||
|
||||
if (isset($options['projection'])) {
|
||||
$options['fields'] = $options['projection'];
|
||||
}
|
||||
|
||||
if (isset($options['returnDocument'])) {
|
||||
$options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER;
|
||||
}
|
||||
|
||||
unset($options['projection'], $options['returnDocument']);
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\BulkWrite as Bulk;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\InsertManyResult;
|
||||
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Operation for inserting multiple documents with the insert command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::insertMany()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/insert/
|
||||
*/
|
||||
class InsertMany implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var object[]|array[] */
|
||||
private $documents;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs an insert command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to the command(s)
|
||||
* associated with this insert.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array[]|object[] $documents List of documents to insert
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, array $documents, array $options = [])
|
||||
{
|
||||
if (empty($documents)) {
|
||||
throw new InvalidArgumentException('$documents is empty');
|
||||
}
|
||||
|
||||
$expectedIndex = 0;
|
||||
|
||||
foreach ($documents as $i => $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;
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\BulkWrite as Bulk;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\InsertOneResult;
|
||||
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Operation for inserting a single document with the insert command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::insertOne()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/insert/
|
||||
*/
|
||||
class InsertOne implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array|object */
|
||||
private $document;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs an insert command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param array|object $document Document to insert
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $document, array $options = [])
|
||||
{
|
||||
if (! is_array($document) && ! is_object($document)) {
|
||||
throw InvalidArgumentException::invalidType('$document', $document, 'array or object');
|
||||
}
|
||||
|
||||
if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
|
||||
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], '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->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;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2020-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use Iterator;
|
||||
use MongoDB\Command\ListCollections as ListCollectionsCommand;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Model\CallbackIterator;
|
||||
|
||||
/**
|
||||
* Operation for the listCollectionNames helper.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Database::listCollectionNames()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listCollections/
|
||||
*/
|
||||
class ListCollectionNames implements Executable
|
||||
{
|
||||
/** @var ListCollectionsCommand */
|
||||
private $listCollections;
|
||||
|
||||
/**
|
||||
* Constructs a listCollections command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * authorizedCollections (boolean): Determines which collections are
|
||||
* returned based on the user privileges.
|
||||
*
|
||||
* For servers < 4.0, this option is ignored.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * filter (document): Query by which to filter collections.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, array $options = [])
|
||||
{
|
||||
$this->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'];
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Command\ListCollections as ListCollectionsCommand;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Model\CollectionInfoCommandIterator;
|
||||
use MongoDB\Model\CollectionInfoIterator;
|
||||
|
||||
/**
|
||||
* Operation for the listCollections command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Database::listCollections()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listCollections/
|
||||
*/
|
||||
class ListCollections implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var ListCollectionsCommand */
|
||||
private $listCollections;
|
||||
|
||||
/**
|
||||
* Constructs a listCollections command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * authorizedCollections (boolean): Determines which collections are
|
||||
* returned based on the user privileges.
|
||||
*
|
||||
* For servers < 4.0, this option is ignored.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * filter (document): Query by which to filter collections.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, array $options = [])
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2020-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use ArrayIterator;
|
||||
use Iterator;
|
||||
use MongoDB\Command\ListDatabases as ListDatabasesCommand;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
|
||||
use function array_column;
|
||||
|
||||
/**
|
||||
* Operation for the ListDatabases command, returning only database names.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Client::listDatabaseNames()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases
|
||||
*/
|
||||
class ListDatabaseNames implements Executable
|
||||
{
|
||||
/** @var ListDatabasesCommand */
|
||||
private $listDatabases;
|
||||
|
||||
/**
|
||||
* Constructs a listDatabases command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * authorizedDatabases (boolean): Determines which databases are returned
|
||||
* based on the user privileges.
|
||||
*
|
||||
* For servers < 4.0.5, this option is ignored.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * filter (document): Query by which to filter databases.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->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'));
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Command\ListDatabases as ListDatabasesCommand;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Model\DatabaseInfoIterator;
|
||||
use MongoDB\Model\DatabaseInfoLegacyIterator;
|
||||
|
||||
/**
|
||||
* Operation for the ListDatabases command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Client::listDatabases()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases`
|
||||
*/
|
||||
class ListDatabases implements Executable
|
||||
{
|
||||
/** @var ListDatabasesCommand */
|
||||
private $listDatabases;
|
||||
|
||||
/**
|
||||
* Constructs a listDatabases command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * authorizedDatabases (boolean): Determines which databases are returned
|
||||
* based on the user privileges.
|
||||
*
|
||||
* For servers < 4.0.5, this option is ignored.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * filter (document): Query by which to filter databases.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->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));
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use EmptyIterator;
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\CommandException;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Model\CachingIterator;
|
||||
use MongoDB\Model\IndexInfoIterator;
|
||||
use MongoDB\Model\IndexInfoIteratorIterator;
|
||||
|
||||
use function is_integer;
|
||||
|
||||
/**
|
||||
* Operation for the listIndexes command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::listIndexes()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/listIndexes/
|
||||
*/
|
||||
class ListIndexes implements Executable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $errorCodeDatabaseNotFound = 60;
|
||||
|
||||
/** @var integer */
|
||||
private static $errorCodeNamespaceNotFound = 26;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a listIndexes command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* @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 = [])
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,418 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use ArrayIterator;
|
||||
use MongoDB\BSON\JavascriptInterface;
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\MapReduceResult;
|
||||
use stdClass;
|
||||
|
||||
use function assert;
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\create_field_path_type_map;
|
||||
use function MongoDB\is_mapreduce_output_inline;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Operation for the mapReduce command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::mapReduce()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/mapReduce/
|
||||
*/
|
||||
class MapReduce implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var JavascriptInterface */
|
||||
private $map;
|
||||
|
||||
/** @var JavascriptInterface */
|
||||
private $reduce;
|
||||
|
||||
/** @var array|object|string */
|
||||
private $out;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a mapReduce command.
|
||||
*
|
||||
* Required arguments:
|
||||
*
|
||||
* * map (MongoDB\BSON\Javascript): A JavaScript function that associates
|
||||
* or "maps" a value with a key and emits the key and value pair.
|
||||
*
|
||||
* Passing a Javascript instance with a scope is deprecated. Put all
|
||||
* scope variables in the "scope" option of the MapReduce operation.
|
||||
*
|
||||
* * reduce (MongoDB\BSON\Javascript): A JavaScript function that "reduces"
|
||||
* to a single object all the values associated with a particular key.
|
||||
*
|
||||
* Passing a Javascript instance with a scope is deprecated. Put all
|
||||
* scope variables in the "scope" option of the MapReduce operation.
|
||||
*
|
||||
* * out (string|document): Specifies where to output the result of the
|
||||
* map-reduce operation. You can either output to a collection or return
|
||||
* the result inline. On a primary member of a replica set you can output
|
||||
* either to a collection or inline, but on a secondary, only inline
|
||||
* output is possible.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation. This only applies when results
|
||||
* are output to a collection.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * finalize (MongoDB\BSON\JavascriptInterface): Follows the reduce method
|
||||
* and modifies the output.
|
||||
*
|
||||
* Passing a Javascript instance with a scope is deprecated. Put all
|
||||
* scope variables in the "scope" option of the MapReduce operation.
|
||||
*
|
||||
* * jsMode (boolean): Specifies whether to convert intermediate data into
|
||||
* BSON format between the execution of the map and reduce functions.
|
||||
*
|
||||
* * limit (integer): Specifies a maximum number of documents for the input
|
||||
* into the map function.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * query (document): Specifies the selection criteria using query
|
||||
* operators for determining the documents input to the map function.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern. This is not
|
||||
* supported when results are returned inline.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
|
||||
*
|
||||
* This option is ignored if results are output to a collection.
|
||||
*
|
||||
* * scope (document): Specifies global variables that are accessible in
|
||||
* the map, reduce and finalize functions.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * sort (document): Sorts the input documents. This option is useful for
|
||||
* optimization. For example, specify the sort key to be the same as the
|
||||
* emit key so that there are fewer reduce operations. The sort key must
|
||||
* be in an existing index for this collection.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be
|
||||
* applied to the returned Cursor (it is not sent to the server).
|
||||
*
|
||||
* * verbose (boolean): Specifies whether to include the timing information
|
||||
* in the result information.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern. This only
|
||||
* applies when results are output to a collection.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection name
|
||||
* @param JavascriptInterface $map Map function
|
||||
* @param JavascriptInterface $reduce Reduce function
|
||||
* @param string|array|object $out Output specification
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [])
|
||||
{
|
||||
if (! is_string($out) && ! is_array($out) && ! is_object($out)) {
|
||||
throw InvalidArgumentException::invalidType('$out', $out, 'string or array or object');
|
||||
}
|
||||
|
||||
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['finalize']) && ! $options['finalize'] instanceof JavascriptInterface) {
|
||||
throw InvalidArgumentException::invalidType('"finalize" option', $options['finalize'], JavascriptInterface::class);
|
||||
}
|
||||
|
||||
if (isset($options['jsMode']) && ! is_bool($options['jsMode'])) {
|
||||
throw InvalidArgumentException::invalidType('"jsMode" option', $options['jsMode'], 'boolean');
|
||||
}
|
||||
|
||||
if (isset($options['limit']) && ! is_integer($options['limit'])) {
|
||||
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
|
||||
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['query']) && ! is_array($options['query']) && ! is_object($options['query'])) {
|
||||
throw InvalidArgumentException::invalidType('"query" option', $options['query'], 'array or object');
|
||||
}
|
||||
|
||||
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['scope']) && ! is_array($options['scope']) && ! is_object($options['scope'])) {
|
||||
throw InvalidArgumentException::invalidType('"scope" option', $options['scope'], 'array or object');
|
||||
}
|
||||
|
||||
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['verbose']) && ! is_bool($options['verbose'])) {
|
||||
throw InvalidArgumentException::invalidType('"verbose" option', $options['verbose'], 'boolean');
|
||||
}
|
||||
|
||||
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['readConcern']) && $options['readConcern']->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;
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2018-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Operation for the collMod command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Database::modifyCollection()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/collMod/
|
||||
*/
|
||||
class ModifyCollection implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array */
|
||||
private $collectionOptions;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a collMod command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will only be
|
||||
* used for the returned command result document.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param string $collectionName Collection or view to modify
|
||||
* @param array $collectionOptions Collection or view options to assign
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, array $collectionOptions, array $options = [])
|
||||
{
|
||||
if (empty($collectionOptions)) {
|
||||
throw new InvalidArgumentException('$collectionOptions is empty');
|
||||
}
|
||||
|
||||
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 (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->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;
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2021-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
|
||||
/**
|
||||
* Operation for the renameCollection command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::rename()
|
||||
* @see \MongoDB\Database::renameCollection()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/renameCollection/
|
||||
*/
|
||||
class RenameCollection implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $fromNamespace;
|
||||
|
||||
/** @var string */
|
||||
private $toNamespace;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a renameCollection command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be used
|
||||
* for the returned command result document.
|
||||
*
|
||||
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
|
||||
*
|
||||
* * dropTarget (boolean): If true, MongoDB will drop the target before
|
||||
* renaming the collection.
|
||||
*
|
||||
* @param string $fromDatabaseName Database name
|
||||
* @param string $fromCollectionName Collection name
|
||||
* @param string $toDatabaseName New database name
|
||||
* @param string $toCollectionName New collection name
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $fromDatabaseName, string $fromCollectionName, string $toDatabaseName, string $toCollectionName, array $options = [])
|
||||
{
|
||||
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 (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']);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\UpdateResult;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function MongoDB\is_first_key_operator;
|
||||
use function MongoDB\is_pipeline;
|
||||
|
||||
/**
|
||||
* Operation for replacing a single document with the update command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::replaceOne()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/update/
|
||||
*/
|
||||
class ReplaceOne implements Executable
|
||||
{
|
||||
/** @var Update */
|
||||
private $update;
|
||||
|
||||
/**
|
||||
* Constructs an update command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.2 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * upsert (boolean): When true, a new document is created if no document
|
||||
* matches the query. 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|object $filter Query by which to filter documents
|
||||
* @param array|object $replacement Replacement document
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, $replacement, array $options = [])
|
||||
{
|
||||
if (! is_array($replacement) && ! is_object($replacement)) {
|
||||
throw InvalidArgumentException::invalidType('$replacement', $replacement, 'array or object');
|
||||
}
|
||||
|
||||
if (is_first_key_operator($replacement)) {
|
||||
throw new InvalidArgumentException('First key in $replacement argument is an update operator');
|
||||
}
|
||||
|
||||
if (is_pipeline($replacement)) {
|
||||
throw new InvalidArgumentException('$replacement argument is a pipeline');
|
||||
}
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
-305
@@ -1,305 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\BulkWrite as Bulk;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\UpdateResult;
|
||||
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\is_first_key_operator;
|
||||
use function MongoDB\is_pipeline;
|
||||
use function MongoDB\is_write_concern_acknowledged;
|
||||
use function MongoDB\server_supports_feature;
|
||||
|
||||
/**
|
||||
* Operation for the update command.
|
||||
*
|
||||
* This class is used internally by the ReplaceOne, UpdateMany, and UpdateOne
|
||||
* operation classes.
|
||||
*
|
||||
* @internal
|
||||
* @see https://mongodb.com/docs/manual/reference/command/update/
|
||||
*/
|
||||
class Update implements Executable, Explainable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $wireVersionForHint = 8;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var string */
|
||||
private $collectionName;
|
||||
|
||||
/** @var array|object */
|
||||
private $filter;
|
||||
|
||||
/** @var array|object */
|
||||
private $update;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a update command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * arrayFilters (document array): A set of filters specifying to which
|
||||
* array elements an update should apply.
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.2 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * multi (boolean): When true, updates all documents matching the query.
|
||||
* This option cannot be true if the $update argument is a replacement
|
||||
* document (i.e. contains no update operators). The default is false.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * upsert (boolean): When true, a new document is created if no document
|
||||
* matches the query. 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|object $filter Query by which to delete documents
|
||||
* @param array|object $update Update to apply to the matched
|
||||
* document(s) or a replacement document
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
|
||||
{
|
||||
if (! is_array($filter) && ! is_object($filter)) {
|
||||
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
|
||||
}
|
||||
|
||||
if (! is_array($update) && ! is_object($update)) {
|
||||
throw InvalidArgumentException::invalidType('$update', $filter, 'array or object');
|
||||
}
|
||||
|
||||
$options += [
|
||||
'multi' => 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;
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\UpdateResult;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function MongoDB\is_first_key_operator;
|
||||
use function MongoDB\is_pipeline;
|
||||
|
||||
/**
|
||||
* Operation for updating multiple documents with the update command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::updateMany()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/update/
|
||||
*/
|
||||
class UpdateMany implements Executable, Explainable
|
||||
{
|
||||
/** @var Update */
|
||||
private $update;
|
||||
|
||||
/**
|
||||
* Constructs an update command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * arrayFilters (document array): A set of filters specifying to which
|
||||
* array elements an update should apply.
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.2 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * upsert (boolean): When true, a new document is created if no document
|
||||
* matches the query. 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|object $filter Query by which to filter documents
|
||||
* @param array|object $update Update to apply to the matched documents
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
|
||||
{
|
||||
if (! is_array($update) && ! is_object($update)) {
|
||||
throw InvalidArgumentException::invalidType('$update', $update, 'array or object');
|
||||
}
|
||||
|
||||
if (! is_first_key_operator($update) && ! is_pipeline($update)) {
|
||||
throw new InvalidArgumentException('Expected an update document with operator as first key or a pipeline');
|
||||
}
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\UpdateResult;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function MongoDB\is_first_key_operator;
|
||||
use function MongoDB\is_pipeline;
|
||||
|
||||
/**
|
||||
* Operation for updating a single document with the update command.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::updateOne()
|
||||
* @see https://mongodb.com/docs/manual/reference/command/update/
|
||||
*/
|
||||
class UpdateOne implements Executable, Explainable
|
||||
{
|
||||
/** @var Update */
|
||||
private $update;
|
||||
|
||||
/**
|
||||
* Constructs an update command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * arrayFilters (document array): A set of filters specifying to which
|
||||
* array elements an update should apply.
|
||||
*
|
||||
* * bypassDocumentValidation (boolean): If true, allows the write to
|
||||
* circumvent document level validation.
|
||||
*
|
||||
* * collation (document): Collation specification.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* This is not supported for servers versions < 4.4.
|
||||
*
|
||||
* * hint (string|document): The index to use. Specify either the index
|
||||
* name as a string or the index key pattern as a document. If specified,
|
||||
* then the query system will only consider plans using the hinted index.
|
||||
*
|
||||
* This is not supported for server versions < 4.2 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * upsert (boolean): When true, a new document is created if no document
|
||||
* matches the query. 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|object $filter Query by which to filter documents
|
||||
* @param array|object $update Update to apply to the matched document
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = [])
|
||||
{
|
||||
if (! is_array($update) && ! is_object($update)) {
|
||||
throw InvalidArgumentException::invalidType('$update', $update, 'array or object');
|
||||
}
|
||||
|
||||
if (! is_first_key_operator($update) && ! is_pipeline($update)) {
|
||||
throw new InvalidArgumentException('Expected an update document with operator as first key or a pipeline');
|
||||
}
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
-478
@@ -1,478 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2017-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\BSON\TimestampInterface;
|
||||
use MongoDB\ChangeStream;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\RuntimeException;
|
||||
use MongoDB\Driver\Manager;
|
||||
use MongoDB\Driver\Monitoring\CommandFailedEvent;
|
||||
use MongoDB\Driver\Monitoring\CommandStartedEvent;
|
||||
use MongoDB\Driver\Monitoring\CommandSubscriber;
|
||||
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\UnexpectedValueException;
|
||||
use MongoDB\Exception\UnsupportedException;
|
||||
use MongoDB\Model\ChangeStreamIterator;
|
||||
|
||||
use function array_intersect_key;
|
||||
use function array_key_exists;
|
||||
use function array_unshift;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\Driver\Monitoring\addSubscriber;
|
||||
use function MongoDB\Driver\Monitoring\removeSubscriber;
|
||||
use function MongoDB\select_server;
|
||||
use function MongoDB\server_supports_feature;
|
||||
|
||||
/**
|
||||
* Operation for creating a change stream with the aggregate command.
|
||||
*
|
||||
* Note: the implementation of CommandSubscriber is an internal implementation
|
||||
* detail and should not be considered part of the public API.
|
||||
*
|
||||
* @api
|
||||
* @see \MongoDB\Collection::watch()
|
||||
* @see https://mongodb.com/docs/manual/changeStreams/
|
||||
*/
|
||||
class Watch implements Executable, /* @internal */ CommandSubscriber
|
||||
{
|
||||
public const FULL_DOCUMENT_DEFAULT = 'default';
|
||||
public const FULL_DOCUMENT_UPDATE_LOOKUP = 'updateLookup';
|
||||
public const FULL_DOCUMENT_WHEN_AVAILABLE = 'whenAvailable';
|
||||
public const FULL_DOCUMENT_REQUIRED = 'required';
|
||||
|
||||
public const FULL_DOCUMENT_BEFORE_CHANGE_OFF = 'off';
|
||||
public const FULL_DOCUMENT_BEFORE_CHANGE_WHEN_AVAILABLE = 'whenAvailable';
|
||||
public const FULL_DOCUMENT_BEFORE_CHANGE_REQUIRED = 'required';
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForStartAtOperationTime = 7;
|
||||
|
||||
/** @var Aggregate */
|
||||
private $aggregate;
|
||||
|
||||
/** @var array */
|
||||
private $aggregateOptions;
|
||||
|
||||
/** @var array */
|
||||
private $changeStreamOptions;
|
||||
|
||||
/** @var string|null */
|
||||
private $collectionName;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var integer */
|
||||
private $firstBatchSize;
|
||||
|
||||
/** @var boolean */
|
||||
private $hasResumed = false;
|
||||
|
||||
/** @var Manager */
|
||||
private $manager;
|
||||
|
||||
/** @var TimestampInterface */
|
||||
private $operationTime;
|
||||
|
||||
/** @var array */
|
||||
private $pipeline;
|
||||
|
||||
/** @var object|null */
|
||||
private $postBatchResumeToken;
|
||||
|
||||
/**
|
||||
* Constructs an aggregate command for creating a change stream.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * batchSize (integer): The number of documents to return per batch.
|
||||
*
|
||||
* * collation (document): Specifies a collation.
|
||||
*
|
||||
* * comment (mixed): BSON value to attach as a comment to this command.
|
||||
*
|
||||
* Only string values are supported for server versions < 4.4.
|
||||
*
|
||||
* * fullDocument (string): Determines how the "fullDocument" response
|
||||
* field will be populated for update operations.
|
||||
*
|
||||
* By default, change streams only return the delta of fields (via an
|
||||
* "updateDescription" field) for update operations and "fullDocument" is
|
||||
* omitted. Insert and replace operations always include the
|
||||
* "fullDocument" field. Delete operations omit the field as the document
|
||||
* no longer exists.
|
||||
*
|
||||
* Specify "updateLookup" to return the current majority-committed
|
||||
* version of the updated document.
|
||||
*
|
||||
* MongoDB 6.0+ allows returning the post-image of the modified document
|
||||
* if the collection has changeStreamPreAndPostImages enabled. Specify
|
||||
* "whenAvailable" to return the post-image if available or a null value
|
||||
* if not. Specify "required" to return the post-image if available or
|
||||
* raise an error if not.
|
||||
*
|
||||
* * fullDocumentBeforeChange (string): Determines how the
|
||||
* "fullDocumentBeforeChange" response field will be populated. By
|
||||
* default, the field is omitted.
|
||||
*
|
||||
* MongoDB 6.0+ allows returning the pre-image of the modified document
|
||||
* if the collection has changeStreamPreAndPostImages enabled. Specify
|
||||
* "whenAvailable" to return the pre-image if available or a null value
|
||||
* if not. Specify "required" to return the pre-image if available or
|
||||
* raise an error if not.
|
||||
*
|
||||
* * maxAwaitTimeMS (integer): The maximum amount of time for the server to
|
||||
* wait on new documents to satisfy a change stream query.
|
||||
*
|
||||
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
|
||||
*
|
||||
* * readPreference (MongoDB\Driver\ReadPreference): Read preference. This
|
||||
* will be used to select a new server when resuming. Defaults to a
|
||||
* "primary" read preference.
|
||||
*
|
||||
* * resumeAfter (document): Specifies the logical starting point for the
|
||||
* new change stream.
|
||||
*
|
||||
* Using this option in conjunction with "startAfter" and/or
|
||||
* "startAtOperationTime" will result in a server error. The options are
|
||||
* mutually exclusive.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* * showExpandedEvents (boolean): Enables the server to send the expanded
|
||||
* list of change stream events.
|
||||
*
|
||||
* This option is not supported for server versions < 6.0.
|
||||
*
|
||||
* * startAfter (document): Specifies the logical starting point for the
|
||||
* new change stream. Unlike "resumeAfter", this option can be used with
|
||||
* a resume token from an "invalidate" event.
|
||||
*
|
||||
* Using this option in conjunction with "resumeAfter" and/or
|
||||
* "startAtOperationTime" will result in a server error. The options are
|
||||
* mutually exclusive.
|
||||
*
|
||||
* * startAtOperationTime (MongoDB\BSON\TimestampInterface): If specified,
|
||||
* the change stream will only provide changes that occurred at or after
|
||||
* the specified timestamp. Any command run against the server will
|
||||
* return an operation time that can be used here. Alternatively, an
|
||||
* operation time may be obtained from MongoDB\Driver\Server::getInfo().
|
||||
*
|
||||
* Using this option in conjunction with "resumeAfter" and/or
|
||||
* "startAfter" will result in a server error. The options are mutually
|
||||
* exclusive.
|
||||
*
|
||||
* This option is not supported for server versions < 4.0.
|
||||
*
|
||||
* * typeMap (array): Type map for BSON deserialization. This will be
|
||||
* applied to the returned Cursor (it is not sent to the server).
|
||||
*
|
||||
* Note: A database-level change stream may be created by specifying null
|
||||
* for the collection name. A cluster-level change stream may be created by
|
||||
* specifying null for both the database and collection name.
|
||||
*
|
||||
* @param Manager $manager Manager instance from the driver
|
||||
* @param string|null $databaseName Database name
|
||||
* @param string|null $collectionName Collection name
|
||||
* @param array $pipeline List of pipeline operations
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct(Manager $manager, ?string $databaseName, ?string $collectionName, array $pipeline, array $options = [])
|
||||
{
|
||||
if (isset($collectionName) && ! isset($databaseName)) {
|
||||
throw new InvalidArgumentException('$collectionName should also be null if $databaseName is null');
|
||||
}
|
||||
|
||||
$options += [
|
||||
'readPreference' => 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;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use Exception;
|
||||
use MongoDB\Driver\Exception\RuntimeException;
|
||||
use MongoDB\Driver\Session;
|
||||
use Throwable;
|
||||
|
||||
use function call_user_func;
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class WithTransaction
|
||||
{
|
||||
/** @var callable */
|
||||
private $callback;
|
||||
|
||||
/** @var array */
|
||||
private $transactionOptions;
|
||||
|
||||
/**
|
||||
* @see Session::startTransaction for supported transaction options
|
||||
*
|
||||
* @param callable $callback A callback that will be invoked within the transaction
|
||||
* @param array $transactionOptions Additional options that are passed to Session::startTransaction
|
||||
*/
|
||||
public function __construct(callable $callback, array $transactionOptions = [])
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use MongoDB\Driver\WriteResult;
|
||||
use MongoDB\Exception\BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Result class for an update operation.
|
||||
*/
|
||||
class UpdateResult
|
||||
{
|
||||
/** @var WriteResult */
|
||||
private $writeResult;
|
||||
|
||||
/** @var boolean */
|
||||
private $isAcknowledged;
|
||||
|
||||
public function __construct(WriteResult $writeResult)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
-569
@@ -1,569 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2015-present MongoDB, Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
namespace MongoDB;
|
||||
|
||||
use Exception;
|
||||
use MongoDB\BSON\Serializable;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Manager;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Exception\InvalidArgumentException;
|
||||
use MongoDB\Exception\RuntimeException;
|
||||
use MongoDB\Operation\ListCollections;
|
||||
use MongoDB\Operation\WithTransaction;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
|
||||
use function assert;
|
||||
use function end;
|
||||
use function get_object_vars;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function key;
|
||||
use function MongoDB\BSON\fromPHP;
|
||||
use function MongoDB\BSON\toPHP;
|
||||
use function reset;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Check whether all servers support executing a write stage on a secondary.
|
||||
*
|
||||
* @internal
|
||||
* @param Server[] $servers
|
||||
*/
|
||||
function all_servers_support_write_stage_on_secondary(array $servers): bool
|
||||
{
|
||||
/* Write stages on secondaries are technically supported by FCV 4.4, but the
|
||||
* CRUD spec requires all 5.0+ servers since FCV is not tracked by SDAM. */
|
||||
static $wireVersionForWriteStageOnSecondary = 13;
|
||||
|
||||
foreach ($servers as $server) {
|
||||
// We can assume that load balancers only front 5.0+ servers
|
||||
if ($server->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;
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for cachestore_mongodb.
|
||||
*
|
||||
* @package cachestore_mongodb
|
||||
* @category privacy
|
||||
* @copyright 2018 Andrew Nicols <[email protected]>
|
||||
* @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 <[email protected]>
|
||||
* @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) {
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* 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.';
|
||||
Vendored
-585
@@ -1,585 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
}
|
||||
-13
@@ -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
|
||||
Vendored
-33
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* 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));
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
// This mongodb is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<libraries>
|
||||
<library>
|
||||
<location>MongoDB</location>
|
||||
<name>MongoDB PHP Library</name>
|
||||
<description>This library provides a high-level abstraction around the lower-level PHP driver, also known as the mongodb extension.</description>
|
||||
<version>1.15.0</version>
|
||||
<license>Apache</license>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
<repository>https://github.com/mongodb/mongo-php-driver</repository>
|
||||
</library>
|
||||
</libraries>
|
||||
Vendored
-29
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* 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.
|
||||
Vendored
+3
@@ -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.
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user