Passed
Push — master ( f381ab...b62f3e )
by Aimeos
02:34
created

DBAL::normalize()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
c 0
b 0
f 0
dl 0
loc 22
rs 8.8333
cc 7
nc 12
nop 1
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2016-2022
6
 * @package Base
7
 * @subpackage DB
8
 */
9
10
11
namespace Aimeos\Base\DB\Connection;
12
13
14
/**
15
 * Database connection class for DBAL connections
16
 *
17
 * @package Base
18
 * @subpackage DB
19
 */
20
class DBAL extends Base implements Iface
21
{
22
	private $connection;
23
	private $txnumber = 0;
24
	private $stmts;
25
26
27
	/**
28
	 * Initializes the DBAL connection object
29
	 *
30
	 * @param array $params Associative list of connection parameters
31
	 */
32
	public function __construct( array $params )
33
	{
34
		parent::__construct( $this->normalize( $params ) );
35
36
		$this->stmts = $params['stmt'] ?? [];
37
		$this->connect();
38
	}
39
40
41
	/**
42
	 * Closes the connection to the database server
43
	 *
44
	 * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining
45
	 */
46
	public function close() : Iface
47
	{
48
		if( $this->inTransaction() ) {
49
			$this->rollback();
50
		}
51
52
		$this->connection->close();
53
		return $this;
54
	}
55
56
57
	/**
58
	 * Connects (or reconnects) to the database server
59
	 *
60
	 * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining
61
	 */
62
	public function connect() : \Aimeos\Base\DB\Connection\Iface
63
	{
64
		if( $this->connection && $this->connection->ping() ) {
65
			return $this;
66
		}
67
68
		$param = $this->getParameters();
69
		$param['driverOptions'][\PDO::ATTR_CASE] = \PDO::CASE_NATURAL;
70
		$param['driverOptions'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_EXCEPTION;
71
		$param['driverOptions'][\PDO::ATTR_ORACLE_NULLS] = \PDO::NULL_NATURAL;
72
		$param['driverOptions'][\PDO::ATTR_STRINGIFY_FETCHES] = false;
73
74
		$conn = $this->connection;
75
76
		$this->connection = \Doctrine\DBAL\DriverManager::getConnection( $param );
77
		$this->txnumber = 0;
78
79
		unset( $conn );
80
81
		foreach( $this->stmts as $stmt ) {
82
			$this->create( $stmt )->execute()->finish();
83
		}
84
85
		return $this;
86
	}
87
88
89
	/**
90
	 * Creates a DBAL database statement
91
	 *
92
	 * @param string $sql SQL statement, maybe with place holders
93
	 * @return \Aimeos\Base\DB\Statement\Iface DBAL statement object
94
	 * @throws \Aimeos\Base\DB\Exception if type is invalid or the DBAL object throws an exception
95
	 */
96
	public function create( string $sql ) : \Aimeos\Base\DB\Statement\Iface
97
	{
98
		try
99
		{
100
			if( strpos( $sql, '?' ) === false ) {
101
				return new \Aimeos\Base\DB\Statement\DBAL\Simple( $this, $sql );
102
			}
103
104
			return new \Aimeos\Base\DB\Statement\DBAL\Prepared( $this, $sql );
105
		}
106
		catch( \Exception $e )
107
		{
108
			throw new \Aimeos\Base\DB\Exception( $e->getMessage(), $e->getCode() );
109
		}
110
	}
111
112
113
	/**
114
	 * Returns the underlying connection object
115
	 *
116
	 * @return \Doctrine\DBAL\Connection Underlying connection object
117
	 */
118
	public function getRawObject()
119
	{
120
		return $this->connection;
121
	}
122
123
124
	/**
125
	 * Checks if a transaction is currently running
126
	 *
127
	 * @return bool True if transaction is currently running, false if not
128
	 */
129
	public function inTransaction() : bool
130
	{
131
		$conn = $this->connection->getWrappedConnection();
132
133
		if( $conn instanceof \PDO ) {
134
			return $conn->inTransaction();
135
		}
136
137
		return $conn->getNativeConnection()->inTransaction();
138
	}
139
140
141
	/**
142
	 * Starts a transaction for this connection.
143
	 *
144
	 * Transactions can't be nested and a new transaction can only be started
145
	 * if the previous transaction was committed or rolled back before.
146
	 *
147
	 * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining
148
	 */
149
	public function begin() : Iface
150
	{
151
		if( $this->txnumber === 0 )
152
		{
153
			if( $this->connection->getWrappedConnection()->beginTransaction() === false ) {
154
				throw new \Aimeos\Base\DB\Exception( 'Unable to start new transaction' );
155
			}
156
		}
157
158
		$this->txnumber++;
159
		return $this;
160
	}
161
162
163
	/**
164
	 * Commits the changes done inside of the transaction to the storage.
165
	 *
166
	 * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining
167
	 */
168
	public function commit() : Iface
169
	{
170
		if( $this->txnumber === 1 )
171
		{
172
			if( $this->connection->getWrappedConnection()->commit() === false ) {
173
				throw new \Aimeos\Base\DB\Exception( 'Failed to commit transaction' );
174
			}
175
		}
176
177
		$this->txnumber--;
178
		return $this;
179
	}
180
181
182
	/**
183
	 * Discards the changes done inside of the transaction.
184
	 *
185
	 * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining
186
	 */
187
	public function rollback() : Iface
188
	{
189
		if( $this->txnumber === 1 )
190
		{
191
			if( $this->connection->getWrappedConnection()->rollBack() === false ) {
192
				throw new \Aimeos\Base\DB\Exception( 'Failed to roll back transaction' );
193
			}
194
		}
195
196
		$this->txnumber--;
197
		return $this;
198
	}
199
200
201
	/**
202
	 * Normalizes the the connection parameters
203
	 *
204
	 * @param array $params Associative list of connection parameters
205
	 * @return array Normalized connection parameters
206
	 */
207
	protected function normalize( array $params ) : array
208
	{
209
		$adapter = $params['adapter'] ?? 'mysql';
210
211
		$params['user'] = $params['username'] ?? null;
212
		$params['dbname'] = $params['database'] ?? null;
213
214
		if( $socket = $params['socket'] ?? null ) {
215
			$params['unix_socket'] = $socket;
216
		}
217
218
		switch( $adapter )
219
		{
220
			case 'mysql': $params['driver'] = 'pdo_mysql'; break;
221
			case 'oracle': $params['driver'] = 'pdo_oci'; break;
222
			case 'pgsql': $params['driver'] = 'pdo_pgsql'; break;
223
			case 'sqlite': $params['driver'] = 'pdo_sqlite'; break;
224
			case 'sqlsrv': $params['driver'] = 'pdo_sqlsrv'; break;
225
			default: $params['driver'] = $adapter;
226
		}
227
228
		return $params;
229
	}
230
}
231