summaryrefslogtreecommitdiffstatshomepage
path: root/core/lib/Drupal/Core/Extension/DatabaseDriver.php
blob: aa468bea79db59d6f00975ab49b5945f952af3da (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
<?php

namespace Drupal\Core\Extension;

use Composer\Autoload\ClassLoader;
use Drupal\Core\Database\Install\Tasks;

/**
 * Defines a database driver extension object.
 */
class DatabaseDriver extends Extension {

  /**
   * The container class loader.
   */
  private ClassLoader $classLoader;

  /**
   * The install tasks object instance of the database driver.
   */
  private Tasks $installTasks;

  /**
   * Constructs a new DatabaseDriver object.
   *
   * @param string $root
   *   The app root.
   * @param \Drupal\Core\Extension\Extension $module
   *   The module containing the database driver.
   * @param string $driverName
   *   The database driver name.
   * @param \Drupal\Core\Extension\Extension[] $discoveredModules
   *   The modules discovered in the installation.
   */
  public function __construct(
    string $root,
    protected Extension $module,
    protected string $driverName,
    protected array $discoveredModules,
  ) {
    $this->root = $root;
    $this->type = 'database_driver';
  }

  /**
   * Returns the Extension object of the module containing the database driver.
   *
   * @return \Drupal\Core\Extension\Extension
   *   The Extension object of the module containing the database driver.
   */
  public function getModule(): Extension {
    return $this->module;
  }

  /**
   * Returns the name of the database driver.
   *
   * @return string
   *   The name of the database driver.
   */
  public function getDriverName(): string {
    return $this->driverName;
  }

  /**
   * Returns the PHP namespace of the database driver.
   *
   * @return string
   *   The PHP namespace of the database driver.
   */
  public function getNamespace(): string {
    return "Drupal\\" . $this->getModule()->getName() . "\\Driver\\Database\\" . $this->getDriverName();
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->getNamespace();
  }

  /**
   * {@inheritdoc}
   */
  public function getPath() {
    return $this->getModule()->getPath() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Driver' . DIRECTORY_SEPARATOR . 'Database' . DIRECTORY_SEPARATOR . $this->getDriverName();
  }

  /**
   * {@inheritdoc}
   */
  public function load() {
    if (!isset($this->classLoader)) {
      if (\Drupal::hasContainer() && \Drupal::hasService('class_loader')) {
        $this->classLoader = \Drupal::service('class_loader');
      }
      else {
        $this->classLoader = require DRUPAL_ROOT . '/autoload.php';
      }
      $this->classLoader->addPsr4($this->getNamespace() . '\\', $this->getPath());
      foreach (($this->getAutoloadInfo()['dependencies'] ?? []) as $dependency) {
        $this->classLoader->addPsr4($dependency['namespace'] . '\\', $dependency['autoload']);
      }
    }
    return TRUE;
  }

  /**
   * Returns the install tasks object instance of this database driver.
   *
   * @return \Drupal\Core\Database\Install\Tasks
   *   The install tasks object instance.
   */
  public function getInstallTasks(): Tasks {
    if (!isset($this->installTasks)) {
      $this->load();
      $installTasksClass = $this->getNamespace() . "\\Install\\Tasks";
      $this->installTasks = new $installTasksClass();
    }
    return $this->installTasks;
  }

  // phpcs:disable
  /**
   * Returns an array with the driver's autoload information.
   *
   * The module that provides the database driver should add the driver's
   * namespace to Composer's autoloader. However, since the database connection
   * must be established before Drupal adds the module's entire namespace to the
   * autoloader, the database connection info array includes an "autoload" key
   * containing the autoload directory for the driver's namespace. For requests
   * that connect to the database via a connection info array, the value of the
   * "autoload" key is automatically added to the autoloader.
   *
   * This method can be called to find the default value of that key when the
   * database connection info array isn't available. This includes:
   * - Console commands and test runners that connect to a database specified
   *   by a database URL rather than a connection info array.
   * - During installation, prior to the connection info array being written to
   *   settings.php.
   *
   * This method returns an array with the driver's namespace and autoload
   * directory that must be added to the autoloader, as well as those of any
   * dependency specified in the driver's module.info.yml file, in the format
   * @code
   * [
   *   'autoload' => 'path_to_modules/module_a/src/Driver/Database/driver_1/',
   *   'namespace' => 'Drupal\\module_a\\Driver\\Database\\driver_1',
   *   'dependencies' => [
   *     'module_x' => [
   *       'autoload' => 'path_to_modules/module_x/src/',
   *       'namespace' => 'Drupal\\module_x',
   *     ],
   *   ],
   * ]
   * @endcode
   *
   * @return array{
   *     'autoload': string,
   *     'namespace': string,
   *     'dependencies': array<string, array{'autoload': string, 'namespace': string}>,
   *   }
   */
  // phpcs:enable
  public function getAutoloadInfo(): array {
    $this->getModuleInfo();

    $autoloadInfo = [
      'namespace' => $this->getNamespace(),
      'autoload' => $this->getPath() . DIRECTORY_SEPARATOR,
    ];

    foreach (($this->info['dependencies'] ?? []) as $dependency) {
      $dependencyData = Dependency::createFromString($dependency);
      $dependencyName = $dependencyData->getName();
      if (empty($this->discoveredModules[$dependencyName])) {
        throw new \RuntimeException(sprintf("Cannot find the module '%s' that is required by module '%s'", $dependencyName, $this->getModule()->getName()));
      }
      $autoloadInfo['dependencies'][$dependencyName] = [
        'namespace' => "Drupal\\{$dependencyName}",
        'autoload' => $this->discoveredModules[$dependencyName]->getPath() . '/src/',
      ];
    }

    return $autoloadInfo;
  }

  /**
   * {@inheritdoc}
   */
  public function isExperimental(): bool {
    $this->getModuleInfo();
    return parent::isExperimental();
  }

  /**
   * {@inheritdoc}
   */
  public function isObsolete(): bool {
    $this->getModuleInfo();
    return parent::isObsolete();
  }

  /**
   * Gets the content of the info.yml file of the driver's module, as an array.
   *
   * The info array is saved in the $info property.
   *
   * @throws \Drupal\Core\Extension\InfoParserException
   *   Exception thrown if there is a parsing error or the .info.yml file does
   *   not contain a required key.
   */
  private function getModuleInfo(): void {
    if (!isset($this->info)) {
      $infoParser = new InfoParser($this->root);
      $this->info = $infoParser->parse($this->getModule()->getPathname());
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getPathname() {
    throw new \LogicException(__METHOD__ . '() is not implemented');
  }

  /**
   * {@inheritdoc}
   */
  public function getFilename() {
    throw new \LogicException(__METHOD__ . '() is not implemented');
  }

  /**
   * {@inheritdoc}
   */
  public function getExtensionPathname() {
    throw new \LogicException(__METHOD__ . '() is not implemented');
  }

  /**
   * {@inheritdoc}
   */
  public function getExtensionFilename() {
    throw new \LogicException(__METHOD__ . '() is not implemented');
  }

}