Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 401] [Versions 39 and 311]

   1  <?php
   2  /*
   3   * Copyright 2015-2017 MongoDB, Inc.
   4   *
   5   * Licensed under the Apache License, Version 2.0 (the "License");
   6   * you may not use this file except in compliance with the License.
   7   * You may obtain a copy of the License at
   8   *
   9   *   http://www.apache.org/licenses/LICENSE-2.0
  10   *
  11   * Unless required by applicable law or agreed to in writing, software
  12   * distributed under the License is distributed on an "AS IS" BASIS,
  13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14   * See the License for the specific language governing permissions and
  15   * limitations under the License.
  16   */
  17  
  18  namespace MongoDB;
  19  
  20  use Iterator;
  21  use Jean85\PrettyVersions;
  22  use MongoDB\Driver\ClientEncryption;
  23  use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
  24  use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
  25  use MongoDB\Driver\Manager;
  26  use MongoDB\Driver\ReadConcern;
  27  use MongoDB\Driver\ReadPreference;
  28  use MongoDB\Driver\Session;
  29  use MongoDB\Driver\WriteConcern;
  30  use MongoDB\Exception\InvalidArgumentException;
  31  use MongoDB\Exception\UnexpectedValueException;
  32  use MongoDB\Exception\UnsupportedException;
  33  use MongoDB\Model\BSONArray;
  34  use MongoDB\Model\BSONDocument;
  35  use MongoDB\Model\DatabaseInfoIterator;
  36  use MongoDB\Operation\DropDatabase;
  37  use MongoDB\Operation\ListDatabaseNames;
  38  use MongoDB\Operation\ListDatabases;
  39  use MongoDB\Operation\Watch;
  40  use Throwable;
  41  use function is_array;
  42  use function is_string;
  43  
  44  class Client
  45  {
  46      /** @var array */
  47      private static $defaultTypeMap = [
  48          'array' => BSONArray::class,
  49          'document' => BSONDocument::class,
  50          'root' => BSONDocument::class,
  51      ];
  52  
  53      /** @var integer */
  54      private static $wireVersionForReadConcern = 4;
  55  
  56      /** @var integer */
  57      private static $wireVersionForWritableCommandWriteConcern = 5;
  58  
  59      /** @var string */
  60      private static $handshakeSeparator = ' / ';
  61  
  62      /** @var string|null */
  63      private static $version;
  64  
  65      /** @var Manager */
  66      private $manager;
  67  
  68      /** @var ReadConcern */
  69      private $readConcern;
  70  
  71      /** @var ReadPreference */
  72      private $readPreference;
  73  
  74      /** @var string */
  75      private $uri;
  76  
  77      /** @var array */
  78      private $typeMap;
  79  
  80      /** @var WriteConcern */
  81      private $writeConcern;
  82  
  83      /**
  84       * Constructs a new Client instance.
  85       *
  86       * This is the preferred class for connecting to a MongoDB server or
  87       * cluster of servers. It serves as a gateway for accessing individual
  88       * databases and collections.
  89       *
  90       * Supported driver-specific options:
  91       *
  92       *  * typeMap (array): Default type map for cursors and BSON documents.
  93       *
  94       * Other options are documented in MongoDB\Driver\Manager::__construct().
  95       *
  96       * @see http://docs.mongodb.org/manual/reference/connection-string/
  97       * @see http://php.net/manual/en/mongodb-driver-manager.construct.php
  98       * @see http://php.net/manual/en/mongodb.persistence.php#mongodb.persistence.typemaps
  99       * @param string $uri           MongoDB connection string
 100       * @param array  $uriOptions    Additional connection string options
 101       * @param array  $driverOptions Driver-specific options
 102       * @throws InvalidArgumentException for parameter/option parsing errors
 103       * @throws DriverInvalidArgumentException for parameter/option parsing errors in the driver
 104       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 105       */
 106      public function __construct($uri = 'mongodb://127.0.0.1/', array $uriOptions = [], array $driverOptions = [])
 107      {
 108          $driverOptions += ['typeMap' => self::$defaultTypeMap];
 109  
 110          if (! is_array($driverOptions['typeMap'])) {
 111              throw InvalidArgumentException::invalidType('"typeMap" driver option', $driverOptions['typeMap'], 'array');
 112          }
 113  
 114          if (isset($driverOptions['autoEncryption']['keyVaultClient'])) {
 115              if ($driverOptions['autoEncryption']['keyVaultClient'] instanceof self) {
 116                  $driverOptions['autoEncryption']['keyVaultClient'] = $driverOptions['autoEncryption']['keyVaultClient']->manager;
 117              } elseif (! $driverOptions['autoEncryption']['keyVaultClient'] instanceof Manager) {
 118                  throw InvalidArgumentException::invalidType('"keyVaultClient" autoEncryption option', $driverOptions['autoEncryption']['keyVaultClient'], [self::class, Manager::class]);
 119              }
 120          }
 121  
 122          $driverOptions['driver'] = $this->mergeDriverInfo($driverOptions['driver'] ?? []);
 123  
 124          $this->uri = (string) $uri;
 125          $this->typeMap = $driverOptions['typeMap'] ?? null;
 126  
 127          unset($driverOptions['typeMap']);
 128  
 129          $this->manager = new Manager($uri, $uriOptions, $driverOptions);
 130          $this->readConcern = $this->manager->getReadConcern();
 131          $this->readPreference = $this->manager->getReadPreference();
 132          $this->writeConcern = $this->manager->getWriteConcern();
 133      }
 134  
 135      /**
 136       * Return internal properties for debugging purposes.
 137       *
 138       * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo
 139       * @return array
 140       */
 141      public function __debugInfo()
 142      {
 143          return [
 144              'manager' => $this->manager,
 145              'uri' => $this->uri,
 146              'typeMap' => $this->typeMap,
 147              'writeConcern' => $this->writeConcern,
 148          ];
 149      }
 150  
 151      /**
 152       * Select a database.
 153       *
 154       * Note: databases whose names contain special characters (e.g. "-") may
 155       * be selected with complex syntax (e.g. $client->{"that-database"}) or
 156       * {@link selectDatabase()}.
 157       *
 158       * @see http://php.net/oop5.overloading#object.get
 159       * @see http://php.net/types.string#language.types.string.parsing.complex
 160       * @param string $databaseName Name of the database to select
 161       * @return Database
 162       */
 163      public function __get($databaseName)
 164      {
 165          return $this->selectDatabase($databaseName);
 166      }
 167  
 168      /**
 169       * Return the connection string (i.e. URI).
 170       *
 171       * @return string
 172       */
 173      public function __toString()
 174      {
 175          return $this->uri;
 176      }
 177  
 178      /**
 179       * Returns a ClientEncryption instance for explicit encryption and decryption
 180       *
 181       * @param array $options Encryption options
 182       *
 183       * @return ClientEncryption
 184       */
 185      public function createClientEncryption(array $options)
 186      {
 187          if (isset($options['keyVaultClient'])) {
 188              if ($options['keyVaultClient'] instanceof self) {
 189                  $options['keyVaultClient'] = $options['keyVaultClient']->manager;
 190              } elseif (! $options['keyVaultClient'] instanceof Manager) {
 191                  throw InvalidArgumentException::invalidType('"keyVaultClient" option', $options['keyVaultClient'], [self::class, Manager::class]);
 192              }
 193          }
 194  
 195          return $this->manager->createClientEncryption($options);
 196      }
 197  
 198      /**
 199       * Drop a database.
 200       *
 201       * @see DropDatabase::__construct() for supported options
 202       * @param string $databaseName Database name
 203       * @param array  $options      Additional options
 204       * @return array|object Command result document
 205       * @throws UnsupportedException if options are unsupported on the selected server
 206       * @throws InvalidArgumentException for parameter/option parsing errors
 207       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 208       */
 209      public function dropDatabase($databaseName, array $options = [])
 210      {
 211          if (! isset($options['typeMap'])) {
 212              $options['typeMap'] = $this->typeMap;
 213          }
 214  
 215          $server = select_server($this->manager, $options);
 216  
 217          if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) {
 218              $options['writeConcern'] = $this->writeConcern;
 219          }
 220  
 221          $operation = new DropDatabase($databaseName, $options);
 222  
 223          return $operation->execute($server);
 224      }
 225  
 226      /**
 227       * Return the Manager.
 228       *
 229       * @return Manager
 230       */
 231      public function getManager()
 232      {
 233          return $this->manager;
 234      }
 235  
 236      /**
 237       * Return the read concern for this client.
 238       *
 239       * @see http://php.net/manual/en/mongodb-driver-readconcern.isdefault.php
 240       * @return ReadConcern
 241       */
 242      public function getReadConcern()
 243      {
 244          return $this->readConcern;
 245      }
 246  
 247      /**
 248       * Return the read preference for this client.
 249       *
 250       * @return ReadPreference
 251       */
 252      public function getReadPreference()
 253      {
 254          return $this->readPreference;
 255      }
 256  
 257      /**
 258       * Return the type map for this client.
 259       *
 260       * @return array
 261       */
 262      public function getTypeMap()
 263      {
 264          return $this->typeMap;
 265      }
 266  
 267      /**
 268       * Return the write concern for this client.
 269       *
 270       * @see http://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php
 271       * @return WriteConcern
 272       */
 273      public function getWriteConcern()
 274      {
 275          return $this->writeConcern;
 276      }
 277  
 278      /**
 279       * List database names.
 280       *
 281       * @see ListDatabaseNames::__construct() for supported options
 282       * @throws UnexpectedValueException if the command response was malformed
 283       * @throws InvalidArgumentException for parameter/option parsing errors
 284       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 285       */
 286      public function listDatabaseNames(array $options = []) : Iterator
 287      {
 288          $operation = new ListDatabaseNames($options);
 289          $server = select_server($this->manager, $options);
 290  
 291          return $operation->execute($server);
 292      }
 293  
 294      /**
 295       * List databases.
 296       *
 297       * @see ListDatabases::__construct() for supported options
 298       * @param array $options
 299       * @return DatabaseInfoIterator
 300       * @throws UnexpectedValueException if the command response was malformed
 301       * @throws InvalidArgumentException for parameter/option parsing errors
 302       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 303       */
 304      public function listDatabases(array $options = [])
 305      {
 306          $operation = new ListDatabases($options);
 307          $server = select_server($this->manager, $options);
 308  
 309          return $operation->execute($server);
 310      }
 311  
 312      /**
 313       * Select a collection.
 314       *
 315       * @see Collection::__construct() for supported options
 316       * @param string $databaseName   Name of the database containing the collection
 317       * @param string $collectionName Name of the collection to select
 318       * @param array  $options        Collection constructor options
 319       * @return Collection
 320       * @throws InvalidArgumentException for parameter/option parsing errors
 321       */
 322      public function selectCollection($databaseName, $collectionName, array $options = [])
 323      {
 324          $options += ['typeMap' => $this->typeMap];
 325  
 326          return new Collection($this->manager, $databaseName, $collectionName, $options);
 327      }
 328  
 329      /**
 330       * Select a database.
 331       *
 332       * @see Database::__construct() for supported options
 333       * @param string $databaseName Name of the database to select
 334       * @param array  $options      Database constructor options
 335       * @return Database
 336       * @throws InvalidArgumentException for parameter/option parsing errors
 337       */
 338      public function selectDatabase($databaseName, array $options = [])
 339      {
 340          $options += ['typeMap' => $this->typeMap];
 341  
 342          return new Database($this->manager, $databaseName, $options);
 343      }
 344  
 345      /**
 346       * Start a new client session.
 347       *
 348       * @see http://php.net/manual/en/mongodb-driver-manager.startsession.php
 349       * @param array $options Session options
 350       * @return Session
 351       */
 352      public function startSession(array $options = [])
 353      {
 354          return $this->manager->startSession($options);
 355      }
 356  
 357      /**
 358       * Create a change stream for watching changes to the cluster.
 359       *
 360       * @see Watch::__construct() for supported options
 361       * @param array $pipeline List of pipeline operations
 362       * @param array $options  Command options
 363       * @return ChangeStream
 364       * @throws InvalidArgumentException for parameter/option parsing errors
 365       */
 366      public function watch(array $pipeline = [], array $options = [])
 367      {
 368          if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
 369              $options['readPreference'] = $this->readPreference;
 370          }
 371  
 372          $server = select_server($this->manager, $options);
 373  
 374          if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) {
 375              $options['readConcern'] = $this->readConcern;
 376          }
 377  
 378          if (! isset($options['typeMap'])) {
 379              $options['typeMap'] = $this->typeMap;
 380          }
 381  
 382          $operation = new Watch($this->manager, null, null, $pipeline, $options);
 383  
 384          return $operation->execute($server);
 385      }
 386  
 387      private static function getVersion() : string
 388      {
 389          if (self::$version === null) {
 390              try {
 391                  self::$version = PrettyVersions::getVersion('mongodb/mongodb')->getPrettyVersion();
 392              } catch (Throwable $t) {
 393                  return 'unknown';
 394              }
 395          }
 396  
 397          return self::$version;
 398      }
 399  
 400      private function mergeDriverInfo(array $driver) : array
 401      {
 402          $mergedDriver = [
 403              'name' => 'PHPLIB',
 404              'version' => self::getVersion(),
 405          ];
 406  
 407          if (isset($driver['name'])) {
 408              if (! is_string($driver['name'])) {
 409                  throw InvalidArgumentException::invalidType('"name" handshake option', $driver['name'], 'string');
 410              }
 411  
 412              $mergedDriver['name'] .= self::$handshakeSeparator . $driver['name'];
 413          }
 414  
 415          if (isset($driver['version'])) {
 416              if (! is_string($driver['version'])) {
 417                  throw InvalidArgumentException::invalidType('"version" handshake option', $driver['version'], 'string');
 418              }
 419  
 420              $mergedDriver['version'] .= self::$handshakeSeparator . $driver['version'];
 421          }
 422  
 423          if (isset($driver['platform'])) {
 424              $mergedDriver['platform'] = $driver['platform'];
 425          }
 426  
 427          return $mergedDriver;
 428      }
 429  }