Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

   1  <?php
   2  /*
   3   * Copyright 2018-present 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   *   https://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\Operation;
  19  
  20  use MongoDB\Driver\Command;
  21  use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
  22  use MongoDB\Driver\ReadPreference;
  23  use MongoDB\Driver\Server;
  24  use MongoDB\Driver\Session;
  25  use MongoDB\Exception\InvalidArgumentException;
  26  use MongoDB\Exception\UnsupportedException;
  27  
  28  use function current;
  29  use function is_array;
  30  use function is_string;
  31  use function MongoDB\server_supports_feature;
  32  
  33  /**
  34   * Operation for the explain command.
  35   *
  36   * @api
  37   * @see \MongoDB\Collection::explain()
  38   * @see https://mongodb.com/docs/manual/reference/command/explain/
  39   */
  40  class Explain implements Executable
  41  {
  42      public const VERBOSITY_ALL_PLANS = 'allPlansExecution';
  43      public const VERBOSITY_EXEC_STATS = 'executionStats';
  44      public const VERBOSITY_QUERY = 'queryPlanner';
  45  
  46      /** @var integer */
  47      private static $wireVersionForAggregate = 7;
  48  
  49      /** @var string */
  50      private $databaseName;
  51  
  52      /** @var Explainable */
  53      private $explainable;
  54  
  55      /** @var array */
  56      private $options;
  57  
  58      /**
  59       * Constructs an explain command for explainable operations.
  60       *
  61       * Supported options:
  62       *
  63       *  * comment (mixed): BSON value to attach as a comment to this command.
  64       *
  65       *    This is not supported for servers versions < 4.4.
  66       *
  67       *  * readPreference (MongoDB\Driver\ReadPreference): Read preference.
  68       *
  69       *  * session (MongoDB\Driver\Session): Client session.
  70       *
  71       *  * typeMap (array): Type map for BSON deserialization. This will be used
  72       *    used for the returned command result document.
  73       *
  74       *  * verbosity (string): The mode in which the explain command will be run.
  75       *
  76       * @param string      $databaseName Database name
  77       * @param Explainable $explainable  Operation to explain
  78       * @param array       $options      Command options
  79       * @throws InvalidArgumentException for parameter/option parsing errors
  80       */
  81      public function __construct(string $databaseName, Explainable $explainable, array $options = [])
  82      {
  83          if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
  84              throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
  85          }
  86  
  87          if (isset($options['session']) && ! $options['session'] instanceof Session) {
  88              throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
  89          }
  90  
  91          if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
  92              throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
  93          }
  94  
  95          if (isset($options['verbosity']) && ! is_string($options['verbosity'])) {
  96              throw InvalidArgumentException::invalidType('"verbosity" option', $options['verbosity'], 'string');
  97          }
  98  
  99          $this->databaseName = $databaseName;
 100          $this->explainable = $explainable;
 101          $this->options = $options;
 102      }
 103  
 104      /**
 105       * Execute the operation.
 106       *
 107       * @see Executable::execute()
 108       * @return array|object
 109       * @throws UnsupportedException if the server does not support explaining the operation
 110       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 111       */
 112      public function execute(Server $server)
 113      {
 114          if ($this->explainable instanceof Aggregate && ! server_supports_feature($server, self::$wireVersionForAggregate)) {
 115              throw UnsupportedException::explainNotSupported();
 116          }
 117  
 118          $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $this->createOptions());
 119  
 120          if (isset($this->options['typeMap'])) {
 121              $cursor->setTypeMap($this->options['typeMap']);
 122          }
 123  
 124          return current($cursor->toArray());
 125      }
 126  
 127      /**
 128       * Create the explain command.
 129       */
 130      private function createCommand(Server $server): Command
 131      {
 132          $cmd = ['explain' => $this->explainable->getCommandDocument($server)];
 133  
 134          foreach (['comment', 'verbosity'] as $option) {
 135              if (isset($this->options[$option])) {
 136                  $cmd[$option] = $this->options[$option];
 137              }
 138          }
 139  
 140          return new Command($cmd);
 141      }
 142  
 143      /**
 144       * Create options for executing the command.
 145       *
 146       * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php
 147       */
 148      private function createOptions(): array
 149      {
 150          $options = [];
 151  
 152          if (isset($this->options['readPreference'])) {
 153              $options['readPreference'] = $this->options['readPreference'];
 154          }
 155  
 156          if (isset($this->options['session'])) {
 157              $options['session'] = $this->options['session'];
 158          }
 159  
 160          return $options;
 161      }
 162  
 163      private function isFindAndModify(Explainable $explainable): bool
 164      {
 165          if ($explainable instanceof FindAndModify || $explainable instanceof FindOneAndDelete || $explainable instanceof FindOneAndReplace || $explainable instanceof FindOneAndUpdate) {
 166              return true;
 167          }
 168  
 169          return false;
 170      }
 171  }