[TASK] Don't mark ConnectionPool::getConnectionByName() as internal
[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 TYPO3\CMS\Core\Database\Query\QueryBuilder;
20
21 /**
22 * Manager that handles opening/retrieving database connections.
23 *
24 * It's a facade to the actual Doctrine DBAL DriverManager that implements TYPO3
25 * specific functionality like mapping individual tables to different database
26 * connections.
27 *
28 * getConnectionForTable() is the only supported way to get a connection that
29 * honors the table mapping configuration.
30 */
31 class ConnectionPool
32 {
33 /**
34 * @var string
35 */
36 const DEFAULT_CONNECTION_NAME = 'Default';
37
38 /**
39 * @var Connection[]
40 */
41 protected static $connections = [];
42
43 /**
44 * Creates a connection object based on the specified table name.
45 *
46 * This is the official entry point to get a database connection to ensure
47 * that the mapping of table names to database connections is honored.
48 *
49 * @param string $tableName
50 * @return Connection
51 */
52 public function getConnectionForTable(string $tableName): Connection
53 {
54 if (empty($tableName)) {
55 throw new \UnexpectedValueException(
56 'ConnectionPool->getConnectionForTable() requires a table name to be provided.',
57 1459421719
58 );
59 }
60
61 $connectionName = self::DEFAULT_CONNECTION_NAME;
62 if (!empty($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName])) {
63 $connectionName = (string)$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName];
64 }
65
66 return $this->getConnectionByName($connectionName);
67 }
68
69 /**
70 * Creates a connection object based on the specified identifier.
71 *
72 * This method should only be used in edge cases. Use getConnectionForTable() so
73 * that the tablename<>databaseConnection mapping will be taken into account.
74 *
75 * @param string $connectionName
76 * @return Connection
77 * @throws \Doctrine\DBAL\DBALException
78 */
79 public function getConnectionByName(string $connectionName): Connection
80 {
81 if (empty($connectionName)) {
82 throw new \UnexpectedValueException(
83 'ConnectionPool->getConnectionByName() requires a connection name to be provided.',
84 1459422125
85 );
86 }
87
88 if (isset(static::$connections[$connectionName])) {
89 return static::$connections[$connectionName];
90 }
91
92 $connectionParams = $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] ?? [];
93 if (empty($connectionParams)) {
94 throw new \RuntimeException(
95 'The requested database connection named "' . $connectionName . '" has not been configured.',
96 1459422492
97 );
98 }
99
100 if (empty($connectionParams['wrapperClass'])) {
101 $connectionParams['wrapperClass'] = Connection::class;
102 }
103
104 if (!is_a($connectionParams['wrapperClass'], Connection::class, true)) {
105 throw new \UnexpectedValueException(
106 'The "wrapperClass" for the connection name "' . $connectionName .
107 '" needs to be a subclass of "' . Connection::class . '".',
108 1459422968
109 );
110 }
111
112 static::$connections[$connectionName] = $this->getDatabaseConnection($connectionParams);
113
114 return static::$connections[$connectionName];
115 }
116
117 /**
118 * Creates a connection object based on the specified parameters
119 *
120 * @param array $connectionParams
121 * @return Connection
122 */
123 protected function getDatabaseConnection(array $connectionParams): Connection
124 {
125 // Default to UTF-8 connection charset
126 if (empty($connectionParams['charset'])) {
127 $connectionParams['charset'] = 'utf-8';
128 }
129
130 // Force consistent handling of binary objects across datbase platforms
131 // MySQL returns strings by default, PostgreSQL streams.
132 if (strpos($connectionParams['driver'], 'pdo_') === 0) {
133 $connectionParams['driverOptions'][\PDO::ATTR_STRINGIFY_FETCHES] = true;
134 }
135
136 /** @var Connection $conn */
137 $conn = DriverManager::getConnection($connectionParams);
138 $conn->setFetchMode(\PDO::FETCH_ASSOC);
139 $conn->prepareConnection($connectionParams['initCommands'] ?? '');
140
141 return $conn;
142 }
143
144 /**
145 * Returns the connection specific query builder object that can be used to build
146 * complex SQL queries using and object oriented approach.
147 *
148 * @param string $tableName
149 * @return QueryBuilder
150 */
151 public function getQueryBuilderForTable(string $tableName): QueryBuilder
152 {
153 if (empty($tableName)) {
154 throw new \UnexpectedValueException(
155 'ConnectionPool->getQueryBuilderForTable() requires a connection name to be provided.',
156 1459423448
157 );
158 }
159
160 return $this->getConnectionForTable($tableName)->createQueryBuilder();
161 }
162
163 /**
164 * Returns an array containing the names of all currently configured connections.
165 *
166 * This method should only be used in edge cases. Use getConnectionForTable() so
167 * that the tablename<>databaseConnection mapping will be taken into account.
168 *
169 * @internal
170 * @return array
171 */
172 public function getConnectionNames(): array
173 {
174 return array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']);
175 }
176 }