Completed
Push — master ( 757263...aa4637 )
by Nazar
04:41
created

_Abstract::__clone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 2
rs 10
cc 1
eloc 1
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
abstract class _Abstract {
10
	/**
11
	 * Is connection established
12
	 *
13
	 * @var bool
14
	 */
15
	protected $connected = false;
16
	/**
17
	 * DB type, may be used for constructing requests, accounting particular features of current DB (lowercase name)
18
	 *
19
	 * @var string
20
	 */
21
	protected $db_type = '';
22
	/**
23
	 * Current DB
24
	 *
25
	 * @var string
26
	 */
27
	protected $database;
28
	/**
29
	 * Current prefix
30
	 *
31
	 * @var string
32
	 */
33
	protected $prefix;
34
	/**
35
	 * Total time of requests execution
36
	 *
37
	 * @var int
38
	 */
39
	protected $time;
40
	/**
41
	 * Array for storing of data of the last executed request
42
	 *
43
	 * @var array
44
	 */
45
	protected $query = [
46
		'time' => '',
47
		'text' => ''
48
	];
49
	/**
50
	 * Array for storing data of all executed requests
51
	 *
52
	 * @var array
53
	 */
54
	protected $queries = [
55
		'num'  => '',
56
		'time' => [],
57
		'text' => []
58
	];
59
	/**
60
	 * Connection time
61
	 *
62
	 * @var float
63
	 */
64
	protected $connecting_time;
65
	/**
66
	 * Asynchronous request
67
	 *
68
	 * @var bool
69
	 */
70
	protected $async = false;
71
	/**
72
	 * Connecting to the DB
73
	 *
74
	 * @param string $database
75
	 * @param string $user
76
	 * @param string $password
77
	 * @param string $host
78
	 * @param string $charset
79
	 * @param string $prefix
80
	 */
81
	abstract function __construct ($database, $user = '', $password = '', $host = 'localhost', $charset = 'utf8', $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
	function q ($query, $params = [], ...$param) {
98
		$normalized = $this->prepare_and_normalize_arguments($query, func_get_args());
99
		if (!$normalized) {
100
			return false;
101
		}
102
		list($query, $params) = $normalized;
103
		/**
104
		 * Executing multiple queries
105
		 */
106
		if (is_array($query)) {
107
			return $this->execute_multiple($query, $params);
108
		}
109
		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
	protected function prepare_and_normalize_arguments ($query, $arguments) {
118
		if (!$query || !$arguments) {
119
			return false;
120
		}
121
		$query = str_replace('[prefix]', $this->prefix, $query);
122
		switch (count($arguments)) {
123
			default:
124
				$params = array_slice($arguments, 1);
125
				break;
126
			case 1:
127
				$params = [];
128
				break;
129
			case 2:
130
				$params = (array)$arguments[1];
131
				break;
132
		}
133
		foreach ($params as &$param) {
134
			$param = $this->s($param, false);
135
		}
136
		return [
137
			$query,
138
			$params
139
		];
140
	}
141
	/**
142
	 * @param string[] $queries
143
	 * @param string[] $params
144
	 *
145
	 * @return false|object|resource
146
	 */
147
	protected function execute_multiple ($queries, $params) {
148
		$time_from = microtime(true);
149
		foreach ($queries as &$q) {
150
			$q = $params ? vsprintf($q, $params) : $q;
151
		}
152
		unset($q);
153
		$this->queries['num'] += count($queries);
154
		$result = $this->q_multi_internal($queries);
155
		$this->time += round(microtime(true) - $time_from, 6);
156
		return $result;
157
	}
158
	/**
159
	 * @param string   $query
160
	 * @param string[] $params
161
	 *
162
	 * @return false|object|resource
163
	 */
164
	protected function execute_single ($query, $params) {
165
		$time_from           = microtime(true);
166
		$this->query['text'] = empty($params) ? $query : vsprintf($query, $params);
167
		if (DEBUG) {
168
			$this->queries['text'][] = $this->query['text'];
169
		}
170
		$result              = $this->q_internal($this->query['text']);
171
		$this->query['time'] = round(microtime(true) - $time_from, 6);
172
		$this->time += $this->query['time'];
173
		if (DEBUG) {
174
			$this->queries['time'][] = $this->query['time'];
175
		}
176
		++$this->queries['num'];
177
		return $result;
178
	}
179
	/**
180
	 * Asynchronous, Query
181
	 *
182
	 * Asynchronous SQL request into DB (if is not supported - ordinary request will me executed).
183
	 * Result of execution can't be obtained, so, use it, for example, for deleting some non-critical data
184
	 *
185
	 * @abstract
186
	 *
187
	 * @param string|string[] $query  SQL query string, may be a format string in accordance with the first parameter of sprintf() function
188
	 * @param string|string[] $params May be array of arguments for formatting of <b>$query</b><br>
189
	 *                                or string - in this case it will be first argument for formatting of <b>$query</b>
190
	 * @param string[]        $param  if <b>$params</b> is string - this parameter will be second argument for formatting of <b>$query</b>.
191
	 *                                If you need more arguments - add them after this one, function will accept them.
192
	 *
193
	 * @return false|object|resource
194
	 */
195
	function aq ($query, $params = [], ...$param) {
196
		$this->async = true;
197
		$result      = $this->q($query, $params, ...$param);
198
		$this->async = false;
199
		return $result;
200
	}
201
	/**
202
	 * SQL request to DB
203
	 *
204
	 * @abstract
205
	 *
206
	 * @param string|string[] $query
207
	 *
208
	 * @return false|object|resource
209
	 */
210
	abstract protected function q_internal ($query);
211
	/**
212
	 * Multiple SQL request to DB
213
	 *
214
	 * @abstract
215
	 *
216
	 * @param string[] $query
217
	 *
218
	 * @return false|object|resource
219
	 */
220
	abstract protected function q_multi_internal ($query);
221
	/**
222
	 * Number
223
	 *
224
	 * Getting number of selected rows
225
	 *
226
	 * @deprecated
227
	 * @todo remove after 4.x release
228
	 *
229
	 * @abstract
230
	 *
231
	 * @param object|resource $query_result
232
	 *
233
	 * @return false|int
234
	 */
235
	abstract function n ($query_result);
236
	/**
237
	 * Fetch
238
	 *
239
	 * Fetch a result row as an associative array
240
	 *
241
	 * @abstract
242
	 *
243
	 * @param false|object|resource $query_result
244
	 * @param bool                  $single_column If <b>true</b> function will return not array with one element, but directly its value
245
	 * @param bool                  $array         If <b>true</b> returns array of associative arrays of all fetched rows
246
	 * @param bool                  $indexed       If <b>false</b> - associative array will be returned
247
	 *
248
	 * @return array[]|false|int|int[]|string|string[]
249
	 */
250
	abstract function f ($query_result, $single_column = false, $array = false, $indexed = false);
251
	/**
252
	 * Fetch, Array
253
	 *
254
	 * Similar to ::f() method, with parameter <b>$array</b> = true
255
	 *
256
	 * @deprecated
257
	 * @todo remove after 4.x release
258
	 *
259
	 * @param false|object|resource $query_result
260
	 * @param bool                  $single_column If <b>true</b> function will return not array with one element, but directly its value
261
	 * @param bool                  $indexed       If <b>false</b> - associative array will be returned
262
	 *
263
	 * @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...
264
	 */
265
	function fa ($query_result, $single_column = false, $indexed = false) {
266
		return $this->f($query_result, $single_column, true, $indexed);
267
	}
268
	/**
269
	 * Fetch, Single
270
	 *
271
	 * Similar to ::f() method, with parameter <b>$single_column</b> = true
272
	 *
273
	 * @deprecated
274
	 * @todo remove after 4.x release
275
	 *
276
	 * @param false|object|resource $query_result
277
	 * @param bool                  $array If <b>true</b> returns array of associative arrays of all fetched rows
278
	 *
279
	 * @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...
280
	 */
281
	function fs ($query_result, $array = false) {
282
		return $this->f($query_result, true, $array);
283
	}
284
	/**
285
	 * Fetch, Array, Single
286
	 *
287
	 * Combination of ::fa() and ::fs() methods
288
	 *
289
	 * @deprecated
290
	 * @todo remove after 4.x release
291
	 *
292
	 * @param false|object|resource $query_result
293
	 *
294
	 * @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...
295
	 */
296
	function fas ($query_result) {
297
		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...
298
	}
299
	/**
300
	 * Query, Fetch
301
	 *
302
	 * Combination of ::q() and ::f() methods
303
	 *
304
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
1 ignored issue
show
Documentation introduced by
Should the type for parameter $query not be string[][]?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
305
	 *
306
	 * @return array|false
1 ignored issue
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...
307
	 */
308
	function qf (...$query) {
309
		// TODO: simplify code below to just `return $this->f($this->q(...$query), true);`
310
		$single_column = false;
311
		$array         = false;
312
		$indexed       = false;
313
		if (count($query) > 1 && is_bool($query[1])) {
314
			$single_column = $query[1];
315
			if (isset($query[2])) {
316
				$array = $query[2];
317
			}
318
			if (isset($query[3])) {
319
				$indexed = $query[3];
320
			}
321
			$query = $query[0];
322
		} elseif (count($query) == 1 && is_array($query[0])) {
323
			$query = $query[0];
324
		}
325
		return $this->f($this->q(...$query), $single_column, $array, $indexed);
2 ignored issues
show
Bug introduced by
It seems like $array defined by $query[2] on line 316 can also be of type array<integer,string>; however, cs\DB\_Abstract::f() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $indexed defined by $query[3] on line 319 can also be of type array<integer,string>; however, cs\DB\_Abstract::f() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
326
	}
327
	/**
328
	 * Query, Fetch, Array
329
	 *
330
	 * Short for ::f(::q(), false, true, false)
331
	 *
332
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
1 ignored issue
show
Documentation introduced by
Should the type for parameter $query not be string[][]?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
333
	 *
334
	 * @return array[]|false
1 ignored issue
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...
335
	 */
336
	function qfa (...$query) {
337
		// TODO: simplify code below to just `return $this->f($this->q(...$query), true);`
338
		$single_column = false;
339
		$indexed       = false;
340
		if (count($query) > 1 && is_bool($query[1])) {
341
			$single_column = $query[1];
342
			if (isset($query[2])) {
343
				$indexed = $query[2];
344
			}
345
			$query = $query[0];
346
		} elseif (count($query) == 1 && is_array($query[0])) {
347
			$query = $query[0];
348
		}
349
		return $this->f($this->q(...$query), $single_column, true, $indexed);
1 ignored issue
show
Bug introduced by
It seems like $indexed defined by $query[2] on line 343 can also be of type array<integer,string>; however, cs\DB\_Abstract::f() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
350
	}
351
	/**
352
	 * Query, Fetch, Single
353
	 *
354
	 * Short for ::f(::q(), true, false, false)
355
	 *
356
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
1 ignored issue
show
Documentation introduced by
Should the type for parameter $query not be string[][]?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
357
	 *
358
	 * @return false|int|string
1 ignored issue
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...
359
	 */
360
	function qfs (...$query) {
361
		// TODO: simplify code below to just `return $this->f($this->q(...$query), true);`
362
		$array = false;
363
		if (count($query) == 2 && is_bool($query[1])) {
364
			$array = $query[1];
365
			$query = $query[0];
366
		} elseif (count($query) == 1 && is_array($query[0])) {
367
			$query = $query[0];
368
		}
369
		return $this->f($this->q(...$query), true, $array);
370
	}
371
	/**
372
	 * Query, Fetch, Array, Single
373
	 *
374
	 * Short for ::f(::q(), true, true, false)
375
	 *
376
	 * @param string[] $query SQL query string, or you can put all parameters, that ::q() function can accept in form of array
1 ignored issue
show
Documentation introduced by
Should the type for parameter $query not be string[][]?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
377
	 *
378
	 * @return false|int[]|string[]
1 ignored issue
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...
379
	 */
380
	function qfas (...$query) {
381
		return $this->f($this->q(...$query), true, true);
382
	}
383
	/**
384
	 * Method for simplified inserting of several rows
385
	 *
386
	 * @param string        $query
387
	 * @param array|array[] $params   Array of array of parameters for inserting
388
	 * @param bool          $join     If true - inserting of several rows will be combined in one query. For this, be sure, that your query has keyword
389
	 *                                <i>VALUES</i> in uppercase. Part of query after this keyword will be multiplied with coma separator.
390
	 *
391
	 * @return bool
392
	 */
393
	function insert ($query, $params, $join = true) {
394
		if (!$query || !$params) {
395
			return false;
396
		}
397
		if ($join) {
398
			$query    = explode('VALUES', $query, 2);
399
			$query[1] = explode(')', $query[1], 2);
400
			$query    = [
401
				$query[0],
402
				$query[1][0].')',
403
				$query[1][1]
404
			];
405
			if (!isset($query[1]) || !$query[1]) {
406
				return false;
407
			}
408
			$query[1] .= str_repeat(",$query[1]", count($params) - 1);
409
			$query = $query[0].'VALUES'.$query[1].$query[2];
410
			return (bool)$this->q(
411
				$query,
412
				array_merge(...array_map('array_values', _array($params)))
413
			);
414
		} else {
415
			$result = true;
416
			foreach ($params as $p) {
417
				$result = $result && (bool)$this->q($query, $p);
418
			}
419
			return $result;
420
		}
421
	}
422
	/**
423
	 * Id
424
	 *
425
	 * Get id of last inserted row
426
	 *
427
	 * @abstract
428
	 *
429
	 * @return int
430
	 */
431
	abstract function id ();
432
	/**
433
	 * Affected
434
	 *
435
	 * Get number of affected rows during last query
436
	 *
437
	 * @abstract
438
	 *
439
	 * @return int
440
	 */
441
	abstract function affected ();
442
	/**
443
	 * Free result memory
444
	 *
445
	 * @abstract
446
	 *
447
	 * @param object|resource $query_result
448
	 */
449
	abstract function free ($query_result);
450
	/**
451
	 * Get columns list of table
452
	 *
453
	 * @param string       $table
454
	 * @param false|string $like
455
	 *
456
	 * @return string[]
457
	 */
458
	function columns ($table, $like = false) {
459
		if (!$table) {
460
			return false;
461
		}
462
		if ($like) {
463
			$like    = $this->s($like);
464
			$columns = $this->qfa("SHOW COLUMNS FROM `$table` LIKE $like") ?: [];
465
		} else {
466
			$columns = $this->qfa("SHOW COLUMNS FROM `$table`") ?: [];
467
		}
468
		foreach ($columns as &$column) {
469
			$column = $column['Field'];
470
		}
471
		return $columns;
472
	}
473
	/**
474
	 * Get tables list
475
	 *
476
	 * @param false|string $like
477
	 *
478
	 * @return string[]
479
	 */
480
	function tables ($like = false) {
481
		if ($like) {
482
			$like = $this->s($like);
483
			return $this->qfas("SHOW TABLES FROM `$this->database` LIKE $like") ?: [];
484
		} else {
485
			return $this->qfas("SHOW TABLES FROM `$this->database`") ?: [];
486
		}
487
	}
488
	/**
489
	 * Safe
490
	 *
491
	 * Preparing string for using in SQL query
492
	 * SQL Injection Protection
493
	 *
494
	 * @param string|string[] $string
495
	 * @param bool            $single_quotes_around
496
	 *
497
	 * @return string|string[]
498
	 */
499
	function s ($string, $single_quotes_around = true) {
500
		if (is_array($string)) {
501
			foreach ($string as &$s) {
502
				$s = $this->s_internal($s, $single_quotes_around);
503
			}
504
			return $string;
505
		}
506
		return $this->s_internal($string, $single_quotes_around);
507
	}
508
	/**
509
	 * Preparing string for using in SQL query
510
	 * SQL Injection Protection
511
	 *
512
	 * @param string $string
513
	 * @param bool   $single_quotes_around
514
	 *
515
	 * @return string
516
	 */
517
	abstract protected function s_internal ($string, $single_quotes_around);
518
	/**
519
	 * Get information about server
520
	 *
521
	 * @return string
522
	 */
523
	abstract function server ();
524
	/**
525
	 * Connection state
526
	 *
527
	 * @return bool
528
	 */
529
	function connected () {
530
		return $this->connected;
531
	}
532
	/**
533
	 * Database type (lowercase, for example <i>mysql</i>)
534
	 *
535
	 * @return string
536
	 */
537
	function db_type () {
538
		return $this->db_type;
539
	}
540
	/**
541
	 * Database name
542
	 *
543
	 * @return string
544
	 */
545
	function database () {
546
		return $this->database;
547
	}
548
	/**
549
	 * Queries array, has 3 properties:<ul>
550
	 * <li>num - total number of performed queries
551
	 * <li>time - array with time of each query execution
552
	 * <li>text - array with text text of each query
553
	 *
554
	 * @return array
555
	 */
556
	function queries () {
557
		return $this->queries;
558
	}
559
	/**
560
	 * Last query information, has 2 properties:<ul>
561
	 * <li>time - execution time
562
	 * <li>text - query text
563
	 *
564
	 * @return array
565
	 */
566
	function query () {
567
		return $this->query;
568
	}
569
	/**
570
	 * Total working time (including connection, queries execution and other delays)
571
	 *
572
	 * @return int
573
	 */
574
	function time () {
575
		return $this->time;
576
	}
577
	/**
578
	 * Connecting time
579
	 *
580
	 * @return float
581
	 */
582
	function connecting_time () {
583
		return $this->connecting_time;
584
	}
585
	/**
586
	 * Cloning restriction
587
	 *
588
	 * @final
589
	 */
590
	final function __clone () {
591
	}
592
	/**
593
	 * Disconnecting from DB
594
	 */
595
	abstract function __destruct ();
596
}
597