mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 08:56:36 +02:00
Merge branch 'MDL-70309-311' of git://github.com/ferranrecio/moodle into MOODLE_311_STABLE
This commit is contained in:
commit
b6ea9f0d8c
51 changed files with 1290 additions and 277 deletions
40
cache/stores/mongodb/MongoDB/ChangeStream.php
vendored
40
cache/stores/mongodb/MongoDB/ChangeStream.php
vendored
|
@ -42,13 +42,33 @@ class ChangeStream implements Iterator
|
|||
*/
|
||||
const CURSOR_NOT_FOUND = 43;
|
||||
|
||||
/** @var array */
|
||||
private static $nonResumableErrorCodes = [
|
||||
136, // CappedPositionLost
|
||||
237, // CursorKilled
|
||||
11601, // Interrupted
|
||||
/** @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, // NotMaster
|
||||
11600, // InterruptedAtShutdown
|
||||
11602, // InterruptedDueToReplStateChange
|
||||
13435, // NotMasterNoSlaveOk
|
||||
13436, // NotMasterOrSecondary
|
||||
63, // StaleShardVersion
|
||||
150, // StaleEpoch
|
||||
13388, // StaleConfig
|
||||
234, // RetryChangeStream
|
||||
133, // FailedToSatisfyReadPreference
|
||||
];
|
||||
|
||||
/** @var int */
|
||||
private static $wireVersionForResumableChangeStreamError = 9;
|
||||
|
||||
/** @var callable */
|
||||
private $resumeCallable;
|
||||
|
||||
|
@ -180,15 +200,15 @@ class ChangeStream implements Iterator
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($exception->hasErrorLabel('NonResumableChangeStreamError')) {
|
||||
return false;
|
||||
if ($exception->getCode() === self::$cursorNotFound) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($exception->getCode(), self::$nonResumableErrorCodes)) {
|
||||
return false;
|
||||
if (server_supports_feature($this->iterator->getServer(), self::$wireVersionForResumableChangeStreamError)) {
|
||||
return $exception->hasErrorLabel('ResumableChangeStreamError');
|
||||
}
|
||||
|
||||
return true;
|
||||
return in_array($exception->getCode(), self::$resumableErrorCodes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
105
cache/stores/mongodb/MongoDB/Client.php
vendored
105
cache/stores/mongodb/MongoDB/Client.php
vendored
|
@ -17,6 +17,9 @@
|
|||
|
||||
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;
|
||||
|
@ -31,9 +34,12 @@ 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
|
||||
{
|
||||
|
@ -50,6 +56,12 @@ class Client
|
|||
/** @var integer */
|
||||
private static $wireVersionForWritableCommandWriteConcern = 5;
|
||||
|
||||
/** @var string */
|
||||
private static $handshakeSeparator = ' / ';
|
||||
|
||||
/** @var string|null */
|
||||
private static $version;
|
||||
|
||||
/** @var Manager */
|
||||
private $manager;
|
||||
|
||||
|
@ -95,12 +107,22 @@ class Client
|
|||
{
|
||||
$driverOptions += ['typeMap' => self::$defaultTypeMap];
|
||||
|
||||
if (isset($driverOptions['typeMap']) && ! is_array($driverOptions['typeMap'])) {
|
||||
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 = (string) $uri;
|
||||
$this->typeMap = isset($driverOptions['typeMap']) ? $driverOptions['typeMap'] : null;
|
||||
$this->typeMap = $driverOptions['typeMap'] ?? null;
|
||||
|
||||
unset($driverOptions['typeMap']);
|
||||
|
||||
|
@ -153,6 +175,26 @@ class Client
|
|||
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.
|
||||
*
|
||||
|
@ -233,6 +275,22 @@ class Client
|
|||
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.
|
||||
*
|
||||
|
@ -325,4 +383,47 @@ class Client
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
10
cache/stores/mongodb/MongoDB/Collection.php
vendored
10
cache/stores/mongodb/MongoDB/Collection.php
vendored
|
@ -162,10 +162,10 @@ class Collection
|
|||
$this->manager = $manager;
|
||||
$this->databaseName = (string) $databaseName;
|
||||
$this->collectionName = (string) $collectionName;
|
||||
$this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
|
||||
$this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
|
||||
$this->typeMap = isset($options['typeMap']) ? $options['typeMap'] : self::$defaultTypeMap;
|
||||
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -356,7 +356,7 @@ class Collection
|
|||
*/
|
||||
public function createIndex($key, array $options = [])
|
||||
{
|
||||
$commandOptionKeys = ['maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1];
|
||||
$commandOptionKeys = ['commitQuorum' => 1, 'maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1];
|
||||
$indexOptions = array_diff_key($options, $commandOptionKeys);
|
||||
$commandOptions = array_intersect_key($options, $commandOptionKeys);
|
||||
|
||||
|
|
139
cache/stores/mongodb/MongoDB/Command/ListCollections.php
vendored
Normal file
139
cache/stores/mongodb/MongoDB/Command/ListCollections.php
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 http://docs.mongodb.org/manual/reference/command/listCollections/
|
||||
*/
|
||||
class ListCollections implements Executable
|
||||
{
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* Constructs a listCollections command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct($databaseName, array $options = [])
|
||||
{
|
||||
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 = (string) $databaseName;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the operation.
|
||||
*
|
||||
* @see Executable::execute()
|
||||
* @param Server $server
|
||||
* @return CachingIterator
|
||||
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
|
||||
*/
|
||||
public function execute(Server $server)
|
||||
{
|
||||
$cmd = ['listCollections' => 1];
|
||||
|
||||
if (! empty($this->options['filter'])) {
|
||||
$cmd['filter'] = (object) $this->options['filter'];
|
||||
}
|
||||
|
||||
if (isset($this->options['maxTimeMS'])) {
|
||||
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
|
||||
}
|
||||
|
||||
if (isset($this->options['nameOnly'])) {
|
||||
$cmd['nameOnly'] = $this->options['nameOnly'];
|
||||
}
|
||||
|
||||
$cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions());
|
||||
$cursor->setTypeMap(['root' => 'array', 'document' => 'array']);
|
||||
|
||||
return new CachingIterator($cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 http://php.net/manual/en/mongodb-driver-server.executecommand.php
|
||||
* @return array
|
||||
*/
|
||||
private function createOptions()
|
||||
{
|
||||
$options = [];
|
||||
|
||||
if (isset($this->options['session'])) {
|
||||
$options['session'] = $this->options['session'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
156
cache/stores/mongodb/MongoDB/Command/ListDatabases.php
vendored
Normal file
156
cache/stores/mongodb/MongoDB/Command/ListDatabases.php
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 http://docs.mongodb.org/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.
|
||||
*
|
||||
* * filter (document): Query by which to filter databases.
|
||||
*
|
||||
* For servers < 3.6, this option is ignored.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
*
|
||||
* @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()
|
||||
* @param Server $server
|
||||
* @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)
|
||||
{
|
||||
$cmd = ['listDatabases' => 1];
|
||||
|
||||
if (isset($this->options['authorizedDatabases'])) {
|
||||
$cmd['authorizedDatabases'] = $this->options['authorizedDatabases'];
|
||||
}
|
||||
|
||||
if (! empty($this->options['filter'])) {
|
||||
$cmd['filter'] = (object) $this->options['filter'];
|
||||
}
|
||||
|
||||
if (isset($this->options['maxTimeMS'])) {
|
||||
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
|
||||
}
|
||||
|
||||
if (isset($this->options['nameOnly'])) {
|
||||
$cmd['nameOnly'] = $this->options['nameOnly'];
|
||||
}
|
||||
|
||||
$cursor = $server->executeReadCommand('admin', new Command($cmd), $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 options for executing the command.
|
||||
*
|
||||
* Note: read preference is intentionally omitted, as the spec requires that
|
||||
* the command be executed on the primary.
|
||||
*
|
||||
* @see http://php.net/manual/en/mongodb-driver-server.executecommand.php
|
||||
* @return array
|
||||
*/
|
||||
private function createOptions()
|
||||
{
|
||||
$options = [];
|
||||
|
||||
if (isset($this->options['session'])) {
|
||||
$options['session'] = $this->options['session'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
29
cache/stores/mongodb/MongoDB/Database.php
vendored
29
cache/stores/mongodb/MongoDB/Database.php
vendored
|
@ -17,6 +17,7 @@
|
|||
|
||||
namespace MongoDB;
|
||||
|
||||
use Iterator;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Manager;
|
||||
|
@ -35,6 +36,7 @@ use MongoDB\Operation\CreateCollection;
|
|||
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\Watch;
|
||||
|
@ -129,10 +131,10 @@ class Database
|
|||
|
||||
$this->manager = $manager;
|
||||
$this->databaseName = (string) $databaseName;
|
||||
$this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
|
||||
$this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
|
||||
$this->typeMap = isset($options['typeMap']) ? $options['typeMap'] : self::$defaultTypeMap;
|
||||
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,10 +251,6 @@ class Database
|
|||
*/
|
||||
public function command($command, array $options = [])
|
||||
{
|
||||
if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
|
||||
$options['readPreference'] = $this->readPreference;
|
||||
}
|
||||
|
||||
if (! isset($options['typeMap'])) {
|
||||
$options['typeMap'] = $this->typeMap;
|
||||
}
|
||||
|
@ -408,6 +406,21 @@ class Database
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
namespace MongoDB\Exception;
|
||||
|
||||
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function is_object;
|
||||
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
|
||||
|
@ -28,13 +30,32 @@ class InvalidArgumentException extends DriverInvalidArgumentException implements
|
|||
/**
|
||||
* 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 $expectedType Expected 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($name, $value, $expectedType)
|
||||
{
|
||||
return new static(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, is_object($value) ? get_class($value) : gettype($value)));
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
namespace MongoDB\Exception;
|
||||
|
||||
use function gettype;
|
||||
use function get_debug_type;
|
||||
use function sprintf;
|
||||
|
||||
class ResumeTokenException extends RuntimeException
|
||||
|
@ -30,7 +30,7 @@ class ResumeTokenException extends RuntimeException
|
|||
*/
|
||||
public static function invalidType($value)
|
||||
{
|
||||
return new static(sprintf('Expected resume token to have type "array or object" but found "%s"', gettype($value)));
|
||||
return new static(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,16 @@ 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.
|
||||
*
|
||||
|
@ -39,6 +49,17 @@ class UnsupportedException extends RuntimeException
|
|||
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.
|
||||
*
|
||||
|
@ -49,6 +70,16 @@ class UnsupportedException extends RuntimeException
|
|||
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.
|
||||
*
|
||||
|
|
38
cache/stores/mongodb/MongoDB/GridFS/Bucket.php
vendored
38
cache/stores/mongodb/MongoDB/GridFS/Bucket.php
vendored
|
@ -28,6 +28,7 @@ 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;
|
||||
|
@ -143,19 +144,19 @@ class Bucket
|
|||
'disableMD5' => false,
|
||||
];
|
||||
|
||||
if (isset($options['bucketName']) && ! is_string($options['bucketName'])) {
|
||||
if (! is_string($options['bucketName'])) {
|
||||
throw InvalidArgumentException::invalidType('"bucketName" option', $options['bucketName'], 'string');
|
||||
}
|
||||
|
||||
if (isset($options['chunkSizeBytes']) && ! is_integer($options['chunkSizeBytes'])) {
|
||||
if (! is_integer($options['chunkSizeBytes'])) {
|
||||
throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['chunkSizeBytes']) && $options['chunkSizeBytes'] < 1) {
|
||||
if ($options['chunkSizeBytes'] < 1) {
|
||||
throw new InvalidArgumentException(sprintf('Expected "chunkSizeBytes" option to be >= 1, %d given', $options['chunkSizeBytes']));
|
||||
}
|
||||
|
||||
if (isset($options['disableMD5']) && ! is_bool($options['disableMD5'])) {
|
||||
if (! is_bool($options['disableMD5'])) {
|
||||
throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean');
|
||||
}
|
||||
|
||||
|
@ -180,10 +181,10 @@ class Bucket
|
|||
$this->bucketName = $options['bucketName'];
|
||||
$this->chunkSizeBytes = $options['chunkSizeBytes'];
|
||||
$this->disableMD5 = $options['disableMD5'];
|
||||
$this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
|
||||
$this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
|
||||
$this->typeMap = isset($options['typeMap']) ? $options['typeMap'] : self::$defaultTypeMap;
|
||||
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
|
||||
$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]);
|
||||
|
||||
|
@ -238,6 +239,7 @@ class Bucket
|
|||
* @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)
|
||||
|
@ -246,7 +248,10 @@ class Bucket
|
|||
throw InvalidArgumentException::invalidType('$destination', $destination, 'resource');
|
||||
}
|
||||
|
||||
stream_copy_to_stream($this->openDownloadStream($id), $destination);
|
||||
$source = $this->openDownloadStream($id);
|
||||
if (@stream_copy_to_stream($source, $destination) === false) {
|
||||
throw StreamException::downloadFromIdFailed($id, $source, $destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,6 +278,7 @@ class Bucket
|
|||
* @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($filename, $destination, array $options = [])
|
||||
|
@ -281,7 +287,10 @@ class Bucket
|
|||
throw InvalidArgumentException::invalidType('$destination', $destination, 'resource');
|
||||
}
|
||||
|
||||
stream_copy_to_stream($this->openDownloadStreamByName($filename, $options), $destination);
|
||||
$source = $this->openDownloadStreamByName($filename, $options);
|
||||
if (@stream_copy_to_stream($source, $destination) === false) {
|
||||
throw StreamException::downloadFromFilenameFailed($filename, $source, $destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -607,6 +616,7 @@ class Bucket
|
|||
* @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($filename, $source, array $options = [])
|
||||
|
@ -616,7 +626,11 @@ class Bucket
|
|||
}
|
||||
|
||||
$destination = $this->openUploadStream($filename, $options);
|
||||
stream_copy_to_stream($source, $destination);
|
||||
|
||||
if (@stream_copy_to_stream($source, $destination) === false) {
|
||||
$destinationUri = $this->createPathForFile($this->getRawFileDocumentForStream($destination));
|
||||
throw StreamException::uploadFailed($filename, $source, $destinationUri);
|
||||
}
|
||||
|
||||
return $this->getFileIdForStream($destination);
|
||||
}
|
||||
|
@ -688,7 +702,7 @@ class Bucket
|
|||
$metadata = stream_get_meta_data($stream);
|
||||
|
||||
if (! isset($metadata['wrapper_data']) || ! $metadata['wrapper_data'] instanceof StreamWrapper) {
|
||||
throw InvalidArgumentException::invalidType('$stream wrapper data', isset($metadata['wrapper_data']) ? $metadata['wrapper_data'] : null, StreamWrapper::class);
|
||||
throw InvalidArgumentException::invalidType('$stream wrapper data', $metadata['wrapper_data'] ?? null, StreamWrapper::class);
|
||||
}
|
||||
|
||||
return $metadata['wrapper_data']->getFile();
|
||||
|
|
|
@ -17,14 +17,18 @@
|
|||
|
||||
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 stdClass;
|
||||
use function abs;
|
||||
use function count;
|
||||
use function is_numeric;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
|
@ -289,13 +293,15 @@ class CollectionWrapper
|
|||
*/
|
||||
private function ensureChunksIndex()
|
||||
{
|
||||
$expectedIndex = ['files_id' => 1, 'n' => 1];
|
||||
|
||||
foreach ($this->chunksCollection->listIndexes() as $index) {
|
||||
if ($index->isUnique() && $index->getKey() === ['files_id' => 1, 'n' => 1]) {
|
||||
if ($index->isUnique() && $this->indexKeysMatch($expectedIndex, $index->getKey())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->chunksCollection->createIndex(['files_id' => 1, 'n' => 1], ['unique' => true]);
|
||||
$this->chunksCollection->createIndex($expectedIndex, ['unique' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,13 +309,15 @@ class CollectionWrapper
|
|||
*/
|
||||
private function ensureFilesIndex()
|
||||
{
|
||||
$expectedIndex = ['filename' => 1, 'uploadDate' => 1];
|
||||
|
||||
foreach ($this->filesCollection->listIndexes() as $index) {
|
||||
if ($index->getKey() === ['filename' => 1, 'uploadDate' => 1]) {
|
||||
if ($this->indexKeysMatch($expectedIndex, $index->getKey())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->filesCollection->createIndex(['filename' => 1, 'uploadDate' => 1]);
|
||||
$this->filesCollection->createIndex($expectedIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -334,6 +342,36 @@ class CollectionWrapper
|
|||
$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) {
|
||||
list($expectedKey, $actualKey) = $key;
|
||||
list($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.
|
||||
*
|
||||
|
|
46
cache/stores/mongodb/MongoDB/GridFS/Exception/StreamException.php
vendored
Normal file
46
cache/stores/mongodb/MongoDB/GridFS/Exception/StreamException.php
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
namespace MongoDB\GridFS;
|
||||
|
||||
use Exception;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use stdClass;
|
||||
use Throwable;
|
||||
use function explode;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
|
@ -57,6 +57,14 @@ class StreamWrapper
|
|||
/** @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.
|
||||
*
|
||||
|
@ -88,6 +96,10 @@ class StreamWrapper
|
|||
*/
|
||||
public function stream_close()
|
||||
{
|
||||
if (! $this->stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->stream->close();
|
||||
}
|
||||
|
||||
|
@ -150,7 +162,7 @@ class StreamWrapper
|
|||
|
||||
try {
|
||||
return $this->stream->readBytes($length);
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
trigger_error(sprintf('%s: %s', get_class($e), $e->getMessage()), E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
|
@ -247,7 +259,7 @@ class StreamWrapper
|
|||
|
||||
try {
|
||||
return $this->stream->writeBytes($data);
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
trigger_error(sprintf('%s: %s', get_class($e), $e->getMessage()), E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
|
|
|
@ -114,15 +114,15 @@ class WritableStream
|
|||
throw InvalidArgumentException::invalidType('"aliases" option', $options['aliases'], 'array of strings');
|
||||
}
|
||||
|
||||
if (isset($options['chunkSizeBytes']) && ! is_integer($options['chunkSizeBytes'])) {
|
||||
if (! is_integer($options['chunkSizeBytes'])) {
|
||||
throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer');
|
||||
}
|
||||
|
||||
if (isset($options['chunkSizeBytes']) && $options['chunkSizeBytes'] < 1) {
|
||||
if ($options['chunkSizeBytes'] < 1) {
|
||||
throw new InvalidArgumentException(sprintf('Expected "chunkSizeBytes" option to be >= 1, %d given', $options['chunkSizeBytes']));
|
||||
}
|
||||
|
||||
if (isset($options['disableMD5']) && ! is_bool($options['disableMD5'])) {
|
||||
if (! is_bool($options['disableMD5'])) {
|
||||
throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean');
|
||||
}
|
||||
|
||||
|
|
|
@ -55,8 +55,8 @@ class MapReduceResult implements IteratorAggregate
|
|||
public function __construct(callable $getIterator, stdClass $result)
|
||||
{
|
||||
$this->getIterator = $getIterator;
|
||||
$this->executionTimeMS = (integer) $result->timeMillis;
|
||||
$this->counts = (array) $result->counts;
|
||||
$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 : [];
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
namespace MongoDB\Model;
|
||||
|
||||
use Countable;
|
||||
use Generator;
|
||||
use Iterator;
|
||||
use IteratorIterator;
|
||||
use Traversable;
|
||||
use function count;
|
||||
use function current;
|
||||
|
@ -41,7 +41,7 @@ class CachingIterator implements Countable, Iterator
|
|||
/** @var array */
|
||||
private $items = [];
|
||||
|
||||
/** @var Generator */
|
||||
/** @var IteratorIterator */
|
||||
private $iterator;
|
||||
|
||||
/** @var boolean */
|
||||
|
@ -52,16 +52,17 @@ class CachingIterator implements Countable, Iterator
|
|||
|
||||
/**
|
||||
* Initialize the iterator and stores the first item in the cache. This
|
||||
* effectively rewinds the Traversable and the wrapping Generator, which
|
||||
* will execute up to its first yield statement. Additionally, this mimics
|
||||
* behavior of the SPL iterators and allows users to omit an explicit call
|
||||
* to rewind() before using the other methods.
|
||||
* 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 = $this->wrapTraversable($traversable);
|
||||
$this->iterator = new IteratorIterator($traversable);
|
||||
|
||||
$this->iterator->rewind();
|
||||
$this->storeCurrentItem();
|
||||
}
|
||||
|
||||
|
@ -101,8 +102,12 @@ class CachingIterator implements Countable, Iterator
|
|||
public function next()
|
||||
{
|
||||
if (! $this->iteratorExhausted) {
|
||||
$this->iteratorAdvanced = true;
|
||||
$this->iterator->next();
|
||||
|
||||
$this->storeCurrentItem();
|
||||
|
||||
$this->iteratorExhausted = ! $this->iterator->valid();
|
||||
}
|
||||
|
||||
next($this->items);
|
||||
|
@ -156,20 +161,4 @@ class CachingIterator implements Countable, Iterator
|
|||
|
||||
$this->items[$key] = $this->iterator->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the Traversable with a Generator.
|
||||
*
|
||||
* @param Traversable $traversable
|
||||
* @return Generator
|
||||
*/
|
||||
private function wrapTraversable(Traversable $traversable)
|
||||
{
|
||||
foreach ($traversable as $key => $value) {
|
||||
yield $key => $value;
|
||||
$this->iteratorAdvanced = true;
|
||||
}
|
||||
|
||||
$this->iteratorExhausted = true;
|
||||
}
|
||||
}
|
||||
|
|
88
cache/stores/mongodb/MongoDB/Model/CallbackIterator.php
vendored
Normal file
88
cache/stores/mongodb/MongoDB/Model/CallbackIterator.php
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace MongoDB\Model;
|
||||
|
||||
use Closure;
|
||||
use Iterator;
|
||||
use IteratorIterator;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Iterator to apply a callback before returning an element
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CallbackIterator implements Iterator
|
||||
{
|
||||
/** @var Closure */
|
||||
private $callback;
|
||||
|
||||
/** @var IteratorIterator */
|
||||
private $iterator;
|
||||
|
||||
public function __construct(Traversable $traversable, Closure $callback)
|
||||
{
|
||||
$this->iterator = new IteratorIterator($traversable);
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://php.net/iterator.current
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return ($this->callback)($this->iterator->current());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://php.net/iterator.key
|
||||
* @return mixed
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->iterator->key();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://php.net/iterator.next
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->iterator->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://php.net/iterator.rewind
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->iterator->rewind();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://php.net/iterator.valid
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->iterator->valid();
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ 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;
|
||||
|
@ -63,6 +64,9 @@ class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber
|
|||
/** @var array|object|null */
|
||||
private $resumeToken;
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param Cursor $cursor
|
||||
|
@ -90,6 +94,7 @@ class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber
|
|||
$this->isRewindNop = ($firstBatchSize === 0);
|
||||
$this->postBatchResumeToken = $postBatchResumeToken;
|
||||
$this->resumeToken = $initialResumeToken;
|
||||
$this->server = $cursor->getServer();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -152,6 +157,14 @@ class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber
|
|||
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
|
||||
|
@ -230,8 +243,8 @@ class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber
|
|||
}
|
||||
|
||||
$resumeToken = is_array($document)
|
||||
? (isset($document['_id']) ? $document['_id'] : null)
|
||||
: (isset($document->_id) ? $document->_id : null);
|
||||
? ($document['_id'] ?? null)
|
||||
: ($document->_id ?? null);
|
||||
|
||||
if (! isset($resumeToken)) {
|
||||
$this->isValid = false;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
namespace MongoDB\Model;
|
||||
|
||||
use IteratorIterator;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* CollectionInfoIterator for listCollections command results.
|
||||
|
@ -32,6 +33,19 @@ use IteratorIterator;
|
|||
*/
|
||||
class CollectionInfoCommandIterator extends IteratorIterator implements CollectionInfoIterator
|
||||
{
|
||||
/** @var string|null */
|
||||
private $databaseName;
|
||||
|
||||
/**
|
||||
* @param string|null $databaseName
|
||||
*/
|
||||
public function __construct(Traversable $iterator, $databaseName = null)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->databaseName = $databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element as a CollectionInfo instance.
|
||||
*
|
||||
|
@ -41,6 +55,12 @@ class CollectionInfoCommandIterator extends IteratorIterator implements Collecti
|
|||
*/
|
||||
public function current()
|
||||
{
|
||||
return new CollectionInfo(parent::current());
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class DatabaseInfo implements ArrayAccess
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the collection info as an array.
|
||||
* Return the database info as an array.
|
||||
*
|
||||
* @see http://php.net/oop5.magic#language.oop5.magic.debuginfo
|
||||
* @return array
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
namespace MongoDB\Model;
|
||||
|
||||
use IteratorIterator;
|
||||
use Traversable;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* IndexInfoIterator for both listIndexes command and legacy query results.
|
||||
|
@ -34,6 +36,19 @@ use IteratorIterator;
|
|||
*/
|
||||
class IndexInfoIteratorIterator extends IteratorIterator implements IndexInfoIterator
|
||||
{
|
||||
/** @var string|null $ns */
|
||||
private $ns;
|
||||
|
||||
/**
|
||||
* @param string|null $ns
|
||||
*/
|
||||
public function __construct(Traversable $iterator, $ns = null)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->ns = $ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element as an IndexInfo instance.
|
||||
*
|
||||
|
@ -43,6 +58,12 @@ class IndexInfoIteratorIterator extends IteratorIterator implements IndexInfoIte
|
|||
*/
|
||||
public function current()
|
||||
{
|
||||
return new IndexInfo(parent::current());
|
||||
$info = parent::current();
|
||||
|
||||
if (! array_key_exists('ns', $info) && $this->ns !== null) {
|
||||
$info['ns'] = $this->ns;
|
||||
}
|
||||
|
||||
return new IndexInfo($info);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,14 +62,6 @@ class IndexInput implements Serializable
|
|||
}
|
||||
}
|
||||
|
||||
if (! isset($index['ns'])) {
|
||||
throw new InvalidArgumentException('Required "ns" option is missing from index specification');
|
||||
}
|
||||
|
||||
if (! is_string($index['ns'])) {
|
||||
throw InvalidArgumentException::invalidType('"ns" option', $index['ns'], 'string');
|
||||
}
|
||||
|
||||
if (! isset($index['name'])) {
|
||||
$index['name'] = generate_index_name($index['key']);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ use function sprintf;
|
|||
* @see \MongoDB\Collection::aggregate()
|
||||
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
|
||||
*/
|
||||
class Aggregate implements Executable
|
||||
class Aggregate implements Executable, Explainable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $wireVersionForCollation = 5;
|
||||
|
@ -285,9 +285,12 @@ class Aggregate implements Executable
|
|||
}
|
||||
|
||||
$hasExplain = ! empty($this->options['explain']);
|
||||
$hasWriteStage = is_last_pipeline_operator_write($this->pipeline);
|
||||
$hasWriteStage = $this->hasWriteStage();
|
||||
|
||||
$command = $this->createCommand($server, $hasWriteStage);
|
||||
$command = new Command(
|
||||
$this->createCommandDocument($server, $hasWriteStage),
|
||||
$this->createCommandOptions()
|
||||
);
|
||||
$options = $this->createOptions($hasWriteStage, $hasExplain);
|
||||
|
||||
$cursor = $hasWriteStage && ! $hasExplain
|
||||
|
@ -315,20 +318,17 @@ class Aggregate implements Executable
|
|||
return new ArrayIterator($result->result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the aggregate command.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param boolean $hasWriteStage
|
||||
* @return Command
|
||||
*/
|
||||
private function createCommand(Server $server, $hasWriteStage)
|
||||
public function getCommandDocument(Server $server)
|
||||
{
|
||||
return $this->createCommandDocument($server, $this->hasWriteStage());
|
||||
}
|
||||
|
||||
private function createCommandDocument(Server $server, bool $hasWriteStage) : array
|
||||
{
|
||||
$cmd = [
|
||||
'aggregate' => isset($this->collectionName) ? $this->collectionName : 1,
|
||||
'aggregate' => $this->collectionName ?? 1,
|
||||
'pipeline' => $this->pipeline,
|
||||
];
|
||||
$cmdOptions = [];
|
||||
|
||||
$cmd['allowDiskUse'] = $this->options['allowDiskUse'];
|
||||
|
||||
|
@ -352,10 +352,6 @@ class Aggregate implements Executable
|
|||
$cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint'];
|
||||
}
|
||||
|
||||
if (isset($this->options['maxAwaitTimeMS'])) {
|
||||
$cmdOptions['maxAwaitTimeMS'] = $this->options['maxAwaitTimeMS'];
|
||||
}
|
||||
|
||||
if ($this->options['useCursor']) {
|
||||
/* Ignore batchSize if pipeline includes an $out or $merge stage, as
|
||||
* no documents will be returned and sending a batchSize of zero
|
||||
|
@ -365,7 +361,18 @@ class Aggregate implements Executable
|
|||
: new stdClass();
|
||||
}
|
||||
|
||||
return new Command($cmd, $cmdOptions);
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
private function createCommandOptions() : array
|
||||
{
|
||||
$cmdOptions = [];
|
||||
|
||||
if (isset($this->options['maxAwaitTimeMS'])) {
|
||||
$cmdOptions['maxAwaitTimeMS'] = $this->options['maxAwaitTimeMS'];
|
||||
}
|
||||
|
||||
return $cmdOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -399,4 +406,9 @@ class Aggregate implements Executable
|
|||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function hasWriteStage() : bool
|
||||
{
|
||||
return is_last_pipeline_operator_write($this->pipeline);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ 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;
|
||||
|
||||
|
@ -47,6 +48,9 @@ class CreateIndexes implements Executable
|
|||
/** @var integer */
|
||||
private static $wireVersionForWriteConcern = 5;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForCommitQuorum = 9;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
|
@ -67,6 +71,10 @@ class CreateIndexes implements Executable
|
|||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
|
@ -102,10 +110,6 @@ class CreateIndexes implements Executable
|
|||
throw InvalidArgumentException::invalidType(sprintf('$index[%d]', $i), $index, 'array');
|
||||
}
|
||||
|
||||
if (! isset($index['ns'])) {
|
||||
$index['ns'] = $databaseName . '.' . $collectionName;
|
||||
}
|
||||
|
||||
if (isset($index['collation'])) {
|
||||
$this->isCollationUsed = true;
|
||||
}
|
||||
|
@ -115,6 +119,10 @@ class CreateIndexes implements Executable
|
|||
$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');
|
||||
}
|
||||
|
@ -202,6 +210,16 @@ class CreateIndexes implements Executable
|
|||
'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'];
|
||||
}
|
||||
|
||||
if (isset($this->options['maxTimeMS'])) {
|
||||
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ use MongoDB\Exception\InvalidArgumentException;
|
|||
use MongoDB\Exception\UnsupportedException;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function MongoDB\server_supports_feature;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +44,9 @@ class Delete implements Executable, Explainable
|
|||
/** @var integer */
|
||||
private static $wireVersionForCollation = 5;
|
||||
|
||||
/** @var int */
|
||||
private static $wireVersionForHintServerSideError = 5;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
|
@ -68,6 +72,13 @@ class Delete implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
|
@ -97,6 +108,10 @@ class Delete implements Executable, Explainable
|
|||
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);
|
||||
}
|
||||
|
@ -130,6 +145,13 @@ class Delete implements Executable, Explainable
|
|||
throw UnsupportedException::collationNotSupported();
|
||||
}
|
||||
|
||||
/* Server versions >= 3.4.0 raise errors for unknown update
|
||||
* options. For previous versions, the CRUD spec requires a client-side
|
||||
* error. */
|
||||
if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHintServerSideError)) {
|
||||
throw UnsupportedException::hintNotSupported();
|
||||
}
|
||||
|
||||
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
|
||||
if ($inTransaction && isset($this->options['writeConcern'])) {
|
||||
throw UnsupportedException::writeConcernNotSupportedInTransaction();
|
||||
|
@ -170,6 +192,10 @@ class Delete implements Executable, Explainable
|
|||
$deleteOptions['collation'] = (object) $this->options['collation'];
|
||||
}
|
||||
|
||||
if (isset($this->options['hint'])) {
|
||||
$deleteOptions['hint'] = $this->options['hint'];
|
||||
}
|
||||
|
||||
return $deleteOptions;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,13 @@ class DeleteMany implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
|
|
|
@ -45,6 +45,13 @@ class DeleteOne implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
|
||||
use MongoDB\Driver\Exception\CommandException;
|
||||
use MongoDB\Driver\Server;
|
||||
use MongoDB\Driver\Session;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
|
@ -38,6 +38,9 @@ use function MongoDB\server_supports_feature;
|
|||
*/
|
||||
class DropCollection implements Executable
|
||||
{
|
||||
/** @var integer */
|
||||
private static $errorCodeNamespaceNotFound = 26;
|
||||
|
||||
/** @var string */
|
||||
private static $errorMessageNamespaceNotFound = 'ns not found';
|
||||
|
||||
|
@ -122,13 +125,13 @@ class DropCollection implements Executable
|
|||
|
||||
try {
|
||||
$cursor = $server->executeWriteCommand($this->databaseName, $command, $this->createOptions());
|
||||
} catch (DriverRuntimeException $e) {
|
||||
} catch (CommandException $e) {
|
||||
/* The server may return an error if the collection does not exist.
|
||||
* Check for an error message (unfortunately, there isn't a code)
|
||||
* and NOP instead of throwing.
|
||||
*/
|
||||
if ($e->getMessage() === self::$errorMessageNamespaceNotFound) {
|
||||
return (object) ['ok' => 0, 'errmsg' => self::$errorMessageNamespaceNotFound];
|
||||
* Check for an error code (or message for pre-3.2 servers) and
|
||||
* return the command reply instead of throwing. */
|
||||
if ($e->getCode() === self::$errorCodeNamespaceNotFound ||
|
||||
$e->getMessage() === self::$errorMessageNamespaceNotFound) {
|
||||
return $e->getResultDocument();
|
||||
}
|
||||
|
||||
throw $e;
|
||||
|
|
|
@ -41,6 +41,9 @@ class Explain implements Executable
|
|||
const VERBOSITY_EXEC_STATS = 'executionStats';
|
||||
const VERBOSITY_QUERY = 'queryPlanner';
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForAggregate = 7;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForDistinct = 4;
|
||||
|
||||
|
@ -108,6 +111,10 @@ class Explain implements Executable
|
|||
throw UnsupportedException::explainNotSupported();
|
||||
}
|
||||
|
||||
if ($this->explainable instanceof Aggregate && ! server_supports_feature($server, self::$wireVersionForAggregate)) {
|
||||
throw UnsupportedException::explainNotSupported();
|
||||
}
|
||||
|
||||
$cmd = ['explain' => $this->explainable->getCommandDocument($server)];
|
||||
|
||||
if (isset($this->options['verbosity'])) {
|
||||
|
|
|
@ -20,8 +20,8 @@ namespace MongoDB\Operation;
|
|||
use MongoDB\Driver\Server;
|
||||
|
||||
/**
|
||||
* Explainable interface for explainable operations (count, distinct, find,
|
||||
* findAndModify, delete, and update).
|
||||
* Explainable interface for explainable operations (aggregate, count, distinct,
|
||||
* find, findAndModify, delete, and update).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
|
19
cache/stores/mongodb/MongoDB/Operation/Find.php
vendored
19
cache/stores/mongodb/MongoDB/Operation/Find.php
vendored
|
@ -55,6 +55,9 @@ class Find implements Executable, Explainable
|
|||
/** @var integer */
|
||||
private static $wireVersionForReadConcern = 4;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForAllowDiskUseServerSideError = 4;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
|
@ -72,6 +75,10 @@ class Find implements Executable, Explainable
|
|||
*
|
||||
* 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. The default is false.
|
||||
*
|
||||
* * allowPartialResults (boolean): Get partial results from a mongos if
|
||||
* some shards are inaccessible (instead of throwing an error).
|
||||
*
|
||||
|
@ -120,7 +127,7 @@ class Find implements Executable, Explainable
|
|||
* Set this option to prevent that.
|
||||
*
|
||||
* * oplogReplay (boolean): Internal replication use only. The driver
|
||||
* should not set this.
|
||||
* should not set this. This option is deprecated as of MongoDB 4.4.
|
||||
*
|
||||
* * projection (document): Limits the fields to return for the matching
|
||||
* document.
|
||||
|
@ -169,6 +176,10 @@ class Find implements Executable, Explainable
|
|||
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');
|
||||
}
|
||||
|
@ -314,6 +325,10 @@ class Find implements Executable, Explainable
|
|||
throw UnsupportedException::readConcernNotSupported();
|
||||
}
|
||||
|
||||
if (isset($this->options['allowDiskUse']) && ! server_supports_feature($server, self::$wireVersionForAllowDiskUseServerSideError)) {
|
||||
throw UnsupportedException::allowDiskUseNotSupported();
|
||||
}
|
||||
|
||||
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
|
||||
if ($inTransaction && isset($this->options['readConcern'])) {
|
||||
throw UnsupportedException::readConcernNotSupportedInTransaction();
|
||||
|
@ -416,7 +431,7 @@ class Find implements Executable, Explainable
|
|||
}
|
||||
}
|
||||
|
||||
foreach (['allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) {
|
||||
foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) {
|
||||
if (isset($this->options[$option])) {
|
||||
$options[$option] = $this->options[$option];
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ 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\server_supports_feature;
|
||||
|
@ -54,6 +55,12 @@ class FindAndModify implements Executable, Explainable
|
|||
/** @var integer */
|
||||
private static $wireVersionForDocumentLevelValidation = 4;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForHint = 9;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForHintServerSideError = 8;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForWriteConcern = 4;
|
||||
|
||||
|
@ -91,6 +98,13 @@ class FindAndModify implements Executable, Explainable
|
|||
* * 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.
|
||||
*
|
||||
|
@ -153,6 +167,10 @@ class FindAndModify implements Executable, Explainable
|
|||
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');
|
||||
}
|
||||
|
@ -226,6 +244,14 @@ class FindAndModify implements Executable, Explainable
|
|||
throw UnsupportedException::collationNotSupported();
|
||||
}
|
||||
|
||||
/* Server versions >= 4.1.10 raise errors for unknown findAndModify
|
||||
* options (SERVER-40005), but the CRUD spec requires client-side errors
|
||||
* for server versions < 4.2. For later versions, we'll rely on the
|
||||
* server to either utilize the option or report its own error. */
|
||||
if (isset($this->options['hint']) && ! $this->isHintSupported($server)) {
|
||||
throw UnsupportedException::hintNotSupported();
|
||||
}
|
||||
|
||||
if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) {
|
||||
throw UnsupportedException::writeConcernNotSupported();
|
||||
}
|
||||
|
@ -243,7 +269,7 @@ class FindAndModify implements Executable, Explainable
|
|||
|
||||
$result = current($cursor->toArray());
|
||||
|
||||
return isset($result->value) ? $result->value : null;
|
||||
return $result->value ?? null;
|
||||
}
|
||||
|
||||
public function getCommandDocument(Server $server)
|
||||
|
@ -280,12 +306,10 @@ class FindAndModify implements Executable, Explainable
|
|||
: (object) $this->options['update'];
|
||||
}
|
||||
|
||||
if (isset($this->options['arrayFilters'])) {
|
||||
$cmd['arrayFilters'] = $this->options['arrayFilters'];
|
||||
}
|
||||
|
||||
if (isset($this->options['maxTimeMS'])) {
|
||||
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
|
||||
foreach (['arrayFilters', 'hint', 'maxTimeMS'] as $option) {
|
||||
if (isset($this->options[$option])) {
|
||||
$cmd[$option] = $this->options[$option];
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($this->options['bypassDocumentValidation']) &&
|
||||
|
@ -317,4 +341,20 @@ class FindAndModify implements Executable, Explainable
|
|||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function isAcknowledgedWriteConcern() : bool
|
||||
{
|
||||
if (! isset($this->options['writeConcern'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->options['writeConcern']->getW() > 1 || $this->options['writeConcern']->getJournal();
|
||||
}
|
||||
|
||||
private function isHintSupported(Server $server) : bool
|
||||
{
|
||||
$requiredWireVersion = $this->isAcknowledgedWriteConcern() ? self::$wireVersionForHintServerSideError : self::$wireVersionForHint;
|
||||
|
||||
return server_supports_feature($server, $requiredWireVersion);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,13 @@ class FindOneAndDelete implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
|
|
|
@ -57,6 +57,13 @@ class FindOneAndReplace implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
|
|
|
@ -61,6 +61,13 @@ class FindOneAndUpdate implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
|
|
79
cache/stores/mongodb/MongoDB/Operation/ListCollectionNames.php
vendored
Normal file
79
cache/stores/mongodb/MongoDB/Operation/ListCollectionNames.php
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 http://docs.mongodb.org/manual/reference/command/listCollections/
|
||||
*/
|
||||
class ListCollectionNames implements Executable
|
||||
{
|
||||
/** @var ListCollectionsCommand */
|
||||
private $listCollections;
|
||||
|
||||
/**
|
||||
* Constructs a listCollections command.
|
||||
*
|
||||
* Supported options:
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
*
|
||||
* @param string $databaseName Database name
|
||||
* @param array $options Command options
|
||||
* @throws InvalidArgumentException for parameter/option parsing errors
|
||||
*/
|
||||
public function __construct($databaseName, array $options = [])
|
||||
{
|
||||
$this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => true] + $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the operation.
|
||||
*
|
||||
* @see Executable::execute()
|
||||
* @param Server $server
|
||||
* @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'];
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -17,17 +17,12 @@
|
|||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Command\ListCollections as ListCollectionsCommand;
|
||||
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\CollectionInfoCommandIterator;
|
||||
use MongoDB\Model\CollectionInfoIterator;
|
||||
use function is_array;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Operation for the listCollections command.
|
||||
|
@ -41,8 +36,8 @@ class ListCollections implements Executable
|
|||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
/** @var array */
|
||||
private $options;
|
||||
/** @var ListCollectionsCommand */
|
||||
private $listCollections;
|
||||
|
||||
/**
|
||||
* Constructs a listCollections command.
|
||||
|
@ -64,20 +59,8 @@ class ListCollections implements Executable
|
|||
*/
|
||||
public function __construct($databaseName, array $options = [])
|
||||
{
|
||||
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['session']) && ! $options['session'] instanceof Session) {
|
||||
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
|
||||
}
|
||||
|
||||
$this->databaseName = (string) $databaseName;
|
||||
$this->options = $options;
|
||||
$this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => false] + $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,52 +73,6 @@ class ListCollections implements Executable
|
|||
*/
|
||||
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 http://php.net/manual/en/mongodb-driver-server.executecommand.php
|
||||
* @return array
|
||||
*/
|
||||
private function createOptions()
|
||||
{
|
||||
$options = [];
|
||||
|
||||
if (isset($this->options['session'])) {
|
||||
$options['session'] = $this->options['session'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information for all collections in this database using the
|
||||
* listCollections command.
|
||||
*
|
||||
* @param Server $server
|
||||
* @return CollectionInfoCommandIterator
|
||||
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
|
||||
*/
|
||||
private function executeCommand(Server $server)
|
||||
{
|
||||
$cmd = ['listCollections' => 1];
|
||||
|
||||
if (! empty($this->options['filter'])) {
|
||||
$cmd['filter'] = (object) $this->options['filter'];
|
||||
}
|
||||
|
||||
if (isset($this->options['maxTimeMS'])) {
|
||||
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
|
||||
}
|
||||
|
||||
$cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions());
|
||||
$cursor->setTypeMap(['root' => 'array', 'document' => 'array']);
|
||||
|
||||
return new CollectionInfoCommandIterator(new CachingIterator($cursor));
|
||||
return new CollectionInfoCommandIterator($this->listCollections->execute($server), $this->databaseName);
|
||||
}
|
||||
}
|
||||
|
|
85
cache/stores/mongodb/MongoDB/Operation/ListDatabaseNames.php
vendored
Normal file
85
cache/stores/mongodb/MongoDB/Operation/ListDatabaseNames.php
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 http://docs.mongodb.org/manual/reference/command/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.
|
||||
*
|
||||
* * filter (document): Query by which to filter databases.
|
||||
*
|
||||
* For servers < 3.6, this option is ignored.
|
||||
*
|
||||
* * maxTimeMS (integer): The maximum amount of time to allow the query to
|
||||
* run.
|
||||
*
|
||||
* * session (MongoDB\Driver\Session): Client session.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
*
|
||||
* @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()
|
||||
* @param Server $server
|
||||
* @return Iterator
|
||||
* @throws UnexpectedValueException if the command response was malformed
|
||||
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
|
||||
*/
|
||||
public function execute(Server $server) : Iterator
|
||||
{
|
||||
$result = $this->listDatabases->execute($server);
|
||||
|
||||
return new ArrayIterator(array_column($result, 'name'));
|
||||
}
|
||||
}
|
|
@ -17,18 +17,13 @@
|
|||
|
||||
namespace MongoDB\Operation;
|
||||
|
||||
use MongoDB\Driver\Command;
|
||||
use MongoDB\Command\ListDatabases as ListDatabasesCommand;
|
||||
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\Model\DatabaseInfoIterator;
|
||||
use MongoDB\Model\DatabaseInfoLegacyIterator;
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_integer;
|
||||
use function is_object;
|
||||
|
||||
/**
|
||||
* Operation for the ListDatabases command.
|
||||
|
@ -39,14 +34,19 @@ use function is_object;
|
|||
*/
|
||||
class ListDatabases implements Executable
|
||||
{
|
||||
/** @var array */
|
||||
private $options;
|
||||
/** @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.
|
||||
*
|
||||
* * filter (document): Query by which to filter databases.
|
||||
*
|
||||
* For servers < 3.6, this option is ignored.
|
||||
|
@ -63,19 +63,7 @@ class ListDatabases implements Executable
|
|||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
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['session']) && ! $options['session'] instanceof Session) {
|
||||
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
$this->listDatabases = new ListDatabasesCommand(['nameOnly' => false] + $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,50 +77,6 @@ class ListDatabases implements Executable
|
|||
*/
|
||||
public function execute(Server $server)
|
||||
{
|
||||
$cmd = ['listDatabases' => 1];
|
||||
|
||||
if (! empty($this->options['filter'])) {
|
||||
$cmd['filter'] = (object) $this->options['filter'];
|
||||
}
|
||||
|
||||
if (isset($this->options['maxTimeMS'])) {
|
||||
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
|
||||
}
|
||||
|
||||
$cursor = $server->executeReadCommand('admin', new Command($cmd), $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 an Iterator instead of an array in case listDatabases is
|
||||
* eventually changed to return a command cursor, like the collection
|
||||
* and index enumeration commands. This makes the "totalSize" command
|
||||
* field inaccessible, but users can manually invoke the command if they
|
||||
* need that value.
|
||||
*/
|
||||
return new DatabaseInfoLegacyIterator($result['databases']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 http://php.net/manual/en/mongodb-driver-server.executecommand.php
|
||||
* @return array
|
||||
*/
|
||||
private function createOptions()
|
||||
{
|
||||
$options = [];
|
||||
|
||||
if (isset($this->options['session'])) {
|
||||
$options['session'] = $this->options['session'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
return new DatabaseInfoLegacyIterator($this->listDatabases->execute($server));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,6 +149,6 @@ class ListIndexes implements Executable
|
|||
|
||||
$cursor->setTypeMap(['root' => 'array', 'document' => 'array']);
|
||||
|
||||
return new IndexInfoIteratorIterator(new CachingIterator($cursor));
|
||||
return new IndexInfoIteratorIterator(new CachingIterator($cursor), $this->databaseName . '.' . $this->collectionName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ use function is_string;
|
|||
use function MongoDB\create_field_path_type_map;
|
||||
use function MongoDB\is_mapreduce_output_inline;
|
||||
use function MongoDB\server_supports_feature;
|
||||
use function trigger_error;
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Operation for the mapReduce command.
|
||||
|
@ -88,9 +90,15 @@ class MapReduce implements Executable
|
|||
* * 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
|
||||
|
@ -114,6 +122,9 @@ class MapReduce implements Executable
|
|||
* * 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.
|
||||
*
|
||||
|
@ -242,6 +253,21 @@ class MapReduce implements Executable
|
|||
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 = (string) $databaseName;
|
||||
$this->collectionName = (string) $collectionName;
|
||||
$this->map = $map;
|
||||
|
@ -310,6 +336,27 @@ class MapReduce implements Executable
|
|||
return new MapReduceResult($getIterator, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array|object $out
|
||||
* @return void
|
||||
*/
|
||||
private function checkOutDeprecations($out)
|
||||
{
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -126,7 +126,7 @@ class ModifyCollection implements Executable
|
|||
/**
|
||||
* Create options for executing the command.
|
||||
*
|
||||
* @see http://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php
|
||||
* @see http://php.net/manual/en/mongodb-driver-server.executewritecommand.php
|
||||
* @return array
|
||||
*/
|
||||
private function createOptions()
|
||||
|
|
|
@ -55,6 +55,13 @@ class ReplaceOne implements Executable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
|
|
|
@ -28,6 +28,7 @@ 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\server_supports_feature;
|
||||
|
@ -52,6 +53,9 @@ class Update implements Executable, Explainable
|
|||
/** @var integer */
|
||||
private static $wireVersionForDocumentLevelValidation = 4;
|
||||
|
||||
/** @var integer */
|
||||
private static $wireVersionForHintServerSideError = 5;
|
||||
|
||||
/** @var string */
|
||||
private $databaseName;
|
||||
|
||||
|
@ -89,6 +93,13 @@ class Update implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
|
@ -137,6 +148,10 @@ class Update implements Executable, Explainable
|
|||
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');
|
||||
}
|
||||
|
@ -187,6 +202,13 @@ class Update implements Executable, Explainable
|
|||
throw UnsupportedException::collationNotSupported();
|
||||
}
|
||||
|
||||
/* Server versions >= 3.4.0 raise errors for unknown update
|
||||
* options. For previous versions, the CRUD spec requires a client-side
|
||||
* error. */
|
||||
if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHintServerSideError)) {
|
||||
throw UnsupportedException::hintNotSupported();
|
||||
}
|
||||
|
||||
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
|
||||
if ($inTransaction && isset($this->options['writeConcern'])) {
|
||||
throw UnsupportedException::writeConcernNotSupportedInTransaction();
|
||||
|
@ -261,8 +283,10 @@ class Update implements Executable, Explainable
|
|||
'upsert' => $this->options['upsert'],
|
||||
];
|
||||
|
||||
if (isset($this->options['arrayFilters'])) {
|
||||
$updateOptions['arrayFilters'] = $this->options['arrayFilters'];
|
||||
foreach (['arrayFilters', 'hint'] as $option) {
|
||||
if (isset($this->options[$option])) {
|
||||
$updateOptions[$option] = $this->options[$option];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->options['collation'])) {
|
||||
|
|
|
@ -61,6 +61,13 @@ class UpdateMany implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
|
|
|
@ -61,6 +61,13 @@ class UpdateOne implements Executable, Explainable
|
|||
* This is not supported for server versions < 3.4 and will result in an
|
||||
* exception at execution time if used.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* Sessions are not supported for server versions < 3.6.
|
||||
|
|
|
@ -178,10 +178,14 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
|
|||
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
|
||||
];
|
||||
|
||||
if (isset($options['fullDocument']) && ! is_string($options['fullDocument'])) {
|
||||
if (! is_string($options['fullDocument'])) {
|
||||
throw InvalidArgumentException::invalidType('"fullDocument" option', $options['fullDocument'], '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');
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace MongoDB\Operation;
|
|||
use Exception;
|
||||
use MongoDB\Driver\Exception\RuntimeException;
|
||||
use MongoDB\Driver\Session;
|
||||
use Throwable;
|
||||
use function call_user_func;
|
||||
use function time;
|
||||
|
||||
|
@ -63,7 +64,7 @@ class WithTransaction
|
|||
|
||||
try {
|
||||
call_user_func($this->callback, $session);
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
if ($session->isInTransaction()) {
|
||||
$session->abortTransaction();
|
||||
}
|
||||
|
|
2
cache/stores/mongodb/lib.php
vendored
2
cache/stores/mongodb/lib.php
vendored
|
@ -158,7 +158,7 @@ class cachestore_mongodb extends cache_store implements cache_is_configurable {
|
|||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return version_compare(phpversion('mongodb'), '1.5', 'ge');
|
||||
return version_compare(phpversion('mongodb'), '1.8', 'ge');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
3
cache/stores/mongodb/readme_moodle.txt
vendored
3
cache/stores/mongodb/readme_moodle.txt
vendored
|
@ -7,5 +7,6 @@ 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.5.1) requires PHP mongodb extension >= 1.6.0
|
||||
This version (1.8.0) requires PHP mongodb extension >= 1.8.0
|
||||
|
|
2
cache/stores/mongodb/thirdpartylibs.xml
vendored
2
cache/stores/mongodb/thirdpartylibs.xml
vendored
|
@ -4,7 +4,7 @@
|
|||
<location>MongoDB</location>
|
||||
<name>MongoDB PHP Library</name>
|
||||
<license>Apache</license>
|
||||
<version>1.5.1</version>
|
||||
<version>1.8.0</version>
|
||||
<licenseversion>2.0</licenseversion>
|
||||
</library>
|
||||
</libraries>
|
Loading…
Add table
Add a link
Reference in a new issue