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()`
|
|
|
|
|
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()
|
|
|
|
|
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()
|
|
|
|
|
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')) {
|
|
|
|
|
153
|
|
|
return self::escapeColumnName($key) . ' like "%' . $this->escapeLiteral($value->dat) . '%"';
|
154
|
|
|
}
|
155
|
|
View Code Duplication |
if (is_a($value, '\Gnf\db\Helper\GnfSqlLikeBegin')) {
|
|
|
|
|
156
|
|
|
return self::escapeColumnName($key) . ' like "' . $this->escapeLiteral($value->dat) . '%"';
|
157
|
|
|
}
|
158
|
|
View Code Duplication |
if (is_a($value, '\Gnf\db\Helper\GnfSqlGreater')) {
|
|
|
|
|
159
|
|
|
return self::escapeColumnName($key) . ' > ' . $this->escapeItemExceptNull($value->dat, $key);
|
160
|
|
|
}
|
161
|
|
View Code Duplication |
if (is_a($value, '\Gnf\db\Helper\GnfSqlLesser')) {
|
|
|
|
|
162
|
|
|
return self::escapeColumnName($key) . ' < ' . $this->escapeItemExceptNull($value->dat, $key);
|
163
|
|
|
}
|
164
|
|
View Code Duplication |
if (is_a($value, '\Gnf\db\Helper\GnfSqlGreaterEqual')) {
|
|
|
|
|
165
|
|
|
return self::escapeColumnName($key) . ' >= ' . $this->escapeItemExceptNull($value->dat, $key);
|
166
|
|
|
}
|
167
|
|
View Code Duplication |
if (is_a($value, '\Gnf\db\Helper\GnfSqlLesserEqual')) {
|
|
|
|
|
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')) {
|
|
|
|
|
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')) {
|
|
|
|
|
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 {
|
|
|
|
|
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 {
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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)
|
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
|
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.