db::insert()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 6
nop 3
dl 0
loc 24
rs 9.7998
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | PHPSpider [ A PHP Framework For Crawler ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: Seatle Yang <[email protected]>
10
// +----------------------------------------------------------------------
11
12
//----------------------------------
13
// PHPSpider数据库类文件
14
//----------------------------------
15
16
namespace phpspider\core;
17
18
class db
19
{
20
    private static $configs = array();
21
    private static $rsid;
22
    private static $links = array();
23
    private static $link_name = 'default';
24
    private static $autocommiting = false;
25
26
    public static function _init()
27
    {
28
        // 获取配置
29
        $config = self::$link_name == 'default' ? self::_get_default_config() : self::$configs[self::$link_name];
30
31
        // 创建连接
32
        if (empty(self::$links[self::$link_name]) || empty(self::$links[self::$link_name]['conn']))
33
        {
34
            // 第一次连接,初始化fail和pid
35
            if (empty(self::$links[self::$link_name])) 
36
            {
37
                self::$links[self::$link_name]['fail'] = 0;
38
                self::$links[self::$link_name]['pid'] = function_exists('posix_getpid') ? posix_getpid() : 0; 
39
                //echo "progress[".self::$links[self::$link_name]['pid']."] create db connect[".self::$link_name."]\n";
40
            }
41
            self::$links[self::$link_name]['conn'] = mysqli_connect($config['host'], $config['user'], $config['pass'], $config['name'], $config['port']);
42
            if(mysqli_connect_errno())
43
            {
44
                self::$links[self::$link_name]['fail']++;
45
                $errmsg = 'Mysql Connect failed['.self::$links[self::$link_name]['fail'].']: ' . mysqli_connect_error();
46
                echo util::colorize(date("H:i:s") . " {$errmsg}\n\n", 'fail');
47
                log::add($errmsg, "Error");
48
                // 连接失败5次,中断进程
49
                if (self::$links[self::$link_name]['fail'] >= 5) 
50
                {
51
                    exit(250);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
52
                }
53
                self::_init($config);
0 ignored issues
show
Unused Code introduced by
The call to phpspider\core\db::_init() has too many arguments starting with $config. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

53
                self::/** @scrutinizer ignore-call */ 
54
                      _init($config);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
54
            }
55
            else
56
            {
57
                mysqli_query(self::$links[self::$link_name]['conn'], " SET character_set_connection=utf8, character_set_results=utf8, character_set_client=binary, sql_mode='' ");
58
            }
59
        }
60
        else 
61
        {
62
            $curr_pid = function_exists('posix_getpid') ? posix_getpid() : 0;
63
            // 如果父进程已经生成资源就释放重新生成,因为多进程不能共享连接资源
64
            if (self::$links[self::$link_name]['pid'] != $curr_pid) 
65
            {
66
                self::clear_link();
67
            }
68
        }
69
    }
70
71
    /**
72
     * 重新设置连接
73
     * 传空的话就等于关闭数据库再连接
74
     * 在多进程环境下如果主进程已经调用过了,子进程一定要调用一次 clear_link,否则会报错:
75
     * Error while reading greeting packet. PID=19615,这是两个进程互抢一个连接句柄引起的
76
     * 
77
     * @param array $config
78
     * @return void
79
     * @author seatle <[email protected]> 
80
     * @created time :2016-03-29 00:51
81
     */
82
    public static function clear_link()
83
    {
84
        if(self::$links) 
0 ignored issues
show
Bug Best Practice introduced by
The expression self::links of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
85
        {
86
            foreach(self::$links as $k=>$v)
87
            {
88
                @mysqli_close($v['conn']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mysqli_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

88
                /** @scrutinizer ignore-unhandled */ @mysqli_close($v['conn']);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
89
                unset(self::$links[$k]);
90
            }
91
        }
92
        // 注意,只会连接最后一个,不过貌似也够用了啊
93
        self::_init();
94
    }
95
96
    /**
97
     * 改变链接为指定配置的链接(如果不同时使用多个数据库,不会涉及这个操作)
98
     * @parem  $link_name 链接标识名
99
     * @parem  $config 多次使用时, 这个数组只需传递一次
100
     *         config 格式与 $GLOBALS['config']['db'] 一致
101
     * @return void
102
     */
103
    public static function set_connect($link_name, $config = array())
104
    {
105
        self::$link_name = $link_name;
106
        if (!empty($config))
107
        {
108
            self::$configs[self::$link_name] = $config;
109
        }
110
        else
111
        {
112
            if (empty(self::$configs[self::$link_name]))
113
            {
114
                throw new Exception("You not set a config array for connect!");
0 ignored issues
show
Bug introduced by
The type phpspider\core\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
115
            }
116
        }
117
    }
118
    
119
    
120
    /**
121
     * 还原为默认连接(如果不同时使用多个数据库,不会涉及这个操作)
122
     * @parem $config 指定配置(默认使用inc_config.php的配置)
123
     * @return void
124
     */
125
    public static function set_connect_default()
126
    {
127
        $config = self::_get_default_config();
128
        self::set_connect('default', $config);
129
    }
130
    
131
    
132
    /**
133
     * 获取默认配置
134
     */
135
    protected static function _get_default_config()
136
    {
137
        if (empty(self::$configs['default']))
138
        {
139
            if (!is_array($GLOBALS['config']['db']))
140
            {
141
                exit('db.php _get_default_config()' . '没有mysql配置');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
142
            }
143
            self::$configs['default'] = $GLOBALS['config']['db'];
144
        }
145
        return self::$configs['default'];
146
    }
147
148
    /**
149
     * 返回查询游标
150
     * @return rsid
0 ignored issues
show
Bug introduced by
The type phpspider\core\rsid was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
151
     */
152
    protected static function _get_rsid($rsid = '')
153
    {
154
        return $rsid == '' ? self::$rsid : $rsid;
155
    }
156
157
    public static function autocommit($mode = false)
158
    {
159
        if ( self::$autocommiting ) 
160
        {
161
            return true;
162
        }
163
164
        self::$autocommiting = true;
165
166
        self::_init();
167
        return mysqli_autocommit(self::$links[self::$link_name]['conn'], $mode);
168
    }
169
170
    public static function begin_tran()
171
    {
172
        return self::autocommit(false);
173
    }
174
175
    public static function commit()
176
    {
177
        mysqli_commit(self::$links[self::$link_name]['conn']);
178
        self::autocommit(true);
179
        return true;
180
    }
181
182
183
    public static function rollback()
184
    {
185
        mysqli_rollback(self::$links[self::$link_name]['conn']);
186
        self::autocommit(true);
187
        return true;
188
    }
189
190
    public static function query($sql)
191
    {
192
        $sql = trim($sql);
193
194
        // 初始化数据库
195
        self::_init();
196
        self::$rsid = @mysqli_query(self::$links[self::$link_name]['conn'], $sql);
197
198
        if (self::$rsid === false)
199
        {
200
            // 不要每次都ping,浪费流量浪费性能,执行出错了才重新连接
201
            $errno = mysqli_errno(self::$links[self::$link_name]['conn']);
202
            if ($errno == 2013 || $errno == 2006) 
203
            {
204
                $errmsg = mysqli_error(self::$links[self::$link_name]['conn']);
205
                log::add($errmsg, "Error");
206
207
                @mysqli_close(self::$links[self::$link_name]['conn']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mysqli_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

207
                /** @scrutinizer ignore-unhandled */ @mysqli_close(self::$links[self::$link_name]['conn']);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
208
                self::$links[self::$link_name]['conn'] = null;
209
                return self::query($sql);
210
            }
211
212
            $errmsg = "Query SQL: ".$sql;
213
            log::add($errmsg, "Warning");
214
            $errmsg = "Error SQL: ".mysqli_error(self::$links[self::$link_name]['conn']);
215
            log::add($errmsg, "Warning");
216
217
            $backtrace = debug_backtrace();
218
            array_shift($backtrace);
219
            $narr = array('class', 'type', 'function', 'file', 'line');
220
            $err = "debug_backtrace:\n";
221
            foreach($backtrace as $i => $l)
222
            {
223
                foreach($narr as $k)
224
                {
225
                    if( !isset($l[$k]) ) 
226
                    {
227
                        $l[$k] = '';
228
                    }
229
                }
230
                $err .= "[$i] in function {$l['class']}{$l['type']}{$l['function']} ";
231
                if($l['file']) $err .= " in {$l['file']} ";
232
                if($l['line']) $err .= " on line {$l['line']} ";
233
                $err .= "\n";
234
            }
235
            log::add($err);
236
237
            return false;
238
        }
239
        else
240
        {
241
            return self::$rsid;
242
        }
243
    }
244
245
    public static function fetch($rsid = '')
246
    {
247
        $rsid = self::_get_rsid($rsid);
248
        $row = mysqli_fetch_array($rsid, MYSQLI_ASSOC);
249
        return $row;
250
    }
251
252
    public static function get_one($sql)
253
    {
254
        if (!preg_match("/limit/i", $sql))
255
        {
256
            $sql = preg_replace("/[,;]$/i", '', trim($sql)) . " limit 1 ";
257
        }
258
        $rsid = self::query($sql);
259
        if ($rsid === false) 
260
        {
261
            return array();
262
        }
263
        $row = self::fetch($rsid);
264
        self::free($rsid);
265
        return $row;
266
    }
267
268
    public static function get_all($sql)
269
    {
270
        $rsid = self::query($sql);
271
        if ($rsid === false) 
272
        {
273
            return array();
274
        }
275
        while ( $row = self::fetch($rsid) )
276
        {
277
            $rows[] = $row;
278
        }
279
        self::free($rsid);
280
        return empty($rows) ? false : $rows;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $rows does not seem to be defined for all execution paths leading up to this point.
Loading history...
281
    }
282
283
    public static function free($rsid)
284
    {
285
        return mysqli_free_result($rsid);
0 ignored issues
show
Bug introduced by
Are you sure the usage of mysqli_free_result($rsid) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
286
    }
287
288
    public static function insert_id()
289
    {
290
        return mysqli_insert_id(self::$links[self::$link_name]['conn']);
291
    }
292
293
    public static function affected_rows()
294
    {
295
        return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
296
    }
297
298
    public static function insert($table = '', $data = null, $return_sql = false)
299
    {
300
        $items_sql = $values_sql = "";
301
        foreach ($data as $k => $v)
302
        {
303
            $v = stripslashes($v);
304
            $v = addslashes($v);
305
            $items_sql .= "`$k`,";
306
            $values_sql .= "\"$v\",";
307
        }
308
        $sql = "Insert Ignore Into `{$table}` (" . substr($items_sql, 0, -1) . ") Values (" . substr($values_sql, 0, -1) . ")";
309
        if ($return_sql) 
310
        {
311
            return $sql;
312
        }
313
        else 
314
        {
315
            if (self::query($sql))
316
            {
317
                return mysqli_insert_id(self::$links[self::$link_name]['conn']);
318
            }
319
            else 
320
            {
321
                return false;
322
            }
323
        }
324
    }
325
326
    public static function insert_batch($table = '', $set = NULL, $return_sql = FALSE) 
327
    {
328
        if (empty($table) || empty($set)) 
329
        {
330
            return false;
331
        }
332
        $set = self::strsafe($set);
333
        $fields = self::get_fields($table);
334
335
        $keys_sql = $vals_sql = array();
336
        foreach ($set as $i=>$val) 
337
        {
338
            ksort($val);
339
            $vals = array();
340
            foreach ($val as $k => $v)
341
            {
342
                // 过滤掉数据库没有的字段
343
                if (!in_array($k, $fields)) 
344
                {
345
                    continue;
346
                }
347
                // 如果是第一个数组,把key当做插入条件
348
                if ($i == 0 && $k == 0) 
349
                {
350
                    $keys_sql[] = "`$k`";
351
                }
352
                $vals[] = "\"$v\"";
353
            }
354
            $vals_sql[] = implode(",", $vals);
355
        }
356
357
        $sql = "Insert Ignore Into `{$table}`(".implode(", ", $keys_sql).") Values (".implode("), (", $vals_sql).")";
358
359
        if ($return_sql) return $sql;
360
        
361
        $rt = self::query($sql);
362
        $insert_id = self::insert_id();
363
        $return = empty($insert_id) ? $rt : $insert_id;
364
        return $return;
365
    }
366
367
    public static function update_batch($table = '', $set = NULL, $index = NULL, $where = NULL, $return_sql = FALSE) 
368
    {
369
        if (empty($table) || is_null($set) || is_null($index)) 
370
        {
371
            // 不要用exit,会中断程序
372
            return false;
373
        }
374
        $set = self::strsafe($set);
375
        $fields = self::get_fields($table);
376
377
        $ids = array();
378
        foreach ($set as $val)
379
		{
380
            ksort($val);
381
            // 去重,其实不去也可以,因为相同的when只会执行第一个,后面的就直接跳过不执行了
382
            $key = md5($val[$index]);
383
			$ids[$key] = $val[$index];
384
385
			foreach (array_keys($val) as $field)
386
			{
387
				if ($field != $index)
388
				{
389
					$final[$field][$key] =  'When `'.$index.'` = "'.$val[$index].'" Then "'.$val[$field].'"';
390
				}
391
			}
392
		}
393
        //$ids = array_values($ids);
394
395
        // 如果不是数组而且不为空,就转数组
396
        if (!is_array($where) && !empty($where))
397
        {
398
            $where = array($where);
399
        }
400
        $where[] = $index.' In ("'.implode('","', $ids).'")';
401
        $where = empty($where) ? "" : " Where ".implode(" And ", $where);
402
403
		$sql = "Update `".$table."` Set ";
404
		$cases = '';
405
406
		foreach ($final as $k => $v)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $final does not seem to be defined for all execution paths leading up to this point.
Loading history...
407
		{
408
            // 过滤掉数据库没有的字段
409
            if (!in_array($k, $fields)) 
410
            {
411
                continue;
412
            }
413
			$cases .= '`'.$k.'` = Case '."\n";
414
			foreach ($v as $row)
415
			{
416
				$cases .= $row."\n";
417
			}
418
419
			$cases .= 'Else `'.$k.'` End, ';
420
		}
421
422
		$sql .= substr($cases, 0, -2);
423
424
        // 其实不带 Where In ($index) 的条件也可以的
425
		$sql .= $where;
426
427
        if ($return_sql) return $sql;
428
        
429
        $rt = self::query($sql);
430
        $insert_id = self::affected_rows();
0 ignored issues
show
Unused Code introduced by
The assignment to $insert_id is dead and can be removed.
Loading history...
431
        $return = empty($affected_rows) ? $rt : $affected_rows;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $affected_rows seems to never exist and therefore empty should always be true.
Loading history...
432
        return $return;
433
    }
434
435
    public static function update($table = '', $data = array(), $where = null, $return_sql = false)
436
    {
437
        $sql = "UPDATE `{$table}` SET ";
438
        foreach ($data as $k => $v)
439
        {
440
            $v = stripslashes($v);
441
            $v = addslashes($v);
442
            $sql .= "`{$k}` = \"{$v}\",";
443
        }
444
        if (!is_array($where))
445
        {
446
            $where = array($where);
447
        }
448
        // 删除空字段,不然array("")会成为WHERE
449
        foreach ($where as $k => $v)
450
        {
451
            if (empty($v))
452
            {
453
                unset($where[$k]);
454
            }
455
        }
456
        $where = empty($where) ? "" : " Where " . implode(" And ", $where);
457
        $sql = substr($sql, 0, -1) . $where;
458
        if ($return_sql) 
459
        {
460
            return $sql;
461
        }
462
        else 
463
        {
464
            if (self::query($sql))
465
            {
466
                return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
467
            }
468
            else 
469
            {
470
                return false;
471
            }
472
        }
473
    }
474
475
    public static function delete($table = '', $where = null, $return_sql = false)
476
    {
477
        // 小心全部被删除了
478
        if (empty($where)) 
479
        {
480
            return false;
481
        }
482
        $where = 'Where ' . (!is_array($where) ? $where : implode(' And ', $where));
483
        $sql = "Delete From `{$table}` {$where}";
484
        if ($return_sql) 
485
        {
486
            return $sql;
487
        }
488
        else 
489
        {
490
            if (self::query($sql))
491
            {
492
                return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
493
            }
494
            else 
495
            {
496
                return false;
497
            }
498
        }
499
    }
500
501
    public static function ping()
502
    {
503
        if (!mysqli_ping(self::$links[self::$link_name]['conn']))
504
        {
505
            @mysqli_close(self::$links[self::$link_name]['conn']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mysqli_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

505
            /** @scrutinizer ignore-unhandled */ @mysqli_close(self::$links[self::$link_name]['conn']);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
506
            self::$links[self::$link_name]['conn'] = null;
507
            self::_init();
508
        }
509
    }
510
511
    public static function strsafe($array)
512
    {
513
        $arrays = array();
514
        if(is_array($array)===true)
515
        {
516
            foreach ($array as $key => $val)
517
            {                
518
                if(is_array($val)===true)
519
                {
520
                    $arrays[$key] = self::strsafe($val);
521
                }
522
                else 
523
                {
524
                    //先去掉转义,避免下面重复转义了
525
                    $val = stripslashes($val);
526
                    //进行转义
527
                    $val = addslashes($val);
528
                    //处理addslashes没法处理的 _ % 字符
529
                    //$val = strtr($val, array('_'=>'\_', '%'=>'\%'));
530
                    $arrays[$key] = $val;
531
                }
532
            }
533
            return $arrays;
534
        }
535
        else 
536
        {
537
            $array = stripslashes($array);
538
            $array = addslashes($array);
539
            //$array = strtr($array, array('_'=>'\_', '%'=>'\%'));
540
            return $array;
541
        }
542
    }
543
544
    // 这个是给insert、update、insert_batch、update_batch用的
545
    public static function get_fields($table)
546
    {
547
        // $sql = "SHOW COLUMNS FROM $table"; //和下面的语句效果一样
548
        $rows = self::get_all("Desc `{$table}`");
549
        $fields = array();
550
        foreach ($rows as $k => $v)
551
        {
552
            // 过滤自增主键
553
            // if ($v['Key'] != 'PRI')
554
            if ($v['Extra'] != 'auto_increment')
555
            {
556
                $fields[] = $v['Field'];
557
            }
558
        }
559
        return $fields;
560
    }
561
562
    public static function table_exists($table_name)
563
    {
564
        $sql = "SHOW TABLES LIKE '" . $table_name . "'";
565
        $rsid = self::query($sql);
566
        $table = self::fetch($rsid);
567
        if (empty($table)) 
568
        {
569
            return false;
570
        }
571
        return true;
572
    }
573
}
574
575
576
577
578
579
580