PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Tracker / Db / Mysqli.php
matomo / app / core / Tracker / Db Last commit date
Pdo 6 years ago DbException.php 6 years ago Mysqli.php 6 years ago
Mysqli.php
459 lines
1 <?php
2 /**
3 * Piwik - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik\Tracker\Db;
10
11 use Exception;
12 use Piwik\Tracker\Db;
13
14 /**
15 * mysqli wrapper
16 *
17 */
18 class Mysqli extends Db
19 {
20 protected $connection = null;
21 protected $host;
22 protected $port;
23 protected $socket;
24 protected $dbname;
25 protected $username;
26 protected $password;
27 protected $charset;
28 protected $activeTransaction = false;
29
30 protected $enable_ssl;
31 protected $ssl_key;
32 protected $ssl_cert;
33 protected $ssl_ca;
34 protected $ssl_ca_path;
35 protected $ssl_cipher;
36 protected $ssl_no_verify;
37
38 /**
39 * Builds the DB object
40 *
41 * @param array $dbInfo
42 * @param string $driverName
43 */
44 public function __construct($dbInfo, $driverName = 'mysql')
45 {
46 if (isset($dbInfo['unix_socket']) && substr($dbInfo['unix_socket'], 0, 1) == '/') {
47 $this->host = null;
48 $this->port = null;
49 $this->socket = $dbInfo['unix_socket'];
50 } elseif (isset($dbInfo['port']) && substr($dbInfo['port'], 0, 1) == '/') {
51 $this->host = null;
52 $this->port = null;
53 $this->socket = $dbInfo['port'];
54 } else {
55 $this->host = $dbInfo['host'];
56 $this->port = (int)$dbInfo['port'];
57 $this->socket = null;
58 }
59 $this->dbname = $dbInfo['dbname'];
60 $this->username = $dbInfo['username'];
61 $this->password = $dbInfo['password'];
62 $this->charset = isset($dbInfo['charset']) ? $dbInfo['charset'] : null;
63
64
65 if(!empty($dbInfo['enable_ssl'])){
66 $this->enable_ssl = $dbInfo['enable_ssl'];
67 }
68 if(!empty($dbInfo['ssl_key'])){
69 $this->ssl_key = $dbInfo['ssl_key'];
70 }
71 if(!empty($dbInfo['ssl_cert'])){
72 $this->ssl_cert = $dbInfo['ssl_cert'];
73 }
74 if(!empty($dbInfo['ssl_ca'])){
75 $this->ssl_ca = $dbInfo['ssl_ca'];
76 }
77 if(!empty($dbInfo['ssl_ca_path'])){
78 $this->ssl_ca_path = $dbInfo['ssl_ca_path'];
79 }
80 if(!empty($dbInfo['ssl_cipher'])){
81 $this->ssl_cipher = $dbInfo['ssl_cipher'];
82 }
83 if(!empty($dbInfo['ssl_no_verify'])){
84 $this->ssl_no_verify = $dbInfo['ssl_no_verify'];
85 }
86
87 }
88
89 /**
90 * Destructor
91 */
92 public function __destruct()
93 {
94 $this->connection = null;
95 }
96
97 /**
98 * Connects to the DB
99 *
100 * @throws Exception|DbException if there was an error connecting the DB
101 */
102 public function connect()
103 {
104 if (self::$profiling) {
105 $timer = $this->initProfiler();
106 }
107
108 $this->connection = mysqli_init();
109
110
111 if($this->enable_ssl){
112 mysqli_ssl_set($this->connection, $this->ssl_key, $this->ssl_cert, $this->ssl_ca, $this->ssl_ca_path, $this->ssl_cipher);
113 }
114
115 // Make sure MySQL returns all matched rows on update queries including
116 // rows that actually didn't have to be updated because the values didn't
117 // change. This matches common behaviour among other database systems.
118 // See #6296 why this is important in tracker
119 $flags = MYSQLI_CLIENT_FOUND_ROWS;
120 if ($this->enable_ssl){
121 $flags = $flags | MYSQLI_CLIENT_SSL;
122 }
123 if ($this->ssl_no_verify && defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT')){
124 $flags = $flags | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
125 }
126 mysqli_real_connect($this->connection, $this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket, $flags);
127 if (!$this->connection || mysqli_connect_errno()) {
128 throw new DbException("Connect failed: " . mysqli_connect_error());
129 }
130
131 if ($this->charset && !mysqli_set_charset($this->connection, $this->charset)) {
132 throw new DbException("Set Charset failed: " . mysqli_error($this->connection));
133 }
134
135 $this->password = '';
136
137 if (self::$profiling && isset($timer)) {
138 $this->recordQueryProfile('connect', $timer);
139 }
140 }
141
142 /**
143 * Disconnects from the server
144 */
145 public function disconnect()
146 {
147 mysqli_close($this->connection);
148 $this->connection = null;
149 }
150
151 /**
152 * @param \mysqli_stmt $stmt
153 * @param $fields
154 * @return array|bool|false
155 */
156 private function fetchResult($stmt, $fields) {
157
158 $values = array_fill(0, count($fields), null);
159
160 $refs = array();
161 foreach ($values as $i => &$f) {
162 $refs[$i] = &$f;
163 }
164
165 call_user_func_array(array($stmt, 'bind_result'), $values);
166
167 $result = $stmt->fetch();
168
169 if ($result === null || $result === false) {
170 $stmt->reset();
171 return false;
172 }
173
174 $val = array();
175 foreach ($values as $key => $value) {
176 $val[] = $value;
177 }
178
179 $row = array_combine($fields, $values);
180
181 return $row;
182 }
183
184 /**
185 * Returns an array containing all the rows of a query result, using optional bound parameters.
186 *
187 * @see query()
188 *
189 * @param string $query Query
190 * @param array $parameters Parameters to bind
191 * @return array
192 * @throws Exception|DbException if an exception occurred
193 */
194 public function fetchAll($query, $parameters = array())
195 {
196 try {
197 if (self::$profiling) {
198 $timer = $this->initProfiler();
199 }
200
201 list($stmt, $fields) = $this->executeQuery($query, $parameters);
202
203 $rows = array();
204 while ($row = $this->fetchResult($stmt, $fields)) {
205 $rows[] = $row;
206 }
207
208 $stmt->free_result();
209 $stmt->close();
210
211 if (self::$profiling && isset($timer)) {
212 $this->recordQueryProfile($query, $timer);
213 }
214 return $rows;
215 } catch (Exception $e) {
216 throw new DbException("Error query: " . $e->getMessage());
217 }
218 }
219
220 /**
221 * Returns the first row of a query result, using optional bound parameters.
222 *
223 * @see query()
224 *
225 * @param string $query Query
226 * @param array $parameters Parameters to bind
227 *
228 * @return array
229 *
230 * @throws DbException if an exception occurred
231 */
232 public function fetch($query, $parameters = array())
233 {
234 try {
235 if (self::$profiling) {
236 $timer = $this->initProfiler();
237 }
238
239 list($stmt, $fields) = $this->executeQuery($query, $parameters);
240
241 $row = $this->fetchResult($stmt, $fields);
242
243 $stmt->free_result();
244 $stmt->close();
245
246 if (self::$profiling && isset($timer)) {
247 $this->recordQueryProfile($query, $timer);
248 }
249 if ($row === null) {
250 $row = false;
251 }
252 return $row;
253 } catch (Exception $e) {
254 throw new DbException("Error query: " . $e->getMessage());
255 }
256 }
257
258 /**
259 * Executes a query, using optional bound parameters.
260 *
261 * @param string $query Query
262 * @param array|string $parameters Parameters to bind array('idsite'=> 1)
263 *
264 * @return bool|resource false if failed
265 * @throws DbException if an exception occurred
266 */
267 public function query($query, $parameters = array())
268 {
269 if (is_null($this->connection)) {
270 return false;
271 }
272
273 try {
274 if (self::$profiling) {
275 $timer = $this->initProfiler();
276 }
277
278 list($stmt, $fields) = $this->executeQuery($query, $parameters);
279
280 if (self::$profiling && isset($timer)) {
281 $this->recordQueryProfile($query, $timer);
282 }
283 return $stmt;
284 } catch (Exception $e) {
285 throw new DbException("Error query: " . $e->getMessage() . "
286 In query: $query
287 Parameters: " . var_export($parameters, true), $e->getCode());
288 }
289 }
290
291 /**
292 * Returns the last inserted ID in the DB
293 *
294 * @return int
295 */
296 public function lastInsertId()
297 {
298 return mysqli_insert_id($this->connection);
299 }
300
301 private function executeQuery($sql, $bind) {
302
303 $stmt = mysqli_prepare($this->connection, $sql);
304
305 if (!$stmt) {
306 throw new DbException('preparing query failed: ' . mysqli_error($this->connection) . ' : ' . $sql);
307 }
308
309 if (!is_array($bind)) {
310 $bind = array($bind);
311 }
312
313 if (!empty($bind)) {
314 array_unshift($bind, str_repeat('s', count($bind)));
315 $refs = array();
316 foreach ($bind as $key => $value) {
317 $refs[$key] = &$bind[$key];
318 }
319
320 call_user_func_array(array($stmt, 'bind_param'), $refs);
321 }
322
323 $stmtResult = $stmt->execute();
324
325 if ($stmtResult === false) {
326 throw new DbException("Mysqli statement execute error : " . $stmt->error, $stmt->errno);
327 }
328
329 if (!empty($stmt->error)) {
330 throw new DbException('executeQuery() failed: ' . mysqli_error($this->connection) . ' : ' . $sql);
331 }
332
333 $metaResults = $stmt->result_metadata();
334
335 if ($stmt->errno) {
336 throw new DbException("Mysqli statement metadata error: " . $stmt->error, $stmt->errno);
337 }
338
339 $fields = array();
340 if ($metaResults) {
341 $fetchedFields = $metaResults->fetch_fields();
342 foreach ($fetchedFields as $fetchedField) {
343 $fields[] = $fetchedField->name;
344 }
345 $stmt->store_result();
346 }
347
348 return array($stmt, $fields);
349 }
350 /**
351 * Input is a prepared SQL statement and parameters
352 * Returns the SQL statement
353 *
354 * @param string $query
355 * @param array $parameters
356 * @return string
357 */
358 private function prepare($query, $parameters)
359 {
360 if (!$parameters) {
361 $parameters = array();
362 } elseif (!is_array($parameters)) {
363 $parameters = array($parameters);
364 }
365 $this->paramNb = 0;
366 $this->params = & $parameters;
367 $query = preg_replace_callback('/\?/', array($this, 'replaceParam'), $query);
368 return $query;
369 }
370
371 public function replaceParam($match)
372 {
373 $param = & $this->params[$this->paramNb];
374 $this->paramNb++;
375 if ($param === null) {
376 return 'NULL';
377 } else {
378 return "'" . addslashes($param) . "'";
379 }
380 }
381
382 public function isErrNo($e, $errno)
383 {
384 return \Piwik\Db\Adapter\Mysqli::isMysqliErrorNumber($e, $this->connection, $errno);
385 }
386
387 /**
388 * Return number of affected rows in last query
389 *
390 * @param mixed $queryResult Result from query()
391 * @return int
392 */
393 public function rowCount($queryResult)
394 {
395 if (!empty($queryResult) && is_object($queryResult) && $queryResult instanceof \mysqli_stmt) {
396 return $queryResult->affected_rows;
397 }
398 return mysqli_affected_rows($this->connection);
399 }
400
401 /**
402 * Start Transaction
403 * @return string TransactionID
404 */
405 public function beginTransaction()
406 {
407 if (!$this->activeTransaction === false) {
408 return;
409 }
410
411 if ($this->connection->autocommit(false)) {
412 $this->activeTransaction = uniqid();
413 return $this->activeTransaction;
414 }
415 }
416
417 /**
418 * Commit Transaction
419 * @param $xid
420 * @throws DbException
421 * @internal param TransactionID $string from beginTransaction
422 */
423 public function commit($xid)
424 {
425 if ($this->activeTransaction != $xid || $this->activeTransaction === false) {
426 return;
427 }
428
429 $this->activeTransaction = false;
430
431 if (!$this->connection->commit()) {
432 throw new DbException("Commit failed");
433 }
434
435 $this->connection->autocommit(true);
436 }
437
438 /**
439 * Rollback Transaction
440 * @param $xid
441 * @throws DbException
442 * @internal param TransactionID $string from beginTransaction
443 */
444 public function rollBack($xid)
445 {
446 if ($this->activeTransaction != $xid || $this->activeTransaction === false) {
447 return;
448 }
449
450 $this->activeTransaction = false;
451
452 if (!$this->connection->rollback()) {
453 throw new DbException("Rollback failed");
454 }
455
456 $this->connection->autocommit(true);
457 }
458 }
459