‪TYPO3CMS  ‪main
ConnectionPool.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\Configuration;
21 use Doctrine\DBAL\Driver\Middleware as DriverMiddleware;
22 use Doctrine\DBAL\DriverManager;
23 use Doctrine\DBAL\Events;
24 use Doctrine\DBAL\Types\Type;
25 use Doctrine\DBAL\Types\Types;
29 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
39 
51 {
55  public const ‪DEFAULT_CONNECTION_NAME = 'Default';
56 
60  protected static ‪$connections = [];
61 
65  protected array ‪$customDoctrineTypes = [
66  ‪EnumType::TYPE => EnumType::class,
67  ‪SetType::TYPE => SetType::class,
68  ];
69 
73  protected array ‪$overrideDoctrineTypes = [
74  Types::DATE_MUTABLE => DateType::class,
75  Types::DATETIME_MUTABLE => DateTimeType::class,
76  Types::TIME_MUTABLE => TimeType::class,
77  ];
78 
84  protected static ‪$driverMap = [
85  'pdo_mysql' => PDOMySqlDriver::class,
86  'pdo_sqlite' => PDOSqliteDriver::class,
87  'pdo_pgsql' => PDOPgSqlDriver::class,
88  // TODO: not supported yet, need to be checked later
89 // 'pdo_oci' => PDOOCIDriver::class,
90 // 'drizzle_pdo_mysql' => DrizzlePDOMySQLDriver::class,
91  ];
92 
101  public function ‪getConnectionForTable(string $tableName): ‪Connection
102  {
103  if (empty($tableName)) {
104  throw new \UnexpectedValueException(
105  'ConnectionPool->getConnectionForTable() requires a table name to be provided.',
106  1459421719
107  );
108  }
109 
110  $connectionName = ‪self::DEFAULT_CONNECTION_NAME;
111  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName])) {
112  $connectionName = (string)‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName];
113  }
114 
115  return $this->‪getConnectionByName($connectionName);
116  }
117 
127  public function ‪getConnectionByName(string $connectionName): ‪Connection
128  {
129  if (empty($connectionName)) {
130  throw new \UnexpectedValueException(
131  'ConnectionPool->getConnectionByName() requires a connection name to be provided.',
132  1459422125
133  );
134  }
135 
136  if (isset(static::$connections[$connectionName])) {
137  return static::$connections[$connectionName];
138  }
139 
140  $connectionParams = ‪$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] ?? [];
141  if (empty($connectionParams)) {
142  throw new \RuntimeException(
143  'The requested database connection named "' . $connectionName . '" has not been configured.',
144  1459422492
145  );
146  }
147 
148  if (empty($connectionParams['wrapperClass'])) {
149  $connectionParams['wrapperClass'] = Connection::class;
150  }
151 
152  if (!is_a($connectionParams['wrapperClass'], Connection::class, true)) {
153  throw new \UnexpectedValueException(
154  'The "wrapperClass" for the connection name "' . $connectionName .
155  '" needs to be a subclass of "' . Connection::class . '".',
156  1459422968
157  );
158  }
159 
160  // Transform TYPO3 `tableoptions` to valid `doctrine/dbal` connection param option `defaultTableOptions`
161  // @todo TYPO3 database configuration should be changed to directly write defaultTableOptions instead,
162  // with proper upgrade migration. Along with that, default table options for MySQL in
163  // testing-framework and core should be adjusted.
164  if (isset($connectionParams['tableoptions'])) {
165  $connectionParams['defaultTableOptions'] = array_replace(
166  $connectionParams['defaultTableOptions'] ?? [],
167  $connectionParams['tableoptions']
168  );
169  unset($connectionParams['tableoptions']);
170  }
171 
172  static::$connections[$connectionName] = $this->‪getDatabaseConnection($connectionParams);
173 
174  return static::$connections[$connectionName];
175  }
176 
182  protected function ‪mapCustomDriver(array $connectionParams): array
183  {
184  // if no custom driver is provided, map TYPO3 specific drivers
185  if (!isset($connectionParams['driverClass']) && isset(static::$driverMap[$connectionParams['driver']])) {
186  $connectionParams['driverClass'] = static::$driverMap[$connectionParams['driver']];
187  }
188 
189  return $connectionParams;
190  }
191 
196  protected function ‪getDriverMiddlewares(array $connectionParams): array
197  {
198  $middlewares = [];
199 
200  foreach ($connectionParams['driverMiddlewares'] ?? [] as $className) {
201  if (!in_array(DriverMiddleware::class, class_implements($className) ?: [], true)) {
202  throw new \UnexpectedValueException('Doctrine Driver Middleware must implement \Doctrine\DBAL\Driver\Middleware', 1677958727);
203  }
204  $middlewares[] = GeneralUtility::makeInstance($className);
205  }
206 
207  return $middlewares;
208  }
209 
213  protected function ‪getDatabaseConnection(array $connectionParams): Connection
214  {
215  $this->‪registerDoctrineTypes();
216 
217  // Default to UTF-8 connection charset
218  if (empty($connectionParams['charset'])) {
219  $connectionParams['charset'] = 'utf8';
220  }
221 
222  $connectionParams = $this->‪mapCustomDriver($connectionParams);
223  $middlewares = $this->‪getDriverMiddlewares($connectionParams);
224  $configuration = $middlewares ? (new Configuration())->setMiddlewares($middlewares) : null;
225 
227  $conn = DriverManager::getConnection($connectionParams, $configuration);
228  $conn->prepareConnection($connectionParams['initCommands'] ?? '');
229 
230  // Register all custom data types in the type mapping
231  foreach ($this->customDoctrineTypes as $type => $className) {
232  $conn->getDatabasePlatform()->registerDoctrineTypeMapping($type, $type);
233  }
234 
235  // Register all override data types in the type mapping
236  foreach ($this->overrideDoctrineTypes as $type => $className) {
237  $conn->getDatabasePlatform()->registerDoctrineTypeMapping($type, $type);
238  }
239 
240  // Handler for building custom data type column definitions
241  // in the SchemaManager
242  $conn->getDatabasePlatform()->getEventManager()->addEventListener(
243  Events::onSchemaColumnDefinition,
244  GeneralUtility::makeInstance(SchemaColumnDefinitionListener::class)
245  );
246 
247  // Handler for enhanced index definitions in the SchemaManager
248  $conn->getDatabasePlatform()->getEventManager()->addEventListener(
249  Events::onSchemaIndexDefinition,
250  GeneralUtility::makeInstance(SchemaIndexDefinitionListener::class)
251  );
252 
253  // Handler for adding custom database platform options to ALTER TABLE
254  // requests in the SchemaManager
255  $conn->getDatabasePlatform()->getEventManager()->addEventListener(
256  Events::onSchemaAlterTable,
257  GeneralUtility::makeInstance(SchemaAlterTableListener::class)
258  );
259 
260  return $conn;
261  }
262 
267  public function ‪getQueryBuilderForTable(string $tableName): QueryBuilder
268  {
269  if (empty($tableName)) {
270  throw new \UnexpectedValueException(
271  'ConnectionPool->getQueryBuilderForTable() requires a connection name to be provided.',
272  1459423448
273  );
274  }
275 
276  return $this->‪getConnectionForTable($tableName)->createQueryBuilder();
277  }
278 
287  public function ‪getConnectionNames(): array
288  {
289  return array_keys(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']);
290  }
291 
300  public function ‪registerDoctrineTypes(): void
301  {
302  // Register custom data types
303  foreach ($this->customDoctrineTypes as $type => $className) {
304  if (!Type::hasType($type)) {
305  Type::addType($type, $className);
306  }
307  }
308  // Override data types
309  foreach ($this->overrideDoctrineTypes as $type => $className) {
310  if (!Type::hasType($type)) {
311  Type::addType($type, $className);
312  continue;
313  }
314  Type::overrideType($type, $className);
315  }
316  }
317 
323  public function ‪resetConnections(): void
324  {
325  static::$connections = [];
326  }
327 }
‪TYPO3\CMS\Core\Database\Schema\Types\DateTimeType
Definition: DateTimeType.php:28
‪TYPO3\CMS\Core\Database\Driver\PDOPgSql\Driver
Definition: Driver.php:34
‪TYPO3\CMS\Core\Database\Driver\PDOMySql\Driver
Definition: Driver.php:36
‪TYPO3\CMS\Core\Database\ConnectionPool\registerDoctrineTypes
‪registerDoctrineTypes()
Definition: ConnectionPool.php:298
‪TYPO3\CMS\Core\Database\Schema\EventListener\SchemaIndexDefinitionListener
Definition: SchemaIndexDefinitionListener.php:30
‪TYPO3\CMS\Core\Database\ConnectionPool\mapCustomDriver
‪mapCustomDriver(array $connectionParams)
Definition: ConnectionPool.php:180
‪TYPO3\CMS\Core\Database\Schema\Types\SetType
Definition: SetType.php:27
‪TYPO3\CMS\Core\Database\ConnectionPool\getConnectionNames
‪getConnectionNames()
Definition: ConnectionPool.php:285
‪TYPO3\CMS\Core\Database\Schema\Types\EnumType
Definition: EnumType.php:27
‪TYPO3\CMS\Core\Database\ConnectionPool\$driverMap
‪static string[] $driverMap
Definition: ConnectionPool.php:82
‪TYPO3\CMS\Core\Database\Schema\Types\DateType
Definition: DateType.php:28
‪TYPO3\CMS\Core\Database\Driver\PDOSqlite\Driver
Definition: Driver.php:35
‪TYPO3\CMS\Core\Database\Schema\Types\EnumType\TYPE
‪const TYPE
Definition: EnumType.php:28
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:55
‪TYPO3\CMS\Core\Database\ConnectionPool\$customDoctrineTypes
‪array $customDoctrineTypes
Definition: ConnectionPool.php:64
‪TYPO3\CMS\Core\Database\ConnectionPool\$connections
‪static Connection[] $connections
Definition: ConnectionPool.php:59
‪TYPO3\CMS\Core\Database\ConnectionPool\getConnectionByName
‪getConnectionByName(string $connectionName)
Definition: ConnectionPool.php:125
‪TYPO3\CMS\Core\Database\Schema\EventListener\SchemaAlterTableListener
Definition: SchemaAlterTableListener.php:28
‪TYPO3\CMS\Core\Database\ConnectionPool\getQueryBuilderForTable
‪getQueryBuilderForTable(string $tableName)
Definition: ConnectionPool.php:265
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:36
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Schema\Types\TimeType
Definition: TimeType.php:28
‪TYPO3\CMS\Core\Database\ConnectionPool\getConnectionForTable
‪getConnectionForTable(string $tableName)
Definition: ConnectionPool.php:99
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:51
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Database\ConnectionPool\getDriverMiddlewares
‪getDriverMiddlewares(array $connectionParams)
Definition: ConnectionPool.php:194
‪TYPO3\CMS\Core\Database\Schema\Types\SetType\TYPE
‪const TYPE
Definition: SetType.php:28
‪TYPO3\CMS\Core\Database\ConnectionPool\resetConnections
‪resetConnections()
Definition: ConnectionPool.php:321
‪TYPO3\CMS\Core\Database\ConnectionPool\getDatabaseConnection
‪getDatabaseConnection(array $connectionParams)
Definition: ConnectionPool.php:211
‪TYPO3\CMS\Core\Database
Definition: Connection.php:18
‪TYPO3\CMS\Core\Database\ConnectionPool\$overrideDoctrineTypes
‪array $overrideDoctrineTypes
Definition: ConnectionPool.php:72
‪TYPO3\CMS\Core\Database\Schema\EventListener\SchemaColumnDefinitionListener
Definition: SchemaColumnDefinitionListener.php:30