Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   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 MongoDB\Driver\Cursor;
  21  use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
  22  use MongoDB\Driver\Manager;
  23  use MongoDB\Driver\ReadConcern;
  24  use MongoDB\Driver\ReadPreference;
  25  use MongoDB\Driver\WriteConcern;
  26  use MongoDB\Exception\InvalidArgumentException;
  27  use MongoDB\Exception\UnexpectedValueException;
  28  use MongoDB\Exception\UnsupportedException;
  29  use MongoDB\GridFS\Bucket;
  30  use MongoDB\Model\BSONArray;
  31  use MongoDB\Model\BSONDocument;
  32  use MongoDB\Model\CollectionInfoIterator;
  33  use MongoDB\Operation\Aggregate;
  34  use MongoDB\Operation\CreateCollection;
  35  use MongoDB\Operation\DatabaseCommand;
  36  use MongoDB\Operation\DropCollection;
  37  use MongoDB\Operation\DropDatabase;
  38  use MongoDB\Operation\ListCollections;
  39  use MongoDB\Operation\ModifyCollection;
  40  use MongoDB\Operation\Watch;
  41  use Traversable;
  42  use function is_array;
  43  use function strlen;
  44  
  45  class Database
  46  {
  47      /** @var array */
  48      private static $defaultTypeMap = [
  49          'array' => BSONArray::class,
  50          'document' => BSONDocument::class,
  51          'root' => BSONDocument::class,
  52      ];
  53  
  54      /** @var integer */
  55      private static $wireVersionForReadConcern = 4;
  56  
  57      /** @var integer */
  58      private static $wireVersionForWritableCommandWriteConcern = 5;
  59  
  60      /** @var integer */
  61      private static $wireVersionForReadConcernWithWriteStage = 8;
  62  
  63      /** @var string */
  64      private $databaseName;
  65  
  66      /** @var Manager */
  67      private $manager;
  68  
  69      /** @var ReadConcern */
  70      private $readConcern;
  71  
  72      /** @var ReadPreference */
  73      private $readPreference;
  74  
  75      /** @var array */
  76      private $typeMap;
  77  
  78      /** @var WriteConcern */
  79      private $writeConcern;
  80  
  81      /**
  82       * Constructs new Database instance.
  83       *
  84       * This class provides methods for database-specific operations and serves
  85       * as a gateway for accessing collections.
  86       *
  87       * Supported options:
  88       *
  89       *  * readConcern (MongoDB\Driver\ReadConcern): The default read concern to
  90       *    use for database operations and selected collections. Defaults to the
  91       *    Manager's read concern.
  92       *
  93       *  * readPreference (MongoDB\Driver\ReadPreference): The default read
  94       *    preference to use for database operations and selected collections.
  95       *    Defaults to the Manager's read preference.
  96       *
  97       *  * typeMap (array): Default type map for cursors and BSON documents.
  98       *
  99       *  * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
 100       *    to use for database operations and selected collections. Defaults to
 101       *    the Manager's write concern.
 102       *
 103       * @param Manager $manager      Manager instance from the driver
 104       * @param string  $databaseName Database name
 105       * @param array   $options      Database options
 106       * @throws InvalidArgumentException for parameter/option parsing errors
 107       */
 108      public function __construct(Manager $manager, $databaseName, array $options = [])
 109      {
 110          if (strlen($databaseName) < 1) {
 111              throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName);
 112          }
 113  
 114          if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
 115              throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
 116          }
 117  
 118          if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
 119              throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
 120          }
 121  
 122          if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
 123              throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
 124          }
 125  
 126          if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
 127              throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
 128          }
 129  
 130          $this->manager = $manager;
 131          $this->databaseName = (string) $databaseName;
 132          $this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
 133          $this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
 134          $this->typeMap = isset($options['typeMap']) ? $options['typeMap'] : self::$defaultTypeMap;
 135          $this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
 136      }
 137  
 138      /**
 139       * Return internal properties for debugging purposes.
 140       *
 141       * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo
 142       * @return array
 143       */
 144      public function __debugInfo()
 145      {
 146          return [
 147              'databaseName' => $this->databaseName,
 148              'manager' => $this->manager,
 149              'readConcern' => $this->readConcern,
 150              'readPreference' => $this->readPreference,
 151              'typeMap' => $this->typeMap,
 152              'writeConcern' => $this->writeConcern,
 153          ];
 154      }
 155  
 156      /**
 157       * Select a collection within this database.
 158       *
 159       * Note: collections whose names contain special characters (e.g. ".") may
 160       * be selected with complex syntax (e.g. $database->{"system.profile"}) or
 161       * {@link selectCollection()}.
 162       *
 163       * @see http://php.net/oop5.overloading#object.get
 164       * @see http://php.net/types.string#language.types.string.parsing.complex
 165       * @param string $collectionName Name of the collection to select
 166       * @return Collection
 167       */
 168      public function __get($collectionName)
 169      {
 170          return $this->selectCollection($collectionName);
 171      }
 172  
 173      /**
 174       * Return the database name.
 175       *
 176       * @return string
 177       */
 178      public function __toString()
 179      {
 180          return $this->databaseName;
 181      }
 182  
 183      /**
 184       * Runs an aggregation framework pipeline on the database for pipeline
 185       * stages that do not require an underlying collection, such as $currentOp
 186       * and $listLocalSessions. Requires MongoDB >= 3.6
 187       *
 188       * @see Aggregate::__construct() for supported options
 189       * @param array $pipeline List of pipeline operations
 190       * @param array $options  Command options
 191       * @return Traversable
 192       * @throws UnexpectedValueException if the command response was malformed
 193       * @throws UnsupportedException if options are not supported by the selected server
 194       * @throws InvalidArgumentException for parameter/option parsing errors
 195       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 196       */
 197      public function aggregate(array $pipeline, array $options = [])
 198      {
 199          $hasWriteStage = is_last_pipeline_operator_write($pipeline);
 200  
 201          if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
 202              $options['readPreference'] = $this->readPreference;
 203          }
 204  
 205          if ($hasWriteStage) {
 206              $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY);
 207          }
 208  
 209          $server = select_server($this->manager, $options);
 210  
 211          /* MongoDB 4.2 and later supports a read concern when an $out stage is
 212           * being used, but earlier versions do not.
 213           *
 214           * A read concern is also not compatible with transactions.
 215           */
 216          if (! isset($options['readConcern']) &&
 217              server_supports_feature($server, self::$wireVersionForReadConcern) &&
 218              ! is_in_transaction($options) &&
 219              ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage))
 220          ) {
 221              $options['readConcern'] = $this->readConcern;
 222          }
 223  
 224          if (! isset($options['typeMap'])) {
 225              $options['typeMap'] = $this->typeMap;
 226          }
 227  
 228          if ($hasWriteStage &&
 229              ! isset($options['writeConcern']) &&
 230              server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) &&
 231              ! is_in_transaction($options)) {
 232              $options['writeConcern'] = $this->writeConcern;
 233          }
 234  
 235          $operation = new Aggregate($this->databaseName, null, $pipeline, $options);
 236  
 237          return $operation->execute($server);
 238      }
 239  
 240      /**
 241       * Execute a command on this database.
 242       *
 243       * @see DatabaseCommand::__construct() for supported options
 244       * @param array|object $command Command document
 245       * @param array        $options Options for command execution
 246       * @return Cursor
 247       * @throws InvalidArgumentException for parameter/option parsing errors
 248       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 249       */
 250      public function command($command, array $options = [])
 251      {
 252          if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
 253              $options['readPreference'] = $this->readPreference;
 254          }
 255  
 256          if (! isset($options['typeMap'])) {
 257              $options['typeMap'] = $this->typeMap;
 258          }
 259  
 260          $operation = new DatabaseCommand($this->databaseName, $command, $options);
 261          $server = select_server($this->manager, $options);
 262  
 263          return $operation->execute($server);
 264      }
 265  
 266      /**
 267       * Create a new collection explicitly.
 268       *
 269       * @see CreateCollection::__construct() for supported options
 270       * @param string $collectionName
 271       * @param array  $options
 272       * @return array|object Command result document
 273       * @throws UnsupportedException if options are not supported by the selected server
 274       * @throws InvalidArgumentException for parameter/option parsing errors
 275       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 276       */
 277      public function createCollection($collectionName, array $options = [])
 278      {
 279          if (! isset($options['typeMap'])) {
 280              $options['typeMap'] = $this->typeMap;
 281          }
 282  
 283          $server = select_server($this->manager, $options);
 284  
 285          if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) {
 286              $options['writeConcern'] = $this->writeConcern;
 287          }
 288  
 289          $operation = new CreateCollection($this->databaseName, $collectionName, $options);
 290  
 291          return $operation->execute($server);
 292      }
 293  
 294      /**
 295       * Drop this database.
 296       *
 297       * @see DropDatabase::__construct() for supported options
 298       * @param array $options Additional options
 299       * @return array|object Command result document
 300       * @throws UnsupportedException if options are unsupported on the selected server
 301       * @throws InvalidArgumentException for parameter/option parsing errors
 302       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 303       */
 304      public function drop(array $options = [])
 305      {
 306          if (! isset($options['typeMap'])) {
 307              $options['typeMap'] = $this->typeMap;
 308          }
 309  
 310          $server = select_server($this->manager, $options);
 311  
 312          if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) {
 313              $options['writeConcern'] = $this->writeConcern;
 314          }
 315  
 316          $operation = new DropDatabase($this->databaseName, $options);
 317  
 318          return $operation->execute($server);
 319      }
 320  
 321      /**
 322       * Drop a collection within this database.
 323       *
 324       * @see DropCollection::__construct() for supported options
 325       * @param string $collectionName Collection name
 326       * @param array  $options        Additional options
 327       * @return array|object Command result document
 328       * @throws UnsupportedException if options are unsupported on the selected server
 329       * @throws InvalidArgumentException for parameter/option parsing errors
 330       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 331       */
 332      public function dropCollection($collectionName, array $options = [])
 333      {
 334          if (! isset($options['typeMap'])) {
 335              $options['typeMap'] = $this->typeMap;
 336          }
 337  
 338          $server = select_server($this->manager, $options);
 339  
 340          if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) {
 341              $options['writeConcern'] = $this->writeConcern;
 342          }
 343  
 344          $operation = new DropCollection($this->databaseName, $collectionName, $options);
 345  
 346          return $operation->execute($server);
 347      }
 348  
 349      /**
 350       * Returns the database name.
 351       *
 352       * @return string
 353       */
 354      public function getDatabaseName()
 355      {
 356          return $this->databaseName;
 357      }
 358  
 359      /**
 360       * Return the Manager.
 361       *
 362       * @return Manager
 363       */
 364      public function getManager()
 365      {
 366          return $this->manager;
 367      }
 368  
 369      /**
 370       * Return the read concern for this database.
 371       *
 372       * @see http://php.net/manual/en/mongodb-driver-readconcern.isdefault.php
 373       * @return ReadConcern
 374       */
 375      public function getReadConcern()
 376      {
 377          return $this->readConcern;
 378      }
 379  
 380      /**
 381       * Return the read preference for this database.
 382       *
 383       * @return ReadPreference
 384       */
 385      public function getReadPreference()
 386      {
 387          return $this->readPreference;
 388      }
 389  
 390      /**
 391       * Return the type map for this database.
 392       *
 393       * @return array
 394       */
 395      public function getTypeMap()
 396      {
 397          return $this->typeMap;
 398      }
 399  
 400      /**
 401       * Return the write concern for this database.
 402       *
 403       * @see http://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php
 404       * @return WriteConcern
 405       */
 406      public function getWriteConcern()
 407      {
 408          return $this->writeConcern;
 409      }
 410  
 411      /**
 412       * Returns information for all collections in this database.
 413       *
 414       * @see ListCollections::__construct() for supported options
 415       * @param array $options
 416       * @return CollectionInfoIterator
 417       * @throws InvalidArgumentException for parameter/option parsing errors
 418       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 419       */
 420      public function listCollections(array $options = [])
 421      {
 422          $operation = new ListCollections($this->databaseName, $options);
 423          $server = select_server($this->manager, $options);
 424  
 425          return $operation->execute($server);
 426      }
 427  
 428      /**
 429       * Modifies a collection or view.
 430       *
 431       * @see ModifyCollection::__construct() for supported options
 432       * @param string $collectionName    Collection or view to modify
 433       * @param array  $collectionOptions Collection or view options to assign
 434       * @param array  $options           Command options
 435       * @return array|object
 436       * @throws InvalidArgumentException for parameter/option parsing errors
 437       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 438       */
 439      public function modifyCollection($collectionName, array $collectionOptions, array $options = [])
 440      {
 441          if (! isset($options['typeMap'])) {
 442              $options['typeMap'] = $this->typeMap;
 443          }
 444  
 445          $server = select_server($this->manager, $options);
 446  
 447          if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) {
 448              $options['writeConcern'] = $this->writeConcern;
 449          }
 450  
 451          $operation = new ModifyCollection($this->databaseName, $collectionName, $collectionOptions, $options);
 452  
 453          return $operation->execute($server);
 454      }
 455  
 456      /**
 457       * Select a collection within this database.
 458       *
 459       * @see Collection::__construct() for supported options
 460       * @param string $collectionName Name of the collection to select
 461       * @param array  $options        Collection constructor options
 462       * @return Collection
 463       * @throws InvalidArgumentException for parameter/option parsing errors
 464       */
 465      public function selectCollection($collectionName, array $options = [])
 466      {
 467          $options += [
 468              'readConcern' => $this->readConcern,
 469              'readPreference' => $this->readPreference,
 470              'typeMap' => $this->typeMap,
 471              'writeConcern' => $this->writeConcern,
 472          ];
 473  
 474          return new Collection($this->manager, $this->databaseName, $collectionName, $options);
 475      }
 476  
 477      /**
 478       * Select a GridFS bucket within this database.
 479       *
 480       * @see Bucket::__construct() for supported options
 481       * @param array $options Bucket constructor options
 482       * @return Bucket
 483       * @throws InvalidArgumentException for parameter/option parsing errors
 484       */
 485      public function selectGridFSBucket(array $options = [])
 486      {
 487          $options += [
 488              'readConcern' => $this->readConcern,
 489              'readPreference' => $this->readPreference,
 490              'typeMap' => $this->typeMap,
 491              'writeConcern' => $this->writeConcern,
 492          ];
 493  
 494          return new Bucket($this->manager, $this->databaseName, $options);
 495      }
 496  
 497      /**
 498       * Create a change stream for watching changes to the database.
 499       *
 500       * @see Watch::__construct() for supported options
 501       * @param array $pipeline List of pipeline operations
 502       * @param array $options  Command options
 503       * @return ChangeStream
 504       * @throws InvalidArgumentException for parameter/option parsing errors
 505       */
 506      public function watch(array $pipeline = [], array $options = [])
 507      {
 508          if (! isset($options['readPreference']) && ! is_in_transaction($options)) {
 509              $options['readPreference'] = $this->readPreference;
 510          }
 511  
 512          $server = select_server($this->manager, $options);
 513  
 514          if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) {
 515              $options['readConcern'] = $this->readConcern;
 516          }
 517  
 518          if (! isset($options['typeMap'])) {
 519              $options['typeMap'] = $this->typeMap;
 520          }
 521  
 522          $operation = new Watch($this->manager, $this->databaseName, null, $pipeline, $options);
 523  
 524          return $operation->execute($server);
 525      }
 526  
 527      /**
 528       * Get a clone of this database with different options.
 529       *
 530       * @see Database::__construct() for supported options
 531       * @param array $options Database constructor options
 532       * @return Database
 533       * @throws InvalidArgumentException for parameter/option parsing errors
 534       */
 535      public function withOptions(array $options = [])
 536      {
 537          $options += [
 538              'readConcern' => $this->readConcern,
 539              'readPreference' => $this->readPreference,
 540              'typeMap' => $this->typeMap,
 541              'writeConcern' => $this->writeConcern,
 542          ];
 543  
 544          return new Database($this->manager, $this->databaseName, $options);
 545      }
 546  }