Completed
Push — master ( cc4c1e...2fc59f )
by Nazar
03:53
created

_Abstract::qfs()   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 1
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 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
	 * @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 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
90
	 * @param string|string[] $params May be array of arguments for formatting of <b>$query</b><br>
91
	 *                                or string - in this case it will be first argument for formatting of <b>$query</b>
92
	 * @param string[]        $param  if <b>$params</b> is string - this parameter will be second argument for formatting of <b>$query</b>.
93
	 *                                If you need more arguments - add them after this one, function will accept them.
94
	 *
95
	 * @return false|object|resource
96
	 */
97 36
	function q ($query, $params = [], ...$param) {
98 36
		$normalized = $this->prepare_and_normalize_arguments($query, func_get_args());
99 36
		if (!$normalized) {
100 2
			return false;
101
		}
102 36
		list($query, $params) = $normalized;
103
		/**
104
		 * Executing multiple queries
105
		 */
106 36
		if (is_array($query)) {
107 10
			return $this->execute_multiple($query, $params);
108
		}
109 36
		return $this->execute_single($query, $params);
110
	}
111
	/**
112
	 * @param string|string[] $query
113
	 * @param array           $arguments
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 36
	protected function prepare_and_normalize_arguments ($query, $arguments) {
118 36
		if (!$query || !$arguments) {
119 2
			return false;
120
		}
121 36
		$query = str_replace('[prefix]', $this->prefix, $query);
122 36
		switch (count($arguments)) {
123
			default:
124 22
				$params = array_slice($arguments, 1);
125 22
				break;
126 36
			case 1:
127 34
				$params = [];
128 34
				break;
129 36
			case 2:
130 34
				$params = (array)$arguments[1];
131 34
				break;
132
		}
133 36
		foreach ($params as &$param) {
134 36
			$param = $this->s($param, false);
135
		}
136
		return [
137 36
			$query,
138 36
			$params
139
		];
140
	}
141
	/**
142
	 * @param string[] $queries
143
	 * @param string[] $params
144
	 *
145
	 * @return false|object|resource
146
	 */
147 10
	protected function execute_multiple ($queries, $params) {
148 10
		$time_from = microtime(true);
149 10
		foreach ($queries as &$q) {
150 10
			$q = $params ? vsprintf($q, $params) : $q;
151
		}
152 10
		unset($q);
153 10
		$this->queries['num'] += count($queries);
154 10
		$result = $this->q_multi_internal($queries);
155 10
		$this->time += round(microtime(true) - $time_from, 6);
156 10
		return $result;
157
	}
158
	/**
159
	 * @param string   $query
160
	 * @param string[] $params
161
	 *
162
	 * @return false|object|resource
163
	 */
164 36
	protected function execute_single ($query, $params) {
165 36
		$time_from           = microtime(true);
166 36
		$this->query['text'] = empty($params) ? $query : vsprintf($query, $params);
167 36
		if (DEBUG) {
168
			$this->queries['text'][] = $this->query['text'];
169
		}
170 36
		$result              = $this->q_internal($this->query['text']);
171 36
		$this->query['time'] = round(microtime(true) - $time_from, 6);
172 36
		$this->time += $this->query['time'];
173 36
		if (DEBUG) {
174
			$this->queries['time'][] = $this->query['time'];
175
		}
176 36
		++$this->queries['num'];
177 36
		return $result;
178
	}
179
	/**
180
	 * SQL request to DB
181
	 *
182
	 * @abstract
183
	 *
184
	 * @param string $query
185
	 *
186
	 * @return false|object|resource
187
	 */
188
	abstract protected function q_internal ($query);
189
	/**
190
	 * Multiple SQL request to DB
191
	 *
192
	 * @abstract
193
	 *
194
	 * @param string[] $query
195
	 *
196
	 * @return false|object|resource
197
	 */
198
	abstract protected function q_multi_internal ($query);
199
	/**
200
	 * Fetch
201
	 *
202
	 * Fetch a result row as an associative array
203
	 *
204
	 * @abstract
205
	 *
206
	 * @param false|object|resource $query_result
207
	 * @param bool                  $single_column If <b>true</b> function will return not array with one element, but directly its value
208
	 * @param bool                  $array         If <b>true</b> returns array of associative arrays of all fetched rows
209
	 * @param bool                  $indexed       If <b>false</b> - associative array will be returned
210
	 *
211
	 * @return array[]|false|int|int[]|string|string[]
212
	 */
213
	abstract function f ($query_result, $single_column = false, $array = false, $indexed = false);
214
	/**
215
	 * Query, Fetch
216
	 *
217
	 * Short for `::f(::q())`, arguments are exactly the same as in `::q()`
218
	 *
219
	 * @param string[] $query
220
	 *
221
	 * @return array|false
222
	 */
223 32
	function qf (...$query) {
224 32
		return $this->f($this->q(...$query));
225
	}
226
	/**
227
	 * Query, Fetch, Array
228
	 *
229
	 * Short for `::f(::q(), false, true)`, arguments are exactly the same as in `::q()`
230
	 *
231
	 * @param string[] $query
232
	 *
233
	 * @return array[]|false
234
	 */
235 19
	function qfa (...$query) {
236 19
		return $this->f($this->q(...$query), false, true);
237
	}
238
	/**
239
	 * Query, Fetch, Single
240
	 *
241
	 * Short for `::f(::q(), true)`, arguments are exactly the same as in `::q()`
242
	 *
243
	 * @param string[] $query
244
	 *
245
	 * @return false|int|string
246
	 */
247 22
	function qfs (...$query) {
248 22
		return $this->f($this->q(...$query), true);
249
	}
250
	/**
251
	 * Query, Fetch, Array, Single
252
	 *
253
	 * Short for `::f(::q(), true, true)`, arguments are exactly the same as in `::q()`
254
	 *
255
	 * @param string[] $query
256
	 *
257
	 * @return false|int[]|string[]
258
	 */
259 27
	function qfas (...$query) {
260 27
		return $this->f($this->q(...$query), true, true);
261
	}
262
	/**
263
	 * Method for simplified inserting of several rows
264
	 *
265
	 * @param string        $query
266
	 * @param array|array[] $params   Array of array of parameters for inserting
267
	 * @param bool          $join     If true - inserting of several rows will be combined in one query. For this, be sure, that your query has keyword
268
	 *                                <i>VALUES</i> in uppercase. Part of query after this keyword will be multiplied with coma separator.
269
	 *
270
	 * @return bool
271
	 */
272 24
	function insert ($query, $params, $join = true) {
273 24
		if (!$query || !$params) {
274 2
			return false;
275
		}
276 24
		if ($join) {
277 24
			$query    = explode('VALUES', $query, 2);
278 24
			$query[1] = explode(')', $query[1], 2);
279
			$query    = [
280 24
				$query[0],
281 24
				$query[1][0].')',
282 24
				$query[1][1]
283
			];
284 24
			$query[1] .= str_repeat(",$query[1]", count($params) - 1);
285 24
			$query = $query[0].'VALUES'.$query[1].$query[2];
286 24
			return (bool)$this->q(
287
				$query,
288 24
				array_merge(...array_map('array_values', _array($params)))
289
			);
290
		} else {
291 2
			$result = true;
292 2
			foreach ($params as $p) {
293 2
				$result = $result && (bool)$this->q($query, $p);
294
			}
295 2
			return $result;
296
		}
297
	}
298
	/**
299
	 * Id
300
	 *
301
	 * Get id of last inserted row
302
	 *
303
	 * @abstract
304
	 *
305
	 * @return int
306
	 */
307
	abstract function id ();
308
	/**
309
	 * Affected
310
	 *
311
	 * Get number of affected rows during last query
312
	 *
313
	 * @abstract
314
	 *
315
	 * @return int
316
	 */
317
	abstract function affected ();
318
	/**
319
	 * Execute transaction
320
	 *
321
	 * All queries done inside callback will be within single transaction, throwing any exception or returning boolean `false` from callback will cause
322
	 * rollback. Nested transaction calls will be wrapped into single big outer transaction, so you might call it safely if needed.
323
	 *
324
	 * @param callable $callback This instance will be used as single argument
325
	 *
326
	 * @return bool
327
	 *
328
	 * @throws Exception Re-throws exception thrown inside callback
329
	 */
330 32
	function transaction ($callback) {
331 32
		$start_transaction = !$this->in_transaction;
332 32
		if ($start_transaction) {
333 32
			$this->in_transaction = true;
334 32
			if (!$this->q_internal('BEGIN')) {
335
				return false;
336
			}
337
		}
338
		try {
339 32
			$result = $callback($this);
340 2
		} catch (Exception $e) {
341 2
			$this->transaction_rollback();
342 2
			throw $e;
343
		}
344 32
		if ($result === false) {
345 2
			$this->transaction_rollback();
346 2
			return false;
347 32
		} elseif ($start_transaction) {
348 32
			$this->in_transaction = false;
349 32
			return (bool)$this->q_internal('COMMIT');
350
		}
351 2
		return true;
352
	}
353 2
	protected function transaction_rollback () {
354 2
		if ($this->in_transaction) {
355 2
			$this->in_transaction = false;
356 2
			$this->q_internal('ROLLBACK');
357
		}
358 2
	}
359
	/**
360
	 * Free result memory
361
	 *
362
	 * @abstract
363
	 *
364
	 * @param false|object|resource $query_result
365
	 */
366
	abstract function free ($query_result);
367
	/**
368
	 * Get columns list of table
369
	 *
370
	 * @param string       $table
371
	 * @param false|string $like
372
	 *
373
	 * @return string[]
374
	 */
375
	abstract function columns ($table, $like = false);
376
	/**
377
	 * Get tables list
378
	 *
379
	 * @param false|string $like
380
	 *
381
	 * @return string[]
382
	 */
383
	abstract function tables ($like = false);
384
	/**
385
	 * Safe
386
	 *
387
	 * Preparing string for using in SQL query
388
	 * SQL Injection Protection
389
	 *
390
	 * @param string|string[] $string
391
	 * @param bool            $single_quotes_around
392
	 *
393
	 * @return string|string[]
394
	 */
395 36
	function s ($string, $single_quotes_around = true) {
396 36
		if (is_array($string)) {
397 6
			foreach ($string as &$s) {
398 6
				$s = $this->s_internal($s, $single_quotes_around);
399
			}
400 6
			return $string;
401
		}
402 36
		return $this->s_internal($string, $single_quotes_around);
403
	}
404
	/**
405
	 * Preparing string for using in SQL query
406
	 * SQL Injection Protection
407
	 *
408
	 * @param string $string
409
	 * @param bool   $single_quotes_around
410
	 *
411
	 * @return string
412
	 */
413
	abstract protected function s_internal ($string, $single_quotes_around);
414
	/**
415
	 * Get information about server
416
	 *
417
	 * @return string
418
	 */
419
	abstract function server ();
420
	/**
421
	 * Connection state
422
	 *
423
	 * @return bool
424
	 */
425 42
	function connected () {
426 42
		return $this->connected;
427
	}
428
	/**
429
	 * Database type (lowercase, for example <i>mysql</i>)
430
	 *
431
	 * @return string
432
	 */
433 2
	function db_type () {
434 2
		return $this->db_type;
435
	}
436
	/**
437
	 * Database name
438
	 *
439
	 * @return string
440
	 */
441 2
	function database () {
442 2
		return $this->database;
443
	}
444
	/**
445
	 * Queries array, has 3 properties:<ul>
446
	 * <li>num - total number of performed queries
447
	 * <li>time - array with time of each query execution
448
	 * <li>text - array with text text of each query
449
	 *
450
	 * @return array
451
	 */
452 4
	function queries () {
453 4
		return $this->queries;
454
	}
455
	/**
456
	 * Last query information, has 2 properties:<ul>
457
	 * <li>time - execution time
458
	 * <li>text - query text
459
	 *
460
	 * @return array
461
	 */
462 2
	function query () {
463 2
		return $this->query;
464
	}
465
	/**
466
	 * Total working time (including connection, queries execution and other delays)
467
	 *
468
	 * @return int
469
	 */
470 4
	function time () {
471 4
		return $this->time;
472
	}
473
	/**
474
	 * Connecting time
475
	 *
476
	 * @return float
477
	 */
478 4
	function connecting_time () {
479 4
		return $this->connecting_time;
480
	}
481
	/**
482
	 * Cloning restriction
483
	 *
484
	 * @final
485
	 */
486
	final protected function __clone () {
487
	}
488
	/**
489
	 * Disconnecting from DB
490
	 */
491
	abstract function __destruct ();
492
}
493