Completed
Push — master ( f9aba3...23d82e )
by Nazar
05:05
created

_Abstract::queries()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
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 = '');
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
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 36
		foreach ($parameters as &$parameter) {
166 36
			$parameter = $this->s($parameter, false);
167
		}
168 36
		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 22
	public function qfa (...$query) {
257 22
		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 32
	public function qfs (...$query) {
269 32
		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);
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
	public function queries () {
477
		return $this->queries;
478
	}
479
	/**
480
	 * Number of made SQL queries
481
	 *
482
	 * @return int
483
	 */
484 6
	public function queries_count () {
485 6
		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 ();
527
}
528