PDODriverAbstract   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 212
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 73
dl 0
loc 212
rs 10
c 0
b 0
f 0
wmc 28

13 Methods

Rating   Name   Duplication   Size   Complexity  
A bindParams() 0 19 5
A getDSN() 0 18 3
A multi_query() 0 11 2
A getServerInfo() 0 2 1
A disconnect() 0 4 1
A prepared_query() 0 10 2
A getClientInfo() 0 2 1
A multi_callback_query() 0 11 2
A __escape() 0 2 1
A __getResult() 0 8 3
A raw_query() 0 2 1
A connect() 0 33 5
A insertID() 0 2 1
1
<?php
2
/**
3
 * Class PDODriverAbstract
4
 *
5
 * @filesource   PDODriverAbstract.php
6
 * @created      28.06.2017
7
 * @package      chillerlan\Database\Drivers
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2017 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\Database\Drivers;
14
15
use PDO, PDOStatement;
16
17
/**
18
 * @property \PDO $db
19
 */
20
abstract class PDODriverAbstract extends DriverAbstract{
21
22
	/**
23
	 * The PDO drivername which is being used in the DSN
24
	 *
25
	 * @var string
26
	 */
27
	protected string $drivername;
28
29
	/**
30
	 * Some basic PDO options
31
	 *
32
	 * @link http://php.net/manual/pdo.getattribute.php
33
	 * @link http://php.net/manual/pdo.constants.php
34
	 *
35
	 * @var array
36
	 */
37
	protected $pdo_options = [
38
		PDO::ATTR_CASE              => PDO::CASE_NATURAL,
39
		PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
40
		PDO::ATTR_STRINGIFY_FETCHES => false,
41
		PDO::ATTR_EMULATE_PREPARES  => false, // will break mssql
42
	];
43
44
	/**
45
	 * @var array
46
	 */
47
	protected $pdo_stmt_options = [];
48
49
	/**
50
	 * Returns a DSN string using the given options
51
	 *
52
	 * @return string DSN
53
	 */
54
	protected function getDSN():string{
55
		$dsn = $this->drivername;
56
57
		if($this->options->socket){
58
			$dsn .= ':unix_socket='.$this->options->socket; // @codeCoverageIgnore
59
		}
60
		else{
61
			$dsn .= ':host='.$this->options->host;
62
63
			if(is_numeric($this->options->port)){
64
				$dsn .= ';port='.$this->options->port;
65
			}
66
67
		}
68
69
		$dsn .= ';dbname='.$this->options->database;
70
71
		return $dsn;
72
	}
73
74
	/** @inheritdoc */
75
	public function connect():DriverInterface{
76
77
		if($this->db instanceof PDO){
0 ignored issues
show
introduced by
$this->db is always a sub-type of PDO.
Loading history...
78
			return $this;
79
		}
80
81
		try{
82
			// @codeCoverageIgnoreStart
83
			if($this->options->use_ssl){
84
				$this->pdo_options += [
85
					PDO::MYSQL_ATTR_SSL_KEY    => $this->options->ssl_key,
86
					PDO::MYSQL_ATTR_SSL_CERT   => $this->options->ssl_cert,
87
					PDO::MYSQL_ATTR_SSL_CA     => $this->options->ssl_ca,
88
					PDO::MYSQL_ATTR_SSL_CAPATH => $this->options->ssl_capath,
89
					PDO::MYSQL_ATTR_SSL_CIPHER => $this->options->ssl_cipher,
90
				];
91
			}
92
			// @codeCoverageIgnoreEnd
93
94
			$this->db = new PDO($this->getDSN(), $this->options->username, $this->options->password, $this->pdo_options);
95
96
			return $this;
97
		}
98
		catch(\Exception $e){
99
100
			// PDOMSSQL workaround
101
			// @codeCoverageIgnoreStart
102
			if(trim(explode(':', $e->getMessage(), 2)[0]) === 'SQLSTATE[IMSSP]'){
103
				return $this;
104
			}
105
			// @codeCoverageIgnoreEnd
106
107
			throw new DriverException('db error: [PDODriver '.$this->drivername.']: '.$e->getMessage());
108
		}
109
	}
110
111
	/** @inheritdoc */
112
	public function disconnect():bool{
113
		$this->db = null;
114
115
		return true;
116
	}
117
118
	/** @inheritdoc */
119
	public function getClientInfo():string{
120
		return $this->db->getAttribute(PDO::ATTR_CLIENT_VERSION);
121
	}
122
123
	/** @inheritdoc */
124
	public function getServerInfo():?string{
125
		return $this->db->getAttribute(PDO::ATTR_SERVER_INFO);
126
	}
127
128
	/** @inheritdoc */
129
	protected function __escape(string $data):string {
130
		return $this->db->quote($data);
131
	}
132
133
	/**
134
	 * Returns the last insert id (if present)
135
	 * @link http://php.net/manual/pdo.lastinsertid.php
136
	 * @return string
137
	 * @codeCoverageIgnore
138
	 */
139
	protected function insertID():string{
140
		return $this->db->lastInsertId();
141
	}
142
143
	/**
144
	 * @param \PDOStatement $stmt
145
	 * @param array         $values
146
	 *
147
	 * @return void
148
	 */
149
	protected function bindParams(PDOStatement &$stmt, array $values){
150
		$param_no = 1;
151
152
		foreach($values as $v){
153
			$t    = gettype($v);
154
			$type = PDO::PARAM_STR;
155
156
			if($t === 'boolean'){
157
				$type = PDO::PARAM_BOOL; // @codeCoverageIgnore
158
			}
159
			elseif($t === 'integer'){
160
				$type = PDO::PARAM_INT;
161
			}
162
			elseif($t === 'NULL'){
163
				$type = PDO::PARAM_NULL; // @codeCoverageIgnore
164
			}
165
166
			$stmt->bindValue($param_no, $v, $type);
167
			$param_no++;
168
		}
169
	}
170
171
	/**
172
	 * @param             $stmt
173
	 * @param string|null $index
174
	 * @param bool        $assoc
175
	 *
176
	 * @return bool|\chillerlan\Database\Result
177
	 */
178
	protected function __getResult($stmt, string $index = null, bool $assoc = null){
179
		$assoc = $assoc ?? true;
180
181
		if(is_bool($stmt)){
182
			return $stmt; // @codeCoverageIgnore
183
		}
184
185
		return parent::getResult([$stmt, 'fetch'], [$assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM], $index, $assoc);
186
	}
187
188
	/** @inheritdoc */
189
	protected function raw_query(string $sql, string $index = null, bool $assoc = null){
190
		return $this->__getResult($this->db->query($sql), $index, $assoc);
191
	}
192
193
	/** @inheritdoc */
194
	protected function prepared_query(string $sql, array $values = null, string $index = null, bool $assoc = null){
195
		$stmt = $this->db->prepare($sql, $this->pdo_stmt_options);
196
197
		if(!empty($values)){
198
			$this->bindParams($stmt, $values);
199
		}
200
201
		$stmt->execute();
202
203
		return $this->__getResult($stmt, $index, $assoc);
204
	}
205
206
	/** @inheritdoc */
207
	protected function multi_query(string $sql, array $values){
208
		$stmt = $this->db->prepare($sql, $this->pdo_stmt_options);
209
210
		foreach($values as $row){
211
			$this->bindParams($stmt, $row);
212
			$stmt->execute();
213
		}
214
215
		unset($stmt);
216
217
		return true;
218
	}
219
220
	/** @inheritdoc */
221
	protected function multi_callback_query(string $sql, iterable $data, $callback){
222
		$stmt = $this->db->prepare($sql, $this->pdo_stmt_options);
223
224
		foreach($data as $k => $row){
225
			$this->bindParams($stmt, call_user_func_array($callback, [$row, $k]));
226
			$stmt->execute();
227
		}
228
229
		unset($stmt);
230
231
		return true;
232
	}
233
234
}
235