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