Completed
Push — master ( e899d8...993498 )
by Nazar
04:15
created

_Abstract::queries()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * @package   CleverStyle CMS
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 int
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' => '',
50
		'text' => ''
51
	];
52
	/**
53
	 * Array for storing data of all executed requests
54
	 *
55
	 * @var array
56
	 */
57
	protected $queries = [
58
		'num'  => '',
59
		'time' => [],
60
		'text' => []
61
	];
62
	/**
63
	 * Connection time
64
	 *
65
	 * @var float
66
	 */
67
	protected $connecting_time;
68
	/**
69
	 * Asynchronous request
70
	 *
71
	 * @var bool
72
	 */
73
	protected $async = false;
74
	/**
75
	 * @var bool
76
	 */
77
	protected $in_transaction = false;
78
	/**
79
	 * Connecting to the DB
80
	 *
81
	 * @param string $database
82
	 * @param string $user
83
	 * @param string $password
84
	 * @param string $host
85
	 * @param string $charset
86
	 * @param string $prefix
87
	 */
88
	abstract function __construct ($database, $user = '', $password = '', $host = 'localhost', $charset = 'utf8', $prefix = '');
89
	/**
90
	 * Query
91
	 *
92
	 * SQL request into DB
93
	 *
94
	 * @abstract
95
	 *
96
	 * @param string|string[] $query  SQL query string or array, may be a format string in accordance with the first parameter of sprintf() function
97
	 * @param string|string[] $params May be array of arguments for formatting of <b>$query</b><br>
98
	 *                                or string - in this case it will be first argument for formatting of <b>$query</b>
99
	 * @param string[]        $param  if <b>$params</b> is string - this parameter will be second argument for formatting of <b>$query</b>.
100
	 *                                If you need more arguments - add them after this one, function will accept them.
101
	 *
102
	 * @return false|object|resource
103
	 */
104
	function q ($query, $params = [], ...$param) {
105
		$normalized = $this->prepare_and_normalize_arguments($query, func_get_args());
106
		if (!$normalized) {
107
			return false;
108
		}
109
		list($query, $params) = $normalized;
110
		/**
111
		 * Executing multiple queries
112
		 */
113
		if (is_array($query)) {
114
			return $this->execute_multiple($query, $params);
115
		}
116
		return $this->execute_single($query, $params);
117
	}
118
	/**
119
	 * @param string|string[] $query
120
	 * @param array           $arguments
121
	 *
122
	 * @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...
123
	 */
124
	protected function prepare_and_normalize_arguments ($query, $arguments) {
125
		if (!$query || !$arguments) {
126
			return false;
127
		}
128
		$query = str_replace('[prefix]', $this->prefix, $query);
129
		switch (count($arguments)) {
130
			default:
131
				$params = array_slice($arguments, 1);
132
				break;
133
			case 1:
134
				$params = [];
135
				break;
136
			case 2:
137
				$params = (array)$arguments[1];
138
				break;
139
		}
140
		foreach ($params as &$param) {
141
			$param = $this->s($param, false);
142
		}
143
		return [
144
			$query,
145
			$params
146
		];
147
	}
148
	/**
149
	 * @param string[] $queries
150
	 * @param string[] $params
151
	 *
152
	 * @return false|object|resource
153
	 */
154
	protected function execute_multiple ($queries, $params) {
155
		$time_from = microtime(true);
156
		foreach ($queries as &$q) {
157
			$q = $params ? vsprintf($q, $params) : $q;
158
		}
159
		unset($q);
160
		$this->queries['num'] += count($queries);
161
		$result = $this->q_multi_internal($queries);
162
		$this->time += round(microtime(true) - $time_from, 6);
163
		return $result;
164
	}
165
	/**
166
	 * @param string   $query
167
	 * @param string[] $params
168
	 *
169
	 * @return false|object|resource
170
	 */
171
	protected function execute_single ($query, $params) {
172
		$time_from           = microtime(true);
173
		$this->query['text'] = empty($params) ? $query : vsprintf($query, $params);
174
		if (DEBUG) {
175
			$this->queries['text'][] = $this->query['text'];
176
		}
177
		$result              = $this->q_internal($this->query['text']);
178
		$this->query['time'] = round(microtime(true) - $time_from, 6);
179
		$this->time += $this->query['time'];
180
		if (DEBUG) {
181
			$this->queries['time'][] = $this->query['time'];
182
		}
183
		++$this->queries['num'];
184
		return $result;
185
	}
186
	/**
187
	 * Asynchronous, Query
188
	 *
189
	 * Asynchronous SQL request into DB (if is not supported - ordinary request will me executed).
190
	 * Result of execution can't be obtained, so, use it, for example, for deleting some non-critical data
191
	 *
192
	 * @abstract
193
	 *
194
	 * @param string|string[] $query  SQL query string, may be a format string in accordance with the first parameter of sprintf() function
195
	 * @param string|string[] $params May be array of arguments for formatting of <b>$query</b><br>
196
	 *                                or string - in this case it will be first argument for formatting of <b>$query</b>
197
	 * @param string[]        $param  if <b>$params</b> is string - this parameter will be second argument for formatting of <b>$query</b>.
198
	 *                                If you need more arguments - add them after this one, function will accept them.
199
	 *
200
	 * @return false|object|resource
201
	 */
202
	function aq ($query, $params = [], ...$param) {
203
		$this->async = true;
204
		$result      = $this->q($query, $params, ...$param);
205
		$this->async = false;
206
		return $result;
207
	}
208
	/**
209
	 * SQL request to DB
210
	 *
211
	 * @abstract
212
	 *
213
	 * @param string|string[] $query
214
	 *
215
	 * @return false|object|resource
216
	 */
217
	abstract protected function q_internal ($query);
218
	/**
219
	 * Multiple SQL request to DB
220
	 *
221
	 * @abstract
222
	 *
223
	 * @param string[] $query
224
	 *
225
	 * @return false|object|resource
226
	 */
227
	abstract protected function q_multi_internal ($query);
228
	/**
229
	 * Number
230
	 *
231
	 * Getting number of selected rows
232
	 *
233
	 * @deprecated
234
	 * @todo remove after 4.x release
235
	 *
236
	 * @abstract
237
	 *
238
	 * @param object|resource $query_result
239
	 *
240
	 * @return false|int
241
	 */
242
	abstract function n ($query_result);
243
	/**
244
	 * Fetch
245
	 *
246
	 * Fetch a result row as an associative array
247
	 *
248
	 * @abstract
249
	 *
250
	 * @param false|object|resource $query_result
251
	 * @param bool                  $single_column If <b>true</b> function will return not array with one element, but directly its value
252
	 * @param bool                  $array         If <b>true</b> returns array of associative arrays of all fetched rows
253
	 * @param bool                  $indexed       If <b>false</b> - associative array will be returned
254
	 *
255
	 * @return array[]|false|int|int[]|string|string[]
256
	 */
257
	abstract function f ($query_result, $single_column = false, $array = false, $indexed = false);
258
	/**
259
	 * Fetch, Array
260
	 *
261
	 * Similar to ::f() method, with parameter <b>$array</b> = true
262
	 *
263
	 * @deprecated
264
	 * @todo remove after 4.x release
265
	 *
266
	 * @param false|object|resource $query_result
267
	 * @param bool                  $single_column If <b>true</b> function will return not array with one element, but directly its value
268
	 * @param bool                  $indexed       If <b>false</b> - associative array will be returned
269
	 *
270
	 * @return array[]|false
0 ignored issues
show
Documentation introduced by
Should the return type not be array[]|false|integer|integer[]|string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
271
	 */
272
	function fa ($query_result, $single_column = false, $indexed = false) {
273
		return $this->f($query_result, $single_column, true, $indexed);
274
	}
275
	/**
276
	 * Fetch, Single
277
	 *
278
	 * Similar to ::f() method, with parameter <b>$single_column</b> = true
279
	 *
280
	 * @deprecated
281
	 * @todo remove after 4.x release
282
	 *
283
	 * @param false|object|resource $query_result
284
	 * @param bool                  $array If <b>true</b> returns array of associative arrays of all fetched rows
285
	 *
286
	 * @return false|int|int[]|string|string[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array[]|false|integer|integer[]|string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
287
	 */
288
	function fs ($query_result, $array = false) {
289
		return $this->f($query_result, true, $array);
290
	}
291
	/**
292
	 * Fetch, Array, Single
293
	 *
294
	 * Combination of ::fa() and ::fs() methods
295
	 *
296
	 * @deprecated
297
	 * @todo remove after 4.x release
298
	 *
299
	 * @param false|object|resource $query_result
300
	 *
301
	 * @return false|int[]|string[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array[]|false|integer|integer[]|string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
302
	 */
303
	function fas ($query_result) {
304
		return $this->fa($query_result, true);
0 ignored issues
show
Deprecated Code introduced by
The method cs\DB\_Abstract::fa() has been deprecated.

This method has been deprecated.

Loading history...
305
	}
306
	/**
307
	 * Query, Fetch
308
	 *
309
	 * Combination of ::q() and ::f() methods
310
	 *
311
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
312
	 *
313
	 * @return array|false
314
	 */
315
	function qf (...$query) {
316
		// TODO: simplify code below
317
		$single_column = false;
318
		$array         = false;
319
		$indexed       = false;
320
		if (count($query) > 1 && is_bool($query[1])) {
321
			$single_column = $query[1];
322
			if (isset($query[2])) {
323
				$array = $query[2];
324
			}
325
			if (isset($query[3])) {
326
				$indexed = $query[3];
327
			}
328
			$query = $query[0];
329
		} elseif (count($query) == 1 && is_array($query[0])) {
330
			$query = $query[0];
331
		}
332
		return $this->f($this->q(...$query), $single_column, $array, $indexed);
333
	}
334
	/**
335
	 * Query, Fetch, Array
336
	 *
337
	 * Short for ::f(::q(), false, true, false)
338
	 *
339
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
340
	 *
341
	 * @return array[]|false
342
	 */
343
	function qfa (...$query) {
344
		// TODO: simplify code below
345
		$single_column = false;
346
		$indexed       = false;
347
		if (count($query) > 1 && is_bool($query[1])) {
348
			$single_column = $query[1];
349
			if (isset($query[2])) {
350
				$indexed = $query[2];
351
			}
352
			$query = $query[0];
353
		} elseif (count($query) == 1 && is_array($query[0])) {
354
			$query = $query[0];
355
		}
356
		return $this->f($this->q(...$query), $single_column, true, $indexed);
357
	}
358
	/**
359
	 * Query, Fetch, Single
360
	 *
361
	 * Short for ::f(::q(), true, false, false)
362
	 *
363
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
364
	 *
365
	 * @return false|int|string
366
	 */
367
	function qfs (...$query) {
368
		// TODO: simplify code below
369
		$array = false;
370
		if (count($query) == 2 && is_bool($query[1])) {
371
			$array = $query[1];
372
			$query = $query[0];
373
		} elseif (count($query) == 1 && is_array($query[0])) {
374
			$query = $query[0];
375
		}
376
		return $this->f($this->q(...$query), true, $array);
377
	}
378
	/**
379
	 * Query, Fetch, Array, Single
380
	 *
381
	 * Short for ::f(::q(), true, true, false)
382
	 *
383
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
384
	 *
385
	 * @return false|int[]|string[]
386
	 */
387
	function qfas (...$query) {
388
		// TODO: simplify code below
389
		if (count($query) == 1 && is_array($query[0])) {
390
			$query = $query[0];
391
		}
392
		return $this->f($this->q(...$query), true, true);
393
	}
394
	/**
395
	 * Method for simplified inserting of several rows
396
	 *
397
	 * @param string        $query
398
	 * @param array|array[] $params   Array of array of parameters for inserting
399
	 * @param bool          $join     If true - inserting of several rows will be combined in one query. For this, be sure, that your query has keyword
400
	 *                                <i>VALUES</i> in uppercase. Part of query after this keyword will be multiplied with coma separator.
401
	 *
402
	 * @return bool
403
	 */
404
	function insert ($query, $params, $join = true) {
405
		if (!$query || !$params) {
406
			return false;
407
		}
408
		if ($join) {
409
			$query    = explode('VALUES', $query, 2);
410
			$query[1] = explode(')', $query[1], 2);
411
			$query    = [
412
				$query[0],
413
				$query[1][0].')',
414
				$query[1][1]
415
			];
416
			if (!isset($query[1]) || !$query[1]) {
417
				return false;
418
			}
419
			$query[1] .= str_repeat(",$query[1]", count($params) - 1);
420
			$query = $query[0].'VALUES'.$query[1].$query[2];
421
			return (bool)$this->q(
422
				$query,
423
				array_merge(...array_map('array_values', _array($params)))
424
			);
425
		} else {
426
			$result = true;
427
			foreach ($params as $p) {
428
				$result = $result && (bool)$this->q($query, $p);
429
			}
430
			return $result;
431
		}
432
	}
433
	/**
434
	 * Id
435
	 *
436
	 * Get id of last inserted row
437
	 *
438
	 * @abstract
439
	 *
440
	 * @return int
441
	 */
442
	abstract function id ();
443
	/**
444
	 * Affected
445
	 *
446
	 * Get number of affected rows during last query
447
	 *
448
	 * @abstract
449
	 *
450
	 * @return int
451
	 */
452
	abstract function affected ();
453
	/**
454
	 * Execute transaction
455
	 *
456
	 * All queries done inside callback will be within single transaction, throwing any exception or returning boolean `false` from callback will cause
457
	 * rollback. Nested transaction calls will be wrapped into single big outer transaction, so you might call it safely if needed.
458
	 *
459
	 * @param callable $callback This instance will be used as single argument
460
	 *
461
	 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
462
	 *
463
	 * @throws Exception Re-throws exception thrown inside callback
464
	 */
465
	function transaction ($callback) {
466
		$start_transaction = !$this->in_transaction;
467
		if ($start_transaction) {
468
			$this->in_transaction = true;
469
			if (!$this->q_internal('BEGIN')) {
470
				return false;
471
			}
472
		}
473
		try {
474
			$result = $callback($this);
475
		} catch (Exception $e) {
476
			$this->transaction_rollback();
477
			throw $e;
478
		}
479
		if ($result === false) {
480
			$this->transaction_rollback();
481
			return false;
482
		} elseif ($start_transaction) {
483
			$this->in_transaction = false;
484
			return (bool)$this->q_internal('COMMIT');
485
		}
486
	}
487
	protected function transaction_rollback () {
488
		if ($this->in_transaction) {
489
			$this->in_transaction = false;
490
			$this->q_internal('ROLLBACK');
491
		}
492
	}
493
	/**
494
	 * Free result memory
495
	 *
496
	 * @abstract
497
	 *
498
	 * @param object|resource $query_result
499
	 */
500
	abstract function free ($query_result);
501
	/**
502
	 * Get columns list of table
503
	 *
504
	 * @param string       $table
505
	 * @param false|string $like
506
	 *
507
	 * @return string[]
508
	 */
509
	function columns ($table, $like = false) {
510
		if (!$table) {
511
			return false;
512
		}
513
		if ($like) {
514
			$like    = $this->s($like);
515
			$columns = $this->qfa("SHOW COLUMNS FROM `$table` LIKE $like") ?: [];
516
		} else {
517
			$columns = $this->qfa("SHOW COLUMNS FROM `$table`") ?: [];
518
		}
519
		foreach ($columns as &$column) {
520
			$column = $column['Field'];
521
		}
522
		return $columns;
523
	}
524
	/**
525
	 * Get tables list
526
	 *
527
	 * @param false|string $like
528
	 *
529
	 * @return string[]
530
	 */
531
	function tables ($like = false) {
532
		if ($like) {
533
			$like = $this->s($like);
534
			return $this->qfas("SHOW TABLES FROM `$this->database` LIKE $like") ?: [];
535
		} else {
536
			return $this->qfas("SHOW TABLES FROM `$this->database`") ?: [];
537
		}
538
	}
539
	/**
540
	 * Safe
541
	 *
542
	 * Preparing string for using in SQL query
543
	 * SQL Injection Protection
544
	 *
545
	 * @param string|string[] $string
546
	 * @param bool            $single_quotes_around
547
	 *
548
	 * @return string|string[]
549
	 */
550
	function s ($string, $single_quotes_around = true) {
551
		if (is_array($string)) {
552
			foreach ($string as &$s) {
553
				$s = $this->s_internal($s, $single_quotes_around);
554
			}
555
			return $string;
556
		}
557
		return $this->s_internal($string, $single_quotes_around);
558
	}
559
	/**
560
	 * Preparing string for using in SQL query
561
	 * SQL Injection Protection
562
	 *
563
	 * @param string $string
564
	 * @param bool   $single_quotes_around
565
	 *
566
	 * @return string
567
	 */
568
	abstract protected function s_internal ($string, $single_quotes_around);
569
	/**
570
	 * Get information about server
571
	 *
572
	 * @return string
573
	 */
574
	abstract function server ();
575
	/**
576
	 * Connection state
577
	 *
578
	 * @return bool
579
	 */
580
	function connected () {
581
		return $this->connected;
582
	}
583
	/**
584
	 * Database type (lowercase, for example <i>mysql</i>)
585
	 *
586
	 * @return string
587
	 */
588
	function db_type () {
589
		return $this->db_type;
590
	}
591
	/**
592
	 * Database name
593
	 *
594
	 * @return string
595
	 */
596
	function database () {
597
		return $this->database;
598
	}
599
	/**
600
	 * Queries array, has 3 properties:<ul>
601
	 * <li>num - total number of performed queries
602
	 * <li>time - array with time of each query execution
603
	 * <li>text - array with text text of each query
604
	 *
605
	 * @return array
606
	 */
607
	function queries () {
608
		return $this->queries;
609
	}
610
	/**
611
	 * Last query information, has 2 properties:<ul>
612
	 * <li>time - execution time
613
	 * <li>text - query text
614
	 *
615
	 * @return array
616
	 */
617
	function query () {
618
		return $this->query;
619
	}
620
	/**
621
	 * Total working time (including connection, queries execution and other delays)
622
	 *
623
	 * @return int
624
	 */
625
	function time () {
626
		return $this->time;
627
	}
628
	/**
629
	 * Connecting time
630
	 *
631
	 * @return float
632
	 */
633
	function connecting_time () {
634
		return $this->connecting_time;
635
	}
636
	/**
637
	 * Cloning restriction
638
	 *
639
	 * @final
640
	 */
641
	final function __clone () {
642
	}
643
	/**
644
	 * Disconnecting from DB
645
	 */
646
	abstract function __destruct ();
647
}
648