[BUGFIX] Fix several typos in php comments
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / ConnectionPool.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Database;
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
18 use Doctrine\DBAL\DriverManager;
19 use Doctrine\DBAL\Events;
20 use Doctrine\DBAL\Types\Type;
21 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
22 use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaAlterTableListener;
23 use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaColumnDefinitionListener;
24 use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaIndexDefinitionListener;
25 use TYPO3\CMS\Core\Database\Schema\Types\EnumType;
26 use TYPO3\CMS\Core\Database\Schema\Types\SetType;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29 /**
30 * Manager that handles opening/retrieving database connections.
31 *
32 * It's a facade to the actual Doctrine DBAL DriverManager that implements TYPO3
33 * specific functionality like mapping individual tables to different database
34 * connections.
35 *
36 * getConnectionForTable() is the only supported way to get a connection that
37 * honors the table mapping configuration.
38 */
39 class ConnectionPool
40 {
41 /**
42 * @var string
43 */
44 const DEFAULT_CONNECTION_NAME = 'Default';
45
46 /**
47 * @var Connection[]
48 */
49 protected static $connections = [];
50
51 /**
52 * @var array
53 */
54 protected $customDoctrineTypes = [
55 EnumType::TYPE => EnumType::class,
56 SetType::TYPE => SetType::class,
57 ];
58
59 /**
60 * Creates a connection object based on the specified table name.
61 *
62 * This is the official entry point to get a database connection to ensure
63 * that the mapping of table names to database connections is honored.
64 *
65 * @param string $tableName
66 * @return Connection
67 */
68 public function getConnectionForTable(string $tableName): Connection
69 {
70 if (empty($tableName)) {
71 throw new \UnexpectedValueException(
72 'ConnectionPool->getConnectionForTable() requires a table name to be provided.',
73 1459421719
74 );
75 }
76
77 $connectionName = self::DEFAULT_CONNECTION_NAME;
78 if (!empty($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName])) {
79 $connectionName = (string)$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName];
80 }
81
82 return $this->getConnectionByName($connectionName);
83 }
84
85 /**
86 * Creates a connection object based on the specified identifier.
87 *
88 * This method should only be used in edge cases. Use getConnectionForTable() so
89 * that the tablename<>databaseConnection mapping will be taken into account.
90 *
91 * @param string $connectionName
92 * @return Connection
93 * @throws \Doctrine\DBAL\DBALException
94 */
95 public function getConnectionByName(string $connectionName): Connection
96 {
97 if (empty($connectionName)) {
98 throw new \UnexpectedValueException(
99 'ConnectionPool->getConnectionByName() requires a connection name to be provided.',
100 1459422125
101 );
102 }
103
104 if (isset(static::$connections[$connectionName])) {
105 return static::$connections[$connectionName];
106 }
107
108 $connectionParams = $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] ?? [];
109 if (empty($connectionParams)) {
110 throw new \RuntimeException(
111 'The requested database connection named "' . $connectionName . '" has not been configured.',
112 1459422492
113 );
114 }
115
116 if (empty($connectionParams['wrapperClass'])) {
117 $connectionParams['wrapperClass'] = Connection::class;
118 }
119
120 if (!is_a($connectionParams['wrapperClass'], Connection::class, true)) {
121 throw new \UnexpectedValueException(
122 'The "wrapperClass" for the connection name "' . $connectionName .
123 '" needs to be a subclass of "' . Connection::class . '".',
124 1459422968
125 );
126 }
127
128 static::$connections[$connectionName] = $this->getDatabaseConnection($connectionParams);
129
130 return static::$connections[$connectionName];
131 }
132
133 /**
134 * Creates a connection object based on the specified parameters
135 *
136 * @param array $connectionParams
137 * @return Connection
138 */
139 protected function getDatabaseConnection(array $connectionParams): Connection
140 {
141 // Default to UTF-8 connection charset
142 if (empty($connectionParams['charset'])) {
143 $connectionParams['charset'] = 'utf8';
144 }
145
146 // Force consistent handling of binary objects across database platforms
147 // MySQL returns strings by default, PostgreSQL streams.
148 if (strpos($connectionParams['driver'], 'pdo_') === 0) {
149 $connectionParams['driverOptions'][\PDO::ATTR_STRINGIFY_FETCHES] = true;
150 }
151
152 /** @var Connection $conn */
153 $conn = DriverManager::getConnection($connectionParams);
154 $conn->setFetchMode(\PDO::FETCH_ASSOC);
155 $conn->prepareConnection($connectionParams['initCommands'] ?? '');
156
157 // Register custom data types
158 foreach ($this->customDoctrineTypes as $type => $className) {
159 if (!Type::hasType($type)) {
160 Type::addType($type, $className);
161 }
162 }
163
164 // Register all custom data types in the type mapping
165 foreach ($this->customDoctrineTypes as $type => $className) {
166 $conn->getDatabasePlatform()->registerDoctrineTypeMapping($type, $type);
167 }
168
169 // Handler for building custom data type column definitions
170 // in the SchemaManager
171 $conn->getDatabasePlatform()->getEventManager()->addEventListener(
172 Events::onSchemaColumnDefinition,
173 GeneralUtility::makeInstance(SchemaColumnDefinitionListener::class)
174 );
175
176 // Handler for enhanced index definitions in the SchemaManager
177 $conn->getDatabasePlatform()->getEventManager()->addEventListener(
178 Events::onSchemaIndexDefinition,
179 GeneralUtility::makeInstance(SchemaIndexDefinitionListener::class)
180 );
181
182 // Handler for adding custom database platform options to ALTER TABLE
183 // requests in the SchemaManager
184 $conn->getDatabasePlatform()->getEventManager()->addEventListener(
185 Events::onSchemaAlterTable,
186 GeneralUtility::makeInstance(SchemaAlterTableListener::class)
187 );
188
189 return $conn;
190 }
191
192 /**
193 * Returns the connection specific query builder object that can be used to build
194 * complex SQL queries using and object oriented approach.
195 *
196 * @param string $tableName
197 * @return QueryBuilder
198 */
199 public function getQueryBuilderForTable(string $tableName): QueryBuilder
200 {
201 if (empty($tableName)) {
202 throw new \UnexpectedValueException(
203 'ConnectionPool->getQueryBuilderForTable() requires a connection name to be provided.',
204 1459423448
205 );
206 }
207
208 return $this->getConnectionForTable($tableName)->createQueryBuilder();
209 }
210
211 /**
212 * Returns an array containing the names of all currently configured connections.
213 *
214 * This method should only be used in edge cases. Use getConnectionForTable() so
215 * that the tablename<>databaseConnection mapping will be taken into account.
216 *
217 * @internal
218 * @return array
219 */
220 public function getConnectionNames(): array
221 {
222 return array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']);
223 }
224
225 /**
226 * Returns the list of custom Doctrine data types implemented by TYPO3.
227 * This method is needed by the Schema parser to register the types as it
228 * does not require a database connection and thus the types don't get
229 * registered automatically.
230 *
231 * @internal
232 * @return array
233 */
234 public function getCustomDoctrineTypes(): array
235 {
236 return $this->customDoctrineTypes;
237 }
238
239 /**
240 * Reset internal list of connections.
241 * Currently primarily used in functional tests to close connections and start
242 * new ones in between single tests.
243 */
244 public function resetConnections(): void
245 {
246 static::$connections = [];
247 }
248 }