base::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Gnf\db;
4
5
use Exception;
6
use Gnf\db\Helper\GnfSqlNot;
7
use Gnf\db\Helper\GnfSqlNow;
8
use Gnf\db\Superclass\gnfDbinterface;
9
use InvalidArgumentException;
10
11
abstract class base implements gnfDbinterface
12
{
13
	private $dump;
14
	protected $db;
15
	private $transactionDepth = 0;
16
	private $transactionError = false;
17
18
	// needed for `parent::__construct()`
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
19
	public function __construct()
20
	{
21
	}
22
23
	public function getDb()
24
	{
25
		return $this->db;
26
	}
27
28
	protected function afterConnect()
29
	{
30
		$this->sqlDo("SET NAMES 'utf8'");
31
	}
32
33
	public function sqlBegin()
34
	{
35
		if ($this->transactionDepth == 0) {
36
			$this->transactionBegin();
37
			$this->transactionError = false;
38
		} else {
39
			if ($this->configIsSupportNestedTransaction()) {
40
				$this->transactionBegin();
41
			}
42
		}
43
		$this->transactionDepth++;
44
	}
45
46
	public function sqlEnd()
47
	{
48
		if ($this->transactionError) {
49
			$this->sqlRollback();
50
51
			return false;
52
		} else {
53
			$this->sqlCommit();
54
55
			return true;
56
		}
57
	}
58
59 View Code Duplication
	public function sqlCommit()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
	{
61
		$this->transactionDepth--;
62
		if ($this->transactionDepth == 0) {
63
			$this->transactionCommit();
64
			$this->transactionError = false;
65
		} else {
66
			if ($this->configIsSupportNestedTransaction()) {
67
				$this->transactionCommit();
68
			}
69
			if ($this->transactionDepth < 0) {
70
				throw new Exception('[mysql] transaction underflow');
71
			}
72
		}
73
	}
74
75 View Code Duplication
	public function sqlRollback()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
76
	{
77
		$this->transactionDepth--;
78
		if ($this->transactionDepth == 0) {
79
			$this->transactionRollback();
80
			$this->transactionError = false;
81
		} else {
82
			if ($this->configIsSupportNestedTransaction()) {
83
				$this->transactionRollback();
84
			}
85
			if ($this->transactionDepth < 0) {
86
				throw new Exception('[mysql] transaction underflow');
87
			}
88
		}
89
	}
90
91
	public function isTransactionActive()
92
	{
93
		return $this->transactionDepth > 0;
94
	}
95
96
	/**
97
	 * @param $func callable
98
	 *
99
	 * @return bool transaction success
100
	 * @throws Exception
101
	 */
102
	public function transactional($func)
103
	{
104
		if (!is_callable($func)) {
105
			throw new InvalidArgumentException(
106
				'Expected argument of type "callable", got "' . gettype($func) . '"'
107
			);
108
		}
109
110
		$this->sqlBegin();
111
112
		try {
113
			$func($this);
114
115
			return $this->sqlEnd();
116
		} catch (Exception $e) {
117
			$this->sqlRollback();
118
			throw $e;
119
		}
120
	}
121
122
	/**
123
	 * @param $key
124
	 * @param $value
125
	 *
126
	 * @return string
127
	 *
128
	 * return ''(zero length string) if not available
129
	 * return with '(' . xxx . ')' if has two or more clause
130
	 */
131
	private function callbackSerializeWhere($key, $value)
132
	{
133
		if (is_a($value, '\Gnf\db\Helper\GnfSqlNull') || is_null($value)) {
134
			return self::escapeColumnName($key) . ' is NULL';
135
		}
136
		if (is_a($value, '\Gnf\db\Helper\GnfSqlNot') &&
137
			(
138
				is_a($value->dat, '\Gnf\db\Helper\GnfSqlNull') ||
139
				is_null($value->dat)
140
			)
141
		) {
142
			return self::escapeColumnName($key) . ' is not NULL';
143
		}
144
		if (is_a($value, '\Gnf\db\Helper\GnfSqlNot')) {
145
			$ret = $this->callbackSerializeWhere($key, $value->dat);
146
			if (strlen($ret)) {
147
				return '( !( ' . $ret . ' ) )';
148
			}
149
150
			return '';
151
		}
152 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlLike')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
			return self::escapeColumnName($key) . ' like "%' . $this->escapeLiteral($value->dat) . '%"';
154
		}
155 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlLikeBegin')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
156
			return self::escapeColumnName($key) . ' like "' . $this->escapeLiteral($value->dat) . '%"';
157
		}
158 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlGreater')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
			return self::escapeColumnName($key) . ' > ' . $this->escapeItemExceptNull($value->dat, $key);
160
		}
161 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlLesser')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
			return self::escapeColumnName($key) . ' < ' . $this->escapeItemExceptNull($value->dat, $key);
163
		}
164 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlGreaterEqual')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165
			return self::escapeColumnName($key) . ' >= ' . $this->escapeItemExceptNull($value->dat, $key);
166
		}
167 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlLesserEqual')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
			return self::escapeColumnName($key) . ' <= ' . $this->escapeItemExceptNull($value->dat, $key);
169
		}
170
		if (is_a($value, '\Gnf\db\Helper\GnfSqlBetween')) {
171
			return self::escapeColumnName($key) . ' between ' . $this->escapeItemExceptNull($value->dat, $key) . ' and ' .
172
				$this->escapeItemExceptNull(
173
					$value->dat2,
174
					$key
175
				);
176
		}
177
		if (is_a($value, '\Gnf\db\Helper\GnfSqlRange')) {
178
			return '(' . $this->escapeItemExceptNull($value->dat, $key) . ' <= ' .
179
				self::escapeColumnName(
180
					$key
181
				) . ' and ' . self::escapeColumnName($key) . ' < ' . $this->escapeItemExceptNull($value->dat2, $key) . ')';
182
		}
183 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlAnd')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
			$ret = [];
185
			foreach ($value->dat as $dat) {
186
				if (is_array($dat)) {
187
					$ret[] = '( ' . $this->serializeWhere($dat) . ' )';
188
				} elseif ($dat instanceof GnfSqlNot && is_array($dat->dat)) {
189
					$ret[] = '( ! ( ' . $this->serializeWhere($dat->dat) . ' ) )';
190
				} else {
191
					throw new InvalidArgumentException('process sqlAnd needs where(key, value pair)');
192
				}
193
			}
194
			if (count($ret)) {
195
				return '( ' . implode(' and ', $ret) . ' )';
196
			}
197
198
			return '';
199
		}
200 View Code Duplication
		if (is_a($value, '\Gnf\db\Helper\GnfSqlOr')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
			$ret = [];
202
			foreach ($value->dat as $dat) {
203
				if (is_array($dat)) {
204
					$ret[] = '( ' . $this->serializeWhere($dat) . ' )';
205
				} elseif ($dat instanceof GnfSqlNot && is_array($dat->dat)) {
206
					$ret[] = '( ! ( ' . $this->serializeWhere($dat->dat) . ' ) )';
207
				} else {
208
					throw new InvalidArgumentException('process sqlOr needs where(key, value pair)');
209
				}
210
			}
211
			if (count($ret)) {
212
				return '( ' . implode(' or ', $ret) . ' )';
213
			}
214
215
			return '';
216
		}
217
		if (is_int($key)) {
218
			throw new InvalidArgumentException('cannot implict int key as column : ' . $key);
219
		}
220
		if (is_array($value)) {
221
			//divide
222
			$scalars = [];
223
			$objects = [];
224
			if (count($value) == 0) {
225
				throw new InvalidArgumentException('zero size array, key : ' . $key);
226
			}
227
			foreach ($value as $operand) {
228
				if (is_scalar($operand)) {
229
					$scalars[] = $operand;
230
				} else {
231
					$objects[] = $operand;
232
				}
233
			}
234
235
			//process
236
			if (count($objects)) {
237
				foreach ($objects as $k => $object) {
238
					$objects[$k] = $this->callbackSerializeWhere($key, $object);
239
				}
240
				$objects_query = '( ' . implode(' or ', array_filter($objects, 'strlen')) . ' )';
241
			} else {
242
				$objects_query = '';
243
			}
244
			if (count($scalars)) {
245
				$scalars_query = self::escapeColumnName($key) . ' in ' . $this->escapeItemExceptNull($scalars, $key);
246
			} else {
247
				$scalars_query = '';
248
			}
249
250
			//merge
251
			if (strlen($objects_query) && strlen($scalars_query)) {
252
				return '( ' . $objects_query . ' or ' . $scalars_query . ' )';
253
			}
254
255
			return $objects_query . $scalars_query;
256
		}
257
258
		return self::escapeColumnName($key) . ' = ' . $this->escapeItemExceptNull($value, $key);
259
	}
260
261
	private function serializeWhere($array)
262
	{
263
		if (count($array) == 0) {
264
			throw new InvalidArgumentException('zero size array can not serialize : ' . $array);
265
		}
266
		$wheres = array_map([&$this, 'callbackSerializeWhere'], array_keys($array), $array);
267
		$wheres = array_filter($wheres, 'strlen');
268
269
		return implode(' and ', $wheres);
270
	}
271
272
	private function callbackSerializeUpdate($key, $value)
273
	{
274
		if (is_a($value, '\Gnf\db\Helper\GnfSqlNull') || is_null($value)) {
275
			return self::escapeColumnName($key) . ' = NULL';
276
		}
277
278
		return self::escapeColumnName($key) . ' = ' . $this->escapeItemExceptNull($value, $key);
279
	}
280
281
	private function serializeUpdate($arr)
282
	{
283
		return implode(', ', array_map([&$this, 'callbackSerializeUpdate'], array_keys($arr), $arr));
284
	}
285
286
	private function escapeTable($a)
287
	{
288
		if (is_a($a, '\Gnf\db\Helper\GnfSqlJoin')) {
289
			$ret = '';
290
			foreach ($a->dat as $k => $columns) {
291
				/** @var $has_join_only_one_column
292
				 * if $has_join_only_one_column = true
293
				 * => sqlJoin(array('tb_pay_info.t_id', 'tb_cash.t_id', 'tb_point.t_id'))
294
				 * if $has_join_only_one_column = false
295
				 * => sqljoin(array('tb_pay_info.t_id' => array('tb_cash.t_id', 'tb_point.t_id')))
296
				 */
297
298
				$has_join_only_one_column = is_int($k);
299
300
				if (!is_array($columns)) {
301
					$columns = [$columns];
302
				}
303
				if ($has_join_only_one_column) {
304
					$last_column = '';
305
					foreach ($columns as $key_of_column => $column) {
306
						if (strlen($ret) == 0) {
307
							$ret .= self::escapeTableNameFromFullColumnElement($column);
308 View Code Duplication
						} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
309
							$ret .=
310
								"\n\t" . $a->type . ' ' . self::escapeTableNameFromFullColumnElement($column) .
311
								"\n\t\t" . 'on ' . self::escapeColumnName($last_column) .
312
								' = ' . self::escapeColumnName($column);
313
						}
314
						$last_column = $column;
315
					}
316
				} else {
317
					/** @var $has_more_joinable_where_clause
318
					 * if $has_more_joinable_where_clause = true
319
					 *  => sqljoin(array('tb_pay_info.t_id' => array('tb_cash.t_id', 'tb_cash.type' => 'event')))
320
					 * if $has_more_joinable_where_clause = false
321
					 *  => sqljoin(array('tb_pay_info.t_id' => array('tb_cash.t_id')))
322
					 */
323
324
					$joinable_where_clause = [];
325
					foreach ($columns as $key_of_column => $column) {
326
						$has_more_joinable_where_clause = !is_int($key_of_column);
327
						if ($has_more_joinable_where_clause) {
328
							$table_name = self::escapeTableNameFromFullColumnElement($key_of_column);
329
							$joinable_where_clause[$table_name][$key_of_column] = $column;
330
						}
331
					}
332
333
					foreach ($columns as $key_of_column => $column) {
334
						$has_more_joinable_where_clause = !is_int($key_of_column);
335
						if (!$has_more_joinable_where_clause) {
336
							$join_left_column = $k;
337
							$join_right_column = $column;
338
339
							if (strlen($ret) == 0) {
340
								$ret .= self::escapeTableNameFromFullColumnElement($join_left_column) . ' ' .
341
									"\n\t" . $a->type . ' ' .
342
									self::escapeTableNameFromFullColumnElement($join_right_column) .
343
									"\n\t\t" . 'on ' .
344
									self::escapeColumnName($join_left_column) .
345
									' = ' .
346
									self::escapeColumnName($join_right_column);
347 View Code Duplication
							} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
348
								$ret .= ' ' .
349
									"\n\t" . $a->type .
350
									' ' .
351
									self::escapeTableNameFromFullColumnElement($join_right_column) .
352
									"\n\t\t" . 'on ' .
353
									self::escapeColumnName($join_left_column) .
354
									' = ' .
355
									self::escapeColumnName($join_right_column);
356
							}
357
							$join_right_table_name = self::escapeTableNameFromFullColumnElement($join_right_column);
358
							if (isset($joinable_where_clause[$join_right_table_name])) {
359
								$ret .= ' and '
360
									. $this->serializeWhere($joinable_where_clause[$join_right_table_name]);
361
								unset($joinable_where_clause[$join_right_table_name]);
362
							}
363
						}
364
					}
365
					foreach ($joinable_where_clause as $table_name => $where) {
366
						$ret .= ' and ' . $this->serializeWhere($where);
367
					}
368
				}
369
			}
370
371
			return $ret;
372
		}
373
		if (is_a($a, '\Gnf\db\Helper\GnfSqlTable')) {
374
			$a = $a->dat;
375
		}
376
377
		return self::escapeTableNameFromTableElement($a);
378
	}
379
380
	private static function escapeTableNameFromTableElement($tablename)
381
	{
382
		return self::escapeFullColumnElement($tablename);
383
	}
384
385
	private static function escapeFullColumnElement($table_column_element)
386
	{
387
		$table_column_element = preg_replace("/\..+/", "", $table_column_element);
388
		$table_column_element = str_replace('`', '', $table_column_element);
389
390
		return '`' . $table_column_element . '`';
391
	}
392
393
	private static function escapeTableNameFromFullColumnElement($fullsized_column)
394
	{
395
		$dot_count = substr_count($fullsized_column, '.');
396
		if ($dot_count != 1 && $dot_count != 2) {
397
			throw new Exception('invalid column name (' . $fullsized_column . ') to extract table name');
398
		}
399
		$fullsized_column_items = explode('.', $fullsized_column);
400
		array_pop($fullsized_column_items);
401
		$fullsized_column_items = array_map(
402
			function ($item) {
403
				return self::escapeFullColumnElement($item);
404
			},
405
			$fullsized_column_items
406
		);
407
408
		return implode('.', $fullsized_column_items);
409
	}
410
411
	private static function escapeColumnName($k)
412
	{
413
		if (is_int($k)) {
414
			throw new InvalidArgumentException('cannot implict int key as column : ' . $k);
415
		}
416
		$k = str_replace('`', '', $k);
417
		$k = str_replace('.', '`.`', $k);
418
419
		return '`' . $k . '`';
420
	}
421
422
	//referenced yutarbbs(http://code.google.com/p/yutarbbs) by holies
423
	/**
424
	 * @param $value
425
	 *
426
	 * @return string
427
	 */
428
	private function escapeItem($value)
429
	{
430
		if (is_a($value, '\Gnf\db\Helper\GnfSqlNull') || is_null($value)) {
431
			return 'NULL';
432
		}
433
434
		return $this->escapeItemExceptNull($value);
435
	}
436
437
	/**
438
	 * @param $value
439
	 * @param $column null|string // is string if update
440
	 *
441
	 * @return string
442
	 */
443
	private function escapeItemExceptNull($value, $column = null)
444
	{
445
		if (is_scalar($value)) {
446
			if (is_bool($value)) {
447
				if ($value) {
448
					return 'true';
449
				} else {
450
					return 'false';
451
				}
452
			}
453
454
			return '"' . $this->escapeLiteral($value) . '"';
455
		} elseif (is_array($value)) {
456
			if (count($value) == 0) {
457
				throw new InvalidArgumentException('zero size array, key : ' . $value);
458
			}
459
460
			return '(' . implode(', ', array_map([&$this, 'escapeItemExceptNull'], $value)) . ')';
461
		} elseif (is_object($value)) {
462
			if (is_a($value, GnfSqlNow::class)) {
463
				return 'now()';
464
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlPassword')) {
465
				return 'password(' . $this->escapeItemExceptNull($value->dat) . ')';
466
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlLike')) {
467
				return '"%' . $this->escapeLiteral($value->dat) . '%"';
468
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlLikeBegin')) {
469
				return '"' . $this->escapeLiteral($value->dat) . '%"';
470
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlRaw')) {
471
				return $value->dat;
472
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlTable')) {
473
				return $this->escapeTable($value);
474
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlColumn')) {
475
				return self::escapeColumnName($value->dat);
476
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlWhere')) {
477
				return $this->serializeWhere($value->dat);
478
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlLimit')) {
479
				return 'limit ' . $value->from . ', ' . $value->count;
480
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlAdd') && is_string($column)) {//only for update
481
				if ($value->dat > 0) {
482
					return self::escapeColumnName($column) . ' + ' . ($value->dat);
483
				} elseif ($value->dat < 0) {
484
					return self::escapeColumnName($column) . ' ' . ($value->dat);
485
				}
486
487
				return self::escapeColumnName($column);
488
			} elseif (is_a($value, '\Gnf\db\Helper\GnfSqlStrcat') && is_string($column)) {//only for update
489
				return 'concat(ifnull(' . self::escapeColumnName($column) . ', ""), ' . $this->escapeItemExceptNull(
490
						$value->dat
491
					) . ')';
492
			}
493
494
			return $this->escapeItemExceptNull($value->dat);
495
		}
496
		throw new InvalidArgumentException('invalid escape item');
497
	}
498
499
	private function parseQuery($args)
500
	{
501
		if (count($args) >= 1) {
502
			$sql = array_shift($args);
503
			$escaped_items = array_map([&$this, 'escapeItemExceptNull'], $args);
504
505
			$breaked_sql_blocks = explode('?', $sql);
506
			foreach ($breaked_sql_blocks as $index => $breaked_sql_block) {
507
				if ($index == 0) {
508
					continue;
509
				}
510
				if (count($escaped_items) == 0) {
511
					throw new InvalidArgumentException('unmatched "? count" with "argument count"');
512
				}
513
				$escaped_item = array_shift($escaped_items);
514
				$breaked_sql_blocks[$index] = $escaped_item . $breaked_sql_block;
515
			}
516
			if (count($escaped_items) != 0) {
517
				throw new InvalidArgumentException('unmatched "? count" with "argument count"');
518
			}
519
520
			return implode('', $breaked_sql_blocks);
521
		}
522
523
		return "";
524
	}
525
526
	public function sqlDumpBegin()
527
	{
528
		if (!is_array($this->dump)) {
529
			$this->dump = [];
530
		}
531
		array_push($this->dump, []);
532
	}
533
534
	public function sqlDumpEnd()
535
	{
536
		if (count($this->dump)) {
537
			return array_pop($this->dump);
538
		}
539
540
		return null;
541
	}
542
543
	public function sqlDo($sql)
544
	{
545
		$sql = $this->parseQuery(func_get_args());
546
		$ret = $this->sqlDoWithoutParsing($sql);
547
548
		return $ret;
549
	}
550
551
	/**
552
	 * @param $sql
553
	 *
554
	 * @return mixed
555
	 * @throws Exception
556
	 */
557
	private function sqlDoWithoutParsing($sql)
558
	{
559
		if (count($this->dump)) {
560
			foreach ($this->dump as $k => $v) {
561
				array_push($this->dump[$k], $sql);
562
			}
563
		}
564
		$ret = $this->query($sql);
565
		$err = $this->getError($ret);
566
		if ($err !== null) {
567
			$this->transactionError = true;
568
			throw new Exception('[sql error] ' . $err->message . ' : ' . $sql);
569
		}
570
571
		return $ret;
572
	}
573
574
	public function sqlDump($sql)
0 ignored issues
show
Unused Code introduced by
The parameter $sql is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
575
	{
576
		return $this->parseQuery(func_get_args());
577
	}
578
579
	public function sqlData($sql)
580
	{
581
		$sql = $this->parseQuery(func_get_args());
582
		$res = $this->sqlDoWithoutParsing($sql);
583
		if ($res) {
584
			$arr = $this->fetchRow($res);
585
			if (isset($arr[0])) {
586
				return $arr[0];
587
			}
588
		}
589
590
		return null;
591
	}
592
593 View Code Duplication
	public function sqlDatas($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
594
	{
595
		$sql = $this->parseQuery(func_get_args());
596
		$res = $this->sqlDoWithoutParsing($sql);
597
		$ret = [];
598
		if ($res) {
599
			while ($arr = $this->fetchRow($res)) {
600
				$ret[] = $arr[0];
601
			}
602
		}
603
604
		return $ret;
605
	}
606
607 View Code Duplication
	public function sqlArray($sql)
0 ignored issues
show
Unused Code introduced by
The parameter $sql is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
608
	{
609
		$sql = $this->parseQuery(func_get_args());
610
		$res = $this->sqlDoWithoutParsing($sql);
611
		if ($res) {
612
			$arr = $this->fetchRow($res);
613
			if ($arr) {
614
				return $arr;
615
			}
616
		}
617
618
		return null;
619
	}
620
621 View Code Duplication
	public function sqlArrays($sql)
0 ignored issues
show
Unused Code introduced by
The parameter $sql is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
622
	{
623
		$sql = $this->parseQuery(func_get_args());
624
		$res = $this->sqlDoWithoutParsing($sql);
625
		$ret = [];
626
		if ($res) {
627
			while ($arr = $this->fetchRow($res)) {
628
				$ret[] = $arr;
629
			}
630
		}
631
632
		return $ret;
633
	}
634
635 View Code Duplication
	public function sqlDict($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
636
	{
637
		$sql = $this->parseQuery(func_get_args());
638
		$res = $this->sqlDoWithoutParsing($sql);
639
		if ($res) {
640
			$arr = $this->fetchAssoc($res);
641
			if ($arr !== false) {
642
				return $arr;
643
			}
644
		}
645
646
		return null;
647
	}
648
649 View Code Duplication
	public function sqlDicts($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
650
	{
651
		$sql = $this->parseQuery(func_get_args());
652
		$res = $this->sqlDoWithoutParsing($sql);
653
		$ret = [];
654
		if ($res) {
655
			while ($arr = $this->fetchAssoc($res)) {
656
				$ret[] = $arr;
657
			}
658
		}
659
660
		return $ret;
661
	}
662
663 View Code Duplication
	public function sqlObject($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
664
	{
665
		$sql = $this->parseQuery(func_get_args());
666
		$res = $this->sqlDoWithoutParsing($sql);
667
		if ($res) {
668
			$arr = $this->fetchObject($res);
669
			if ($arr !== false) {
670
				return $arr;
671
			}
672
		}
673
674
		return null;
675
	}
676
677 View Code Duplication
	public function sqlObjects($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
678
	{
679
		$sql = $this->parseQuery(func_get_args());
680
		$res = $this->sqlDoWithoutParsing($sql);
681
		$ret = [];
682
		if ($res) {
683
			while ($arr = $this->fetchObject($res)) {
684
				$ret[] = $arr;
685
			}
686
		}
687
688
		return $ret;
689
	}
690
691 View Code Duplication
	public function sqlLine($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
692
	{
693
		$sql = $this->parseQuery(func_get_args());
694
		$res = $this->sqlDoWithoutParsing($sql);
695
		if ($res) {
696
			$arr = $this->fetchRow($res);
697
			if ($arr !== false) {
698
				return $arr;
699
			}
700
		}
701
702
		return null;
703
	}
704
705 View Code Duplication
	public function sqlLines($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
706
	{
707
		$sql = $this->parseQuery(func_get_args());
708
		$res = $this->sqlDoWithoutParsing($sql);
709
		$ret = [];
710
		if ($res) {
711
			while ($arr = $this->fetchRow($res)) {
712
				$ret[] = $arr;
713
			}
714
		}
715
716
		return $ret;
717
	}
718
719
	public function sqlDictsArgs()
720
	{
721
		$args = func_get_args();
722
		if (!is_array($args[1])) {
723
			trigger_error("sqlDictsArgs's second argument must be an array");
724
			die;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sqlDictsArgs() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
725
		}
726
		array_unshift($args[1], $args[0]);
727
		$args = $args[1];
728
729
		return @call_user_func_array([&$this, 'sqlDicts'], $args);
730
	}
731
732
	public function sqlCount($table, $where)
733
	{
734
		$sql = "SELECT count(*) FROM ? WHERE ?";
735
736
		return $this->sqlData($sql, sqlTable($table), sqlWhere($where));
737
	}
738
739
	public function sqlInsert($table, $dats)
740
	{
741
		$table = $this->escapeItemExceptNull(sqlTable($table));
742
		$dats_keys = array_keys($dats);
743
		$keys = implode(', ', array_map([&$this, 'escapeColumnName'], $dats_keys));
744
		$values = implode(', ', array_map([&$this, 'escapeItem'], $dats, $dats_keys));
745
		$sql = "INSERT INTO " . $table . " (" . $keys . ") VALUES (" . $values . ")";
746
		$stmt = $this->sqlDoWithoutParsing($sql);
747
748
		return $this->getAffectedRows($stmt);
749
	}
750
751
	/**
752
	 * @param       $table
753
	 * @param array $dat_keys
754
	 * @param array $dat_valuess
755
	 *
756
	 * @return int
757
	 */
758
	public function sqlInsertBulk($table, $dat_keys, $dat_valuess)
759
	{
760
		$table = $this->escapeItemExceptNull(sqlTable($table));
761
		$keys = implode(', ', array_map([&$this, 'escapeColumnName'], $dat_keys));
762
		$bulk_values = [];
763
		foreach ($dat_valuess as $dat_values) {
764
			$bulk_values[] = implode(', ', array_map([&$this, 'escapeItem'], $dat_values));
765
		}
766
		$sql = "INSERT INTO " . $table . " (" . $keys . ") VALUES ";
767
		foreach ($bulk_values as $values) {
768
			$sql .= ' ( ' . $values . ' ),';
769
		}
770
		$sql = substr($sql, 0, -1);
771
		$stmt = $this->sqlDoWithoutParsing($sql);
772
773
		return $this->getAffectedRows($stmt);
774
	}
775
776
	public function sqlInsertOrUpdate($table, $dats, $update = null)
777
	{
778
		/**
779
		 * MySQL 5.1 에서 duplicate key update 구문에 unique 컬럼을 쓰면 퍼포먼스에 문제가 있다.
780
		 * 따라서 update 에 해당하는 것만 따로 받을 수 있도록 수정하였음.
781
		 * 이 후 MySQL 버전에서 이 문제가 해결되면 $update 변수는 삭제될 예정.
782
		 */
783
		if ($update == null) {
784
			$update = $dats;
785
		}
786
787
		$table = $this->escapeItemExceptNull(sqlTable($table));
788
		$dats_keys = array_keys($dats);
789
		$keys = implode(', ', array_map([&$this, 'escapeColumnName'], $dats_keys));
790
		$values = implode(', ', array_map([&$this, 'escapeItem'], $dats, $dats_keys));
791
		$update = $this->serializeUpdate($update);
792
		$sql = "INSERT INTO " . $table . " (" . $keys . ") VALUES (" . $values . ") ON DUPLICATE KEY UPDATE " . $update;
793
		$stmt = $this->sqlDoWithoutParsing($sql);
794
795
		return min(1, $this->getAffectedRows($stmt));
796
	}
797
798
	public function sqlUpdate($table, $dats, $where)
799
	{
800
		$table = $this->escapeItemExceptNull(sqlTable($table));
801
		$update = $this->serializeUpdate($dats);
802
		$where = $this->serializeWhere($where);
803
		$sql = "UPDATE " . $table . " SET " . $update . " WHERE " . $where;
804
		$stmt = $this->sqlDoWithoutParsing($sql);
805
806
		return $this->getAffectedRows($stmt);
807
	}
808
809
	public function sqlDelete($table, $where)
810
	{
811
		$table = $this->escapeItemExceptNull(sqlTable($table));
812
		$where = $this->serializeWhere($where);
813
		$sql = "DELETE FROM " . $table . " WHERE " . $where;
814
		$stmt = $this->sqlDoWithoutParsing($sql);
815
816
		return $this->getAffectedRows($stmt);
817
	}
818
819
	protected function checkConnectionOrTry()
820
	{
821
		if ($this->hasConnected()) {
822
			return;
823
		}
824
		$this->doConnect();
825
	}
826
827
	abstract protected function doConnect();
828
829
	abstract protected function hasConnected();
830
831
	abstract public function select_db($db);
832
833
	abstract protected function transactionBegin();
834
835
	abstract protected function transactionCommit();
836
837
	abstract protected function transactionRollback();
838
839
	/**
840
	 * @return bool
841
	 */
842
	abstract protected function configIsSupportNestedTransaction();
843
844
	abstract protected function escapeLiteral($value);
845
846
	abstract protected function query($sql);
847
848
	abstract protected function getError($handle);
849
850
	abstract protected function fetchRow($handle);
851
852
	abstract protected function fetchAssoc($handle);
853
854
	abstract protected function fetchObject($handle);
855
856
	abstract protected function fetchBoth($handle);
857
858
	/**
859
	 * @param $handle
860
	 *
861
	 * @return int
862
	 */
863
	abstract protected function getAffectedRows($handle);
864
}
865