Completed
Push — master ( d9212e...2b3e47 )
by Nazar
04:39
created

_Abstract::queries_count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\DB;
9
use
10
	Exception;
11
12
abstract class _Abstract {
13
	/**
14
	 * Is connection established
15
	 *
16
	 * @var bool
17
	 */
18
	protected $connected = false;
19
	/**
20
	 * DB type, may be used for constructing requests, accounting particular features of current DB (lowercase name)
21
	 *
22
	 * @var string
23
	 */
24
	protected $db_type = '';
25
	/**
26
	 * Current DB
27
	 *
28
	 * @var string
29
	 */
30
	protected $database;
31
	/**
32
	 * Current prefix
33
	 *
34
	 * @var string
35
	 */
36
	protected $prefix;
37
	/**
38
	 * Total time of requests execution
39
	 *
40
	 * @var float
41
	 */
42
	protected $time;
43
	/**
44
	 * Array for storing of data of the last executed request
45
	 *
46
	 * @var array
47
	 */
48
	protected $query = [
49
		'time' => 0,
50
		'text' => ''
51
	];
52
	/**
53
	 * Array for storing data of all executed requests
54
	 *
55
	 * @var array
56
	 */
57
	protected $queries = [
58
		'num'  => 0,
59
		'time' => [],
60
		'text' => []
61
	];
62
	/**
63
	 * Connection time
64
	 *
65
	 * @var float
66
	 */
67
	protected $connecting_time;
68
	/**
69
	 * @var bool
70
	 */
71
	protected $in_transaction = false;
72
	/**
73
	 * Connecting to the DB
74
	 *
75
	 * @param string $database
76
	 * @param string $user
77
	 * @param string $password
78
	 * @param string $host
79
	 * @param string $prefix
80
	 */
81
	abstract public function __construct ($database, $user = '', $password = '', $host = 'localhost', $prefix = '');
1 ignored issue
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
82
	/**
83
	 * Query
84
	 *
85
	 * SQL request into DB
86
	 *
87
	 * @abstract
88
	 *
89
	 * @param string|string[] $query      SQL query string or array, may be a format string in accordance with the first parameter of sprintf() function or may
90
	 *                                    contain markers for prepared statements (but not both at the same time)
91
	 * @param array           $parameters There might be arbitrary number of parameters for formatting SQL statement or for using in prepared statements.<br>
92
	 *                                    If an array provided as second argument - its items will be used, so that you can either specify parameters as an
93
	 *                                    array, or in line.
94
	 *
95
	 * @return bool|object|resource
96
	 */
97 48
	public function q ($query, ...$parameters) {
98 48
		$normalized = $this->normalize_parameters($query, $parameters);
99 48
		if (!$normalized) {
100 2
			return false;
101
		}
102 48
		list($query, $parameters) = $normalized;
103
		/**
104
		 * Executing multiple queries
105
		 */
106 48
		if (is_array($query)) {
107 16
			return $this->execute_multiple($query, $parameters);
108
		}
109 48
		return $this->execute_single($query, $parameters);
110
	}
111
	/**
112
	 * @param string|string[] $query
113
	 * @param array           $parameters
114
	 *
115
	 * @return array|false
1 ignored issue
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
116
	 */
117 48
	protected function normalize_parameters ($query, $parameters) {
118 48
		if (!$query) {
119 2
			return false;
120
		}
121 48
		$query = str_replace('[prefix]', $this->prefix, $query);
122
		/** @noinspection NotOptimalIfConditionsInspection */
123 48
		if (count($parameters) == 1 && is_array($parameters[0])) {
124 42
			$parameters = $parameters[0];
125
		}
126
		return [
127 48
			$query,
128 48
			array_values($parameters)
129
		];
130
	}
131
	/**
132
	 * @param string[] $queries
133
	 * @param string[] $parameters
134
	 *
135
	 * @return bool
136
	 */
137 16
	protected function execute_multiple ($queries, $parameters) {
138 16
		$time_from         = microtime(true);
139 16
		$parameters_server = [];
140 16
		foreach ($queries as &$q) {
141 16
			$q = $this->prepare_query_and_parameters($q, $parameters);
142 16
			if ($q[1]) {
143 2
				$q                 = $q[0];
144 2
				$parameters_server = $parameters;
145 2
				break;
146
			}
147 16
			$q = $q[0];
148
		}
149 16
		unset($q);
150 16
		$this->queries['num'] += count($queries);
151 16
		$result = $this->q_multi_internal($queries, $parameters_server);
152 16
		$this->time += round(microtime(true) - $time_from, 6);
153 16
		return $result;
154
	}
155
	/**
156
	 * @param string   $query
157
	 * @param string[] $parameters
158
	 *
159
	 * @return array
160
	 */
161 48
	protected function prepare_query_and_parameters ($query, $parameters) {
162 48
		if (!$parameters || strpos($query, '?') !== false) {
163 48
			return [$query, $parameters];
164
		}
165 34
		foreach ($parameters as &$parameter) {
166 34
			$parameter = $this->s($parameter, false);
167
		}
168 34
		return [vsprintf($query, $parameters), []];
169
	}
170
	/**
171
	 * @param string   $query
172
	 * @param string[] $parameters
173
	 *
174
	 * @return bool|object|resource
175
	 */
176 48
	protected function execute_single ($query, $parameters) {
177 48
		$time_from = microtime(true);
178 48
		list($query, $parameters) = $this->prepare_query_and_parameters($query, $parameters);
179 48
		$this->query['text'] = $query[0];
180 48
		if (DEBUG) {
181 1
			$this->queries['text'][] = $this->query['text'];
182
		}
183 48
		$result              = $this->q_internal($query, $parameters);
184 48
		$this->query['time'] = round(microtime(true) - $time_from, 6);
185 48
		$this->time += $this->query['time'];
186 48
		if (DEBUG) {
187 1
			$this->queries['time'][] = $this->query['time'];
188
		}
189 48
		++$this->queries['num'];
190 48
		return $result;
191
	}
192
	/**
193
	 * SQL request to DB
194
	 *
195
	 * @abstract
196
	 *
197
	 * @param string   $query
198
	 * @param string[] $parameters If not empty, than server-side prepared statements should be used
199
	 *
200
	 * @return bool|object|resource
201
	 */
202
	abstract protected function q_internal ($query, $parameters = []);
203
	/**
204
	 * Multiple SQL request to DB
205
	 *
206
	 * @abstract
207
	 *
208
	 * @param string[] $query
209
	 * @param string[] $parameters If not empty, than server-side prepared statements should be used
210
	 *
211
	 * @return bool
212
	 */
213 16
	protected function q_multi_internal ($query, $parameters = []) {
214 16
		$result = true;
215 16
		foreach ($query as $q) {
216 16
			$result = $result && $this->q_internal($q, $parameters);
217
		}
218 16
		return $result;
219
	}
220
	/**
221
	 * Fetch
222
	 *
223
	 * Fetch a result row as an associative array
224
	 *
225
	 * @abstract
226
	 *
227
	 * @param false|object|resource $query_result
228
	 * @param bool                  $single_column If <b>true</b> function will return not array with one element, but directly its value
229
	 * @param bool                  $array         If <b>true</b> returns array of associative arrays of all fetched rows
230
	 * @param bool                  $indexed       If <b>false</b> - associative array will be returned
231
	 *
232
	 * @return array[]|false|int|int[]|string|string[]
233
	 */
234
	abstract public function f ($query_result, $single_column = false, $array = false, $indexed = false);
235
	/**
236
	 * Query, Fetch
237
	 *
238
	 * Short for `::f(::q())`, arguments are exactly the same as in `::q()`
239
	 *
240
	 * @param string[] $query
241
	 *
242
	 * @return array|false
243
	 */
244 40
	public function qf (...$query) {
245 40
		return $this->f($this->q(...$query));
246
	}
247
	/**
248
	 * Query, Fetch, Array
249
	 *
250
	 * Short for `::f(::q(), false, true)`, arguments are exactly the same as in `::q()`
251
	 *
252
	 * @param string[] $query
253
	 *
254
	 * @return array[]|false
255
	 */
256 21
	public function qfa (...$query) {
257 21
		return $this->f($this->q(...$query), false, true);
258
	}
259
	/**
260
	 * Query, Fetch, Single
261
	 *
262
	 * Short for `::f(::q(), true)`, arguments are exactly the same as in `::q()`
263
	 *
264
	 * @param string[] $query
265
	 *
266
	 * @return false|int|string
267
	 */
268 30
	public function qfs (...$query) {
269 30
		return $this->f($this->q(...$query), true);
270
	}
271
	/**
272
	 * Query, Fetch, Array, Single
273
	 *
274
	 * Short for `::f(::q(), true, true)`, arguments are exactly the same as in `::q()`
275
	 *
276
	 * @param string[] $query
277
	 *
278
	 * @return false|int[]|string[]
279
	 */
280 35
	public function qfas (...$query) {
281 35
		return $this->f($this->q(...$query), true, true);
282
	}
283
	/**
284
	 * Method for simplified inserting of several rows
285
	 *
286
	 * @param string        $query
287
	 * @param array|array[] $parameters Array of array of parameters for inserting
288
	 * @param bool          $join       If true - inserting of several rows will be combined in one query. For this, be sure, that your query has keyword
289
	 *                                  <i>VALUES</i> in uppercase. Part of query after this keyword will be multiplied with coma separator.
290
	 *
291
	 * @return bool
292
	 */
293 30
	public function insert ($query, $parameters, $join = true) {
294 30
		if (!$query || !$parameters) {
295 2
			return false;
296
		}
297 30
		if ($join) {
298 30
			$query    = explode('VALUES', $query, 2);
299 30
			$query[1] = explode(')', $query[1], 2);
300
			$query    = [
301 30
				$query[0],
302 30
				$query[1][0].')',
303 30
				$query[1][1]
304
			];
305 30
			$query[1] .= str_repeat(",$query[1]", count($parameters) - 1);
306 30
			$query = $query[0].'VALUES'.$query[1].$query[2];
307 30
			return (bool)$this->q(
308
				$query,
309 30
				array_merge(...array_map('array_values', _array($parameters)))
310
			);
311
		} else {
312 2
			$result = true;
313 2
			foreach ($parameters as $p) {
314 2
				$result = $result && (bool)$this->q($query, $p);
315
			}
316 2
			return $result;
317
		}
318
	}
319
	/**
320
	 * Id
321
	 *
322
	 * Get id of last inserted row
323
	 *
324
	 * @abstract
325
	 *
326
	 * @return int
327
	 */
328
	abstract public function id ();
329
	/**
330
	 * Affected
331
	 *
332
	 * Get number of affected rows during last query
333
	 *
334
	 * @abstract
335
	 *
336
	 * @return int
337
	 */
338
	abstract public function affected ();
339
	/**
340
	 * Execute transaction
341
	 *
342
	 * All queries done inside callback will be within single transaction, throwing any exception or returning boolean `false` from callback will cause
343
	 * rollback. Nested transaction calls will be wrapped into single big outer transaction, so you might call it safely if needed.
344
	 *
345
	 * @param callable $callback This instance will be used as single argument
346
	 *
347
	 * @return bool
348
	 *
349
	 * @throws Exception Re-throws exception thrown inside callback
350
	 */
351 42
	public function transaction ($callback) {
352 42
		$start_transaction = !$this->in_transaction;
353 42
		if ($start_transaction) {
354 42
			$this->in_transaction = true;
355 42
			if (!$this->q_internal('BEGIN')) {
356
				return false;
357
			}
358
		}
359
		try {
360 42
			$result = $callback($this);
361 2
		} catch (Exception $e) {
362 2
			$this->transaction_rollback();
363 2
			throw $e;
364
		}
365 42
		if ($result === false) {
366 2
			$this->transaction_rollback();
367 2
			return false;
368 42
		} elseif ($start_transaction) {
369 42
			$this->in_transaction = false;
370 42
			return (bool)$this->q_internal('COMMIT');
371
		}
372 2
		return true;
373
	}
374 2
	protected function transaction_rollback () {
375 2
		if ($this->in_transaction) {
376 2
			$this->in_transaction = false;
377 2
			$this->q_internal('ROLLBACK');
378
		}
379 2
	}
380
	/**
381
	 * Free result memory
382
	 *
383
	 * @abstract
384
	 *
385
	 * @param false|object|resource $query_result
386
	 */
387
	abstract public function free ($query_result);
1 ignored issue
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
388
	/**
389
	 * Get columns list of table
390
	 *
391
	 * @param string       $table
392
	 * @param false|string $like
393
	 *
394
	 * @return string[]
395
	 */
396
	abstract public function columns ($table, $like = false);
397
	/**
398
	 * Get tables list
399
	 *
400
	 * @param false|string $like
401
	 *
402
	 * @return string[]
403
	 */
404
	abstract public function tables ($like = false);
405
	/**
406
	 * Safe
407
	 *
408
	 * Preparing string for using in SQL query
409
	 * SQL Injection Protection
410
	 *
411
	 * @param string|string[] $string
412
	 * @param bool            $single_quotes_around
413
	 *
414
	 * @return string|string[]
415
	 */
416 44
	public function s ($string, $single_quotes_around = true) {
417 44
		if (is_array($string)) {
418 6
			foreach ($string as &$s) {
419 6
				$s = $this->s_internal($s, $single_quotes_around);
420
			}
421 6
			return $string;
422
		}
423 44
		return $this->s_internal($string, $single_quotes_around);
424
	}
425
	/**
426
	 * Preparing string for using in SQL query
427
	 * SQL Injection Protection
428
	 *
429
	 * @param string $string
430
	 * @param bool   $single_quotes_around
431
	 *
432
	 * @return string
433
	 */
434
	abstract protected function s_internal ($string, $single_quotes_around);
435
	/**
436
	 * Get information about server
437
	 *
438
	 * @return string
439
	 */
440
	abstract public function server ();
441
	/**
442
	 * Connection state
443
	 *
444
	 * @return bool
445
	 */
446 50
	public function connected () {
447 50
		return $this->connected;
448
	}
449
	/**
450
	 * Database type (lowercase, for example <i>mysql</i>)
451
	 *
452
	 * @return string
453
	 */
454 2
	public function db_type () {
455 2
		return $this->db_type;
456
	}
457
	/**
458
	 * Database name
459
	 *
460
	 * @return string
461
	 */
462 4
	public function database () {
463 4
		return $this->database;
464
	}
465
	/**
466
	 * Queries array, has 3 properties:<ul>
467
	 * <li>num - total number of performed queries
468
	 * <li>time - array with time of each query execution
469
	 * <li>text - array with text text of each query
470
	 *
471
	 * @deprecated
472
	 * @todo Remove in 6.x
473
	 *
474
	 * @return array
475
	 */
476 2
	public function queries () {
477 2
		return $this->queries;
478
	}
479
	/**
480
	 * Number of made SQL queries
481
	 *
482
	 * @return int
483
	 */
484 2
	public function queries_count () {
485 2
		return $this->queries['num'];
486
	}
487
	/**
488
	 * Last query information, has 2 properties:<ul>
489
	 * <li>time - execution time
490
	 * <li>text - query text
491
	 *
492
	 * @deprecated
493
	 * @todo Remove in 6.x
494
	 *
495
	 * @return array
496
	 */
497
	public function query () {
498
		return $this->query;
499
	}
500
	/**
501
	 * Total working time (including connection, queries execution and other delays)
502
	 *
503
	 * @return float
504
	 */
505 4
	public function time () {
506 4
		return $this->time;
507
	}
508
	/**
509
	 * Connecting time
510
	 *
511
	 * @return float
512
	 */
513 4
	public function connecting_time () {
514 4
		return $this->connecting_time;
515
	}
516
	/**
517
	 * Cloning restriction
518
	 *
519
	 * @final
520
	 */
521
	final protected function __clone () {
522
	}
523
	/**
524
	 * Disconnecting from DB
525
	 */
526
	abstract public function __destruct ();
1 ignored issue
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
527
}
528