Completed
Push — master ( 250eb3...a7da2e )
by Morris
31:43 queued 19:10
created

ConnectionFactory::getConnection()   C

Complexity

Conditions 8
Paths 15

Size

Total Lines 40
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 31
nc 15
nop 2
dl 0
loc 40
rs 5.3846
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
			case 'sqlite3':
141
				$journalMode = $additionalConnectionParams['sqlite.journal_mode'];
142
				$additionalConnectionParams['platform'] = new OCSqlitePlatform();
143
				$eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode));
144
				break;
145
		}
146
		/** @var Connection $connection */
147
		$connection = DriverManager::getConnection(
148
			array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams),
149
			new Configuration(),
150
			$eventManager
151
		);
152
		return $connection;
153
	}
154
155
	/**
156
	 * @brief Normalize DBMS type
157
	 * @param string $type DBMS type
158
	 * @return string Normalized DBMS type
159
	 */
160
	public function normalizeType($type) {
161
		return $type === 'sqlite' ? 'sqlite3' : $type;
162
	}
163
164
	/**
165
	 * Checks whether the specified DBMS type is valid.
166
	 *
167
	 * @param string $type
168
	 * @return bool
169
	 */
170
	public function isValidType($type) {
171
		$normalizedType = $this->normalizeType($type);
172
		return isset($this->defaultConnectionParams[$normalizedType]);
173
	}
174
175
	/**
176
	 * Create the connection parameters for the config
177
	 *
178
	 * @return array
179
	 */
180
	public function createConnectionParams() {
181
		$type = $this->config->getValue('dbtype', 'sqlite');
182
183
		$connectionParams = [
184
			'user' => $this->config->getValue('dbuser', ''),
185
			'password' => $this->config->getValue('dbpassword', ''),
186
		];
187
		$name = $this->config->getValue('dbname', 'owncloud');
188
189
		if ($this->normalizeType($type) === 'sqlite3') {
190
			$dataDir = $this->config->getValue("datadirectory", \OC::$SERVERROOT . '/data');
191
			$connectionParams['path'] = $dataDir . '/' . $name . '.db';
192
		} else {
193
			$host = $this->config->getValue('dbhost', '');
194
			if (strpos($host, ':')) {
195
				// Host variable may carry a port or socket.
196
				list($host, $portOrSocket) = explode(':', $host, 2);
197
				if (ctype_digit($portOrSocket)) {
198
					$connectionParams['port'] = $portOrSocket;
199
				} else {
200
					$connectionParams['unix_socket'] = $portOrSocket;
201
				}
202
			}
203
			$connectionParams['host'] = $host;
204
			$connectionParams['dbname'] = $name;
205
		}
206
207
		$connectionParams['tablePrefix'] = $this->config->getValue('dbtableprefix', 'oc_');
208
		$connectionParams['sqlite.journal_mode'] = $this->config->getValue('sqlite.journal_mode', 'WAL');
209
210
		//additional driver options, eg. for mysql ssl
211
		$driverOptions = $this->config->getValue('dbdriveroptions', null);
212
		if ($driverOptions) {
213
			$connectionParams['driverOptions'] = $driverOptions;
214
		}
215
216
		// set default table creation options
217
		$connectionParams['defaultTableOptions'] = [
218
			'collate' => 'utf8_bin',
219
			'tablePrefix' => $connectionParams['tablePrefix']
220
		];
221
222
		if ($this->config->getValue('mysql.utf8mb4', false)) {
223
			$connectionParams['defaultTableOptions'] = [
224
				'collate' => 'utf8mb4_bin',
225
				'charset' => 'utf8mb4',
226
				'row_format' => 'compressed',
227
				'tablePrefix' => $connectionParams['tablePrefix']
228
			];
229
		}
230
231
		return $connectionParams;
232
	}
233
}
234