Completed
Pull Request — master (#6563)
by Joas
14:47
created

ConnectionFactory::getConnection()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 44
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 34
nc 16
nop 2
dl 0
loc 44
rs 4.909
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Andreas Fischer <[email protected]>
6
 * @author Jörn Friedrich Dreyer <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Thomas Müller <[email protected]>
10
 *
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OC\DB;
28
29
use Doctrine\Common\EventManager;
30
use Doctrine\DBAL\Configuration;
31
use Doctrine\DBAL\DriverManager;
32
use Doctrine\DBAL\Event\Listeners\OracleSessionInit;
33
use Doctrine\DBAL\Event\Listeners\SQLSessionInit;
34
use OC\SystemConfig;
35
36
/**
37
 * Takes care of creating and configuring Doctrine connections.
38
 */
39
class ConnectionFactory {
40
	/**
41
	 * @var array
42
	 *
43
	 * Array mapping DBMS type to default connection parameters passed to
44
	 * \Doctrine\DBAL\DriverManager::getConnection().
45
	 */
46
	protected $defaultConnectionParams = [
47
		'mysql' => [
48
			'adapter' => '\OC\DB\AdapterMySQL',
49
			'charset' => 'UTF8',
50
			'driver' => 'pdo_mysql',
51
			'wrapperClass' => 'OC\DB\Connection',
52
		],
53
		'oci' => [
54
			'adapter' => '\OC\DB\AdapterOCI8',
55
			'charset' => 'AL32UTF8',
56
			'driver' => 'oci8',
57
			'wrapperClass' => 'OC\DB\OracleConnection',
58
		],
59
		'pgsql' => [
60
			'adapter' => '\OC\DB\AdapterPgSql',
61
			'driver' => 'pdo_pgsql',
62
			'wrapperClass' => 'OC\DB\Connection',
63
		],
64
		'sqlite3' => [
65
			'adapter' => '\OC\DB\AdapterSqlite',
66
			'driver' => 'pdo_sqlite',
67
			'wrapperClass' => 'OC\DB\Connection',
68
		],
69
	];
70
71
	/** @var SystemConfig */
72
	private $config;
73
74
	/**
75
	 * ConnectionFactory constructor.
76
	 *
77
	 * @param SystemConfig $systemConfig
78
	 */
79
	public function __construct(SystemConfig $systemConfig) {
80
		$this->config = $systemConfig;
81
		if ($this->config->getValue('mysql.utf8mb4', false)) {
82
			$this->defaultConnectionParams['mysql']['charset'] = 'utf8mb4';
83
		}
84
	}
85
86
	/**
87
	 * @brief Get default connection parameters for a given DBMS.
88
	 * @param string $type DBMS type
89
	 * @throws \InvalidArgumentException If $type is invalid
90
	 * @return array Default connection parameters.
91
	 */
92
	public function getDefaultConnectionParams($type) {
93
		$normalizedType = $this->normalizeType($type);
94
		if (!isset($this->defaultConnectionParams[$normalizedType])) {
95
			throw new \InvalidArgumentException("Unsupported type: $type");
96
		}
97
		$result = $this->defaultConnectionParams[$normalizedType];
98
		// \PDO::MYSQL_ATTR_FOUND_ROWS may not be defined, e.g. when the MySQL
99
		// driver is missing. In this case, we won't be able to connect anyway.
100
		if ($normalizedType === 'mysql' && defined('\PDO::MYSQL_ATTR_FOUND_ROWS')) {
101
			$result['driverOptions'] = array(
102
				\PDO::MYSQL_ATTR_FOUND_ROWS => true,
103
			);
104
		}
105
		return $result;
106
	}
107
108
	/**
109
	 * @brief Get default connection parameters for a given DBMS.
110
	 * @param string $type DBMS type
111
	 * @param array $additionalConnectionParams Additional connection parameters
112
	 * @return \OC\DB\Connection
113
	 */
114
	public function getConnection($type, $additionalConnectionParams) {
115
		$normalizedType = $this->normalizeType($type);
116
		$eventManager = new EventManager();
117
		switch ($normalizedType) {
118
			case 'mysql':
119
				$eventManager->addEventSubscriber(
120
					new SQLSessionInit("SET SESSION AUTOCOMMIT=1"));
121
				break;
122
			case 'oci':
123
				$eventManager->addEventSubscriber(new OracleSessionInit);
124
				// the driverOptions are unused in dbal and need to be mapped to the parameters
125
				if (isset($additionalConnectionParams['driverOptions'])) {
126
					$additionalConnectionParams = array_merge($additionalConnectionParams, $additionalConnectionParams['driverOptions']);
127
				}
128
				$host = $additionalConnectionParams['host'];
129
				$port = isset($additionalConnectionParams['port']) ? $additionalConnectionParams['port'] : null;
130
				$dbName = $additionalConnectionParams['dbname'];
131
132
				// we set the connect string as dbname and unset the host to coerce doctrine into using it as connect string
133
				if ($host === '') {
134
					$additionalConnectionParams['dbname'] = $dbName; // use dbname as easy connect name
135
				} else {
136
					$additionalConnectionParams['dbname'] = '//' . $host . (!empty($port) ? ":{$port}" : "") . '/' . $dbName;
137
				}
138
				unset($additionalConnectionParams['host']);
139
				break;
140
141
			case 'pgsql':
142
				$additionalConnectionParams['platform'] = new OCPostgreSqlPlatform();
143
				break;
144
			case 'sqlite3':
145
				$journalMode = $additionalConnectionParams['sqlite.journal_mode'];
146
				$additionalConnectionParams['platform'] = new OCSqlitePlatform();
147
				$eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode));
148
				break;
149
		}
150
		/** @var Connection $connection */
151
		$connection = DriverManager::getConnection(
152
			array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams),
153
			new Configuration(),
154
			$eventManager
155
		);
156
		return $connection;
157
	}
158
159
	/**
160
	 * @brief Normalize DBMS type
161
	 * @param string $type DBMS type
162
	 * @return string Normalized DBMS type
163
	 */
164
	public function normalizeType($type) {
165
		return $type === 'sqlite' ? 'sqlite3' : $type;
166
	}
167
168
	/**
169
	 * Checks whether the specified DBMS type is valid.
170
	 *
171
	 * @param string $type
172
	 * @return bool
173
	 */
174
	public function isValidType($type) {
175
		$normalizedType = $this->normalizeType($type);
176
		return isset($this->defaultConnectionParams[$normalizedType]);
177
	}
178
179
	/**
180
	 * Create the connection parameters for the config
181
	 *
182
	 * @return array
183
	 */
184
	public function createConnectionParams() {
185
		$type = $this->config->getValue('dbtype', 'sqlite');
186
187
		$connectionParams = [
188
			'user' => $this->config->getValue('dbuser', ''),
189
			'password' => $this->config->getValue('dbpassword', ''),
190
		];
191
		$name = $this->config->getValue('dbname', 'owncloud');
192
193
		if ($this->normalizeType($type) === 'sqlite3') {
194
			$dataDir = $this->config->getValue("datadirectory", \OC::$SERVERROOT . '/data');
195
			$connectionParams['path'] = $dataDir . '/' . $name . '.db';
196
		} else {
197
			$host = $this->config->getValue('dbhost', '');
198
			if (strpos($host, ':')) {
199
				// Host variable may carry a port or socket.
200
				list($host, $portOrSocket) = explode(':', $host, 2);
201
				if (ctype_digit($portOrSocket)) {
202
					$connectionParams['port'] = $portOrSocket;
203
				} else {
204
					$connectionParams['unix_socket'] = $portOrSocket;
205
				}
206
			}
207
			$connectionParams['host'] = $host;
208
			$connectionParams['dbname'] = $name;
209
		}
210
211
		$connectionParams['tablePrefix'] = $this->config->getValue('dbtableprefix', 'oc_');
212
		$connectionParams['sqlite.journal_mode'] = $this->config->getValue('sqlite.journal_mode', 'WAL');
213
214
		//additional driver options, eg. for mysql ssl
215
		$driverOptions = $this->config->getValue('dbdriveroptions', null);
216
		if ($driverOptions) {
217
			$connectionParams['driverOptions'] = $driverOptions;
218
		}
219
220
		// set default table creation options
221
		$connectionParams['defaultTableOptions'] = [
222
			'collate' => 'utf8_bin',
223
			'tablePrefix' => $connectionParams['tablePrefix']
224
		];
225
226
		if ($this->config->getValue('mysql.utf8mb4', false)) {
227
			$connectionParams['defaultTableOptions'] = [
228
				'collate' => 'utf8mb4_bin',
229
				'charset' => 'utf8mb4',
230
				'row_format' => 'compressed',
231
				'tablePrefix' => $connectionParams['tablePrefix']
232
			];
233
		}
234
235
		return $connectionParams;
236
	}
237
}
238