Completed
Pull Request — master (#7210)
by Lukas
15:36
created

ConnectionFactory   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 196
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 196
rs 10
c 0
b 0
f 0
wmc 23
lcom 1
cbo 4

6 Methods

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