sbqwg

记录一下twcms2.03使用php7.4解决方案

第一步:修改服务器上自己网站的配置文件对应的php版本为7.0以上。
php 7.4
第二步:修改db_mysqli.class.php 文件如下,路径:网站根目录/twcms/kongphp/db/。

<?php

defined('KONG_PATH') || exit;

class db_mysqli implements db_interface {

    private $conf;

    public $tablepre; // 数据表前缀

    //private $wlink; // 写(主)数据库

    //private $rlink; // 读(从)数据库

    //private $xlink; // 分发数据库


    public function __construct(&$conf) {

        $this->conf = &$conf;

        $this->tablepre = $conf['master']['tablepre'];

    }


    /**

     * 创建 MySQL 连接

     * @param string $var 数据库链接名 只能是 wlink rlink xlink

     * @return resource

     */

    public function __get($var) {

        // 主数据库 (写)

        if($var == 'wlink') {

            $cfg = $this->conf['master'];

            empty($cfg['engine']) && $cfg['engine'] = '';

            $this->wlink = $this->connect($cfg['host'], $cfg['user'], $cfg['password'], $cfg['name'], $cfg['charset'], $cfg['engine']);

            return $this->wlink;


            // 从数据库群 (读)

        }elseif($var == 'rlink') {

            if(empty($this->conf['slaves'])) {

                $this->rlink = $this->wlink;

                return $this->rlink;

            }

            $n = rand(0, count($this->conf['slaves']) - 1);

            $cfg = $this->conf['slaves'][$n];

            empty($cfg['engine']) && $cfg['engine'] = '';

            $this->rlink = $this->connect($cfg['host'], $cfg['user'], $cfg['password'], $cfg['name'], $cfg['charset'], $cfg['engine']);

            return $this->rlink;


            // 单点分发数据库 (负责所有表的 maxid count 读写)

        }elseif($var == 'xlink') {

            if(empty($this->conf['arbiter'])) {

                $this->xlink = $this->wlink;

                return $this->xlink;

            }

            $cfg = $this->conf['arbiter'];

            empty($cfg['engine']) && $cfg['engine'] = '';

            $this->xlink = $this->connect($cfg['host'], $cfg['user'], $cfg['password'], $cfg['name'], $cfg['charset'], $cfg['engine']);

            return $this->xlink;

        }

    }


    /**

     * 读取一条数据

     * @param string $key 键名 (高性能需求,键名必须使用索引字段)

     * @return array

     */

    // string

    // in: 'user-uid-2'

    // out: array('uid'=>2, 'username'=>'two')

    public function get($key) {

        list($table, $keyarr, $keystr) = $this->key2arr($key);

        $query = $this->query("SELECT * FROM {$this->tablepre}$table WHERE $keystr LIMIT 1", $this->rlink);

        return mysqli_fetch_assoc($query);

    }


    /**

     * 读取多条数据

     * @param array $keys 键名数组 (高性能需求,键名必须使用索引字段)

     * @return array

     */

    // array

    // in: array(

    // 'article-cid-1-aid-1',

    // 'article-cid-1-aid-2',

    // )

    // out: array(

    // 'article-cid-1-aid-1'=>array('cid'=>1,'cid'=>1, 'title'=>'abc')

    // 'article-cid-1-aid-2'=>array('cid'=>1,'cid'=>2, 'title'=>'bcd')

    // )

    public function multi_get($keys) {

        // 下面这种方式读取比遍历读取效率高

        $sql = '';

        $ret = array();

        foreach($keys as $k) {

            $ret[$k] = array(); // 按原来的顺序赋值,避免后面的 OR 条件取出时顺序混乱

            list($table, $keyarr, $keystr) = $this->key2arr($k);

            $sql .= "$keystr OR ";

        }

        $sql = substr($sql, 0, -4);

        if($sql) {

            $query = $this->query("SELECT * FROM {$this->tablepre}$table WHERE $sql", $this->rlink);

            while($row = mysqli_fetch_assoc($query)) {

                $keyname = $table;

                foreach($keyarr as $k=>$v) {

                    $keyname .= "-$k-".$row[$k];

                }

                $ret[$keyname] = $row;

            }

        }

        return $ret;

    }


    /**

     * 写入一条数据 (包含了 insert 和 update)

     * @param string $key 键名

     * @param array $data 数据

     * @return bool

     */

    public function set($key, $data) {

        if(!is_array($data)) return FALSE;


        list($table, $keyarr) = $this->key2arr($key);

        $data += $keyarr;

        $s = $this->arr2sql($data);


        $exists = $this->get($key);

        if(empty($exists)) {

            return $this->query("INSERT INTO {$this->tablepre}$table SET $s", $this->wlink);

        } else {

            return $this->update($key, $data);

        }

    }


    /**

     * 更新一条数据 (相比 $this->set() 可以修改主键)

     * @param string $key 键名

     * @param array $data 数据

     * @return bool

     */

    public function update($key, $data) {

        list($table, $keyarr, $keystr) = $this->key2arr($key);

        $s = $this->arr2sql($data);

        return $this->query("UPDATE {$this->tablepre}$table SET $s WHERE $keystr LIMIT 1", $this->wlink);

    }


    /**

     * 删除一条数据

     * @param string $key 键名

     * @return bool

     */

    public function delete($key) {

        list($table, $keyarr, $keystr) = $this->key2arr($key);

        return $this->query("DELETE FROM {$this->tablepre}$table WHERE $keystr LIMIT 1", $this->wlink);

    }


    /**

     * 读取/设置 表最大ID

     * @param string $key 键名 只能是表名+一个字段 如:'user-uid'(uid为自增字段)

     * @param boot/int $val 设置值 有三种情况 1.不填为读取(默认) 2.基础上增加 如:'+1' 3.设置指定值

     * @return int

     */

    // maxid('user-uid') 读取 user 表最大 uid

    // maxid('user-uid', '+1') 设置 maxid + 1, 用于占位,保证 key 不会重复

    // maxid('user-uid', 10000) 设置 maxid 为 10000

    public function maxid($key, $val = FALSE) {

        list($table, $col) = explode('-', $key);

        $maxid = $this->table_maxid($key);

        if($val === FALSE) {

            return $maxid;

        }elseif(is_string($val)) {

            $val = max(0, $maxid + intval($val));

        }

        $this->query("UPDATE {$this->tablepre}framework_maxid SET maxid='$val' WHERE name='$table' LIMIT 1", $this->xlink);

        return $val;

    }


    /**

     * 读取表最大ID (如果不存在自动创建表和设置最大ID)

     * @param string $key 键名 只能是表名+一个字段 如:'user-uid'(uid一般为主键)

     * @return int

     */

    public function table_maxid($key) {

        list($table, $col) = explode('-', $key);


        $maxid = FALSE;

        $query = $this->query("SELECT maxid FROM {$this->tablepre}framework_maxid WHERE name='$table' LIMIT 1", $this->xlink, FALSE);


        if($query) {

            $maxid = $this->result($query, 0);

        }elseif(mysqli_errno($this->xlink) == 1146) {

            $sql = "CREATE TABLE `{$this->tablepre}framework_maxid` (";

            $sql .= "`name` char(32) NOT NULL default '',";

            $sql .= "`maxid` int(10) unsigned NOT NULL default '0',";

            $sql .= "PRIMARY KEY (`name`)";

            $sql .= ") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci";

            $this->query($sql, $this->xlink);

        }else{

            throw new Exception('framework_maxid error, mysql_error:'.mysqli_error($this->xlink));

        }

        if($maxid === FALSE) {

            $query = $this->query("SELECT MAX($col) FROM {$this->tablepre}$table", $this->wlink);

            $maxid = $this->result($query, 0);

            $this->query("INSERT INTO {$this->tablepre}framework_maxid SET name='$table', maxid='$maxid'", $this->xlink);

        }

        return $maxid;

    }


    /**

     * 读取/设置 表的总行数

     * @param string $table 表名

     * @param boot/int $val 设置值 有四种情况 1.不填为读取(默认) 2.基础上增加 如:'+1' 3.基础上减少 如:'-1' 4.设置指定值

     * @return int

     */

    public function count($table, $val = FALSE) {

        $count = $this->table_count($table);

        if($val === FALSE) {

            return $count;

        }elseif(is_string($val)) {

            if($val[0] == '+') {

                $val = $count + intval($val);

            }elseif($val[0] == '-') {

                $val = max(0, $count + intval($val));

            }

        }

        $this->query("UPDATE {$this->tablepre}framework_count SET count='$val' WHERE name='$table' LIMIT 1", $this->xlink);

        return $val;

    }


    /**

     * 读取表的总行数 (如果不存在自动创建表和设置总行数)

     * @param string $table 表名

     * @return int

     */

    public function table_count($table) {

        $count = FALSE;

        $query = $this->query("SELECT count FROM {$this->tablepre}framework_count WHERE name='$table' LIMIT 1", $this->xlink, FALSE);


        if($query) {

            $count = $this->result($query, 0);

        }elseif(mysqli_errno($this->xlink) == 1146) {

            $sql = "CREATE TABLE {$this->tablepre}framework_count (";

            $sql .= "`name` char(32) NOT NULL default '',";

            $sql .= "`count` int(10) unsigned NOT NULL default '0',";

            $sql .= "PRIMARY KEY (`name`)";

            $sql .= ") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci";

            $this->query($sql, $this->xlink);

        }else{

            throw new Exception('framework_cout error, mysql_error:'.mysqli_error($this->xlink));

        }

        if($count === FALSE) {

            $query = $this->query("SELECT COUNT(*) FROM {$this->tablepre}$table", $this->wlink);

            $count = $this->result($query, 0);

            $this->query("INSERT INTO {$this->tablepre}framework_count SET name='$table', count='$count'", $this->xlink);

        }

        return $count;

    }


    /**

     * 清空表

     * @param string $table 表名 (表不存在会报错,但无关紧要)

     * @return int

     */

    public function truncate($table) {

        try {

            $this->query("TRUNCATE {$this->tablepre}$table");

            return TRUE;

        } catch(Exception $e) {

            return FALSE;

        }

    }


    /**

     * 根据条件读取数据 (返回数组)

     * @param string $table 表名

     * @param array $pri 主键

     * @param array $where 条件

     * @param array $order 排序

     * @param int $start 开始位置

     * @param int $limit 读取几条

     * @return array

     */

    // in:

    // find_fetch('user', 'uid', array('uid'=> 100), array('uid'=>1), 0, 10);

    // find_fetch('user', 'uid', array('uid'=> array('>'=>'100', '<'=>'200')), array('uid'=>1), 0, 10);

    // find_fetch('user', 'uid', array('username'=> array('LIKE'=>'abc'), array('uid'=>1), 0, 10);


    // out:

    // array(

    // 'user-uid-1'=>array('uid'=>1, 'username'=>'zhangsan'),

    // 'user-uid-2'=>array('uid'=>2, 'username'=>'lisi'),

    // 'user-uid-3'=>array('uid'=>3, 'username'=>'wangwu'),

    // )

    public function find_fetch($table, $pri, $where = array(), $order = array(), $start = 0, $limit = 0) {

        $key_arr = $this->find_fetch_key($table, $pri, $where, $order, $start, $limit);

        if(empty($key_arr)) return array();

        return $this->multi_get($key_arr);

    }


    /**

     * 根据条件返回 key 数组

     * @param string $table 表名

     * @param array $pri 主键

     * @param array $where 条件

     * @param array $order 排序

     * @param int $start 开始位置

     * @param int $limit 读取几条

     * @return array

     */

    // out:

    // array (

    // 'user-uid-1',

    // 'user-uid-2',

    // 'user-uid-3',

    // )

    public function find_fetch_key($table, $pri, $where = array(), $order = array(), $start = 0, $limit = 0) {

        $pris = implode(',', $pri);

        $s = "SELECT $pris FROM {$this->tablepre}$table";

        $s .= $this->arr2where($where);

        if(!empty($order)) {

            $s .= ' ORDER BY ';

            $comma = '';

            foreach($order as $k=>$v) {

                $s .= $comma."$k ".($v == 1 ? ' ASC ' : ' DESC ');

                $comma = ',';

            }

        }

        $s .= ($limit ? " LIMIT $start,$limit" : '');


        $ret = array();

        $query = $this->query($s, $this->rlink);

        while($row = mysqli_fetch_assoc($query)) {

            $keystr = '';

            foreach($pri as $k) {

                $keystr .= "-$k-".$row[$k];

            }

            $ret[] = $table.$keystr;

        }

        return $ret;

    }


    /**

     * 根据条件批量更新数据

     * @param string $table 表名

     * @param array $where 条件

     * @param array $lowprority 是否开启不锁定表

     * @return int 返回影响的记录行数

     */

    public function find_update($table, $where, $data, $lowprority = FALSE) {

        $where = $this->arr2where($where);

        $data = $this->arr2sql($data);

        $lpy = $lowprority ? 'LOW_PRIORITY' : '';

        $this->query("UPDATE $lpy {$this->tablepre}$table SET $data $where", $this->wlink);

        return mysqli_affected_rows($this->wlink);

    }


    /**

     * 根据条件批量删除数据

     * @param string $table 表名

     * @param array $where 条件

     * @param array $lowprority 是否开启不锁定表

     * @return int 返回影响的记录行数

     */

    public function find_delete($table, $where, $lowprority = FALSE) {

        $where = $this->arr2where($where);

        $lpy = $lowprority ? 'LOW_PRIORITY' : '';

        $this->query("DELETE $lpy FROM {$this->tablepre}$table $where", $this->wlink);

        return mysqli_affected_rows($this->wlink);

    }


    /**

     * 准确获取最大ID

     * @param string $key 键名

     * @return int

     */

    public function find_maxid($key) {

        list($table, $maxid) = explode('-', $key);

        $arr = $this->fetch_first("SELECT MAX($maxid) AS num FROM {$this->tablepre}$table");

        return isset($arr['num']) ? intval($arr['num']) : 0;

    }


    /**

     * 准确获取总条数

     * @param string $table 表名

     * @param array $where 条件

     * @return int

     */

    public function find_count($table, $where = array()) {

        $where = $this->arr2where($where);

        $arr = $this->fetch_first("SELECT COUNT(*) AS num FROM {$this->tablepre}$table $where");

        return isset($arr['num']) ? intval($arr['num']) : 0;

    }


    /**

     * 创建索引

     * @param string $table 表名

     * @param array $index 键名数组 // array('uid'=>1, 'dateline'=>-1, 'unique'=>TRUE, 'dropDups'=>TRUE) 为了配合 mongodb 的索引才这样设计的

     * @return boot

     */

    public function index_create($table, $index) {

        $keys = implode(',', array_keys($index));

        $keyname = implode('_', array_keys($index));

        return $this->query("ALTER TABLE {$this->tablepre}$table ADD INDEX $keyname($keys)", $this->wlink);

    }


    /**

     * 删除索引

     * @param string $table 表名

     * @param array $index 键名数组

     * @return boot

     */

    public function index_drop($table, $index) {

        $keys = implode(',', array_keys($index));

        $keyname = implode('_', array_keys($index));

        return $this->query("ALTER TABLE {$this->tablepre}$table DROP INDEX $keyname", $this->wlink);

    }


    // +------------------------------------------------------------------------------

    // | 以下是公共方法,但不推荐外部使用

    // +------------------------------------------------------------------------------

    /**

     * 连接 MySQL 服务器

     * @param string $host 主机

     * @param string $user 用户名

     * @param string $pass 密码

     * @param string $name 数据库名称

     * @param string $charset 字符集

     * @param string $engine 数据库引擎

     * @return resource

     */

    public function connect($host, $user, $pass, $name, $charset = 'utf8', $engine = '') {

        $link = mysqli_connect($host, $user, $pass,$name);

        if(!$link) {

            throw new Exception(mysqli_error($link));

        }

        $result = mysqli_select_db($link,$name);

        if(!$result) {

            throw new Exception(mysqli_error($link));

        }

        if(!empty($engine) && $engine == 'InnoDB') {

            $this->query("SET innodb_flush_log_at_trx_commit=no", $link);

        }


        // 不考虑 mysql 5.0.1 下以版本

        $this->query("SET character_set_connection=$charset, character_set_results=$charset, character_set_client=binary, sql_mode=''", $link);

        //$this->query("SET names utf8, sql_mode=''", $link);

        return $link;

    }


    /**

     * 发送一条 MySQL 查询

     * @param string $sql SQL 语句

     * @param string $link 打开的连接

     * @param boot $isthrow 错误时是否抛

     * @return resource

     */

    public function query($sql, $link = NULL, $isthrow = TRUE) {

        empty($link) && $link = $this->wlink;


        if(defined('DEBUG') && DEBUG && isset($_ENV['_sqls']) && count($_ENV['_sqls']) < 1000) {

            $start = microtime(1);

            $result = mysqli_query( $link,$sql);

            $runtime = number_format(microtime(1) - $start, 4);


            // explain 分析 select 语句

            $explain_str = '';

            if(substr($sql, 0, 6) == 'SELECT') {

                $query = mysqli_query($link,"explain $sql");

                if($query !== FALSE) {

                    $explain_arr = mysqli_fetch_assoc($query);

                    //print_r($explain_arr);

                    $explain_str = ' <font color="blue">[explain type: '.$explain_arr['type'].' | rows: '.$explain_arr['rows'].']</font>';

                }

            }

            $_ENV['_sqls'][] = ' <font color="red">[time:'.$runtime.'s]</font> '.htmlspecialchars(stripslashes($sql)).$explain_str;

        }else{

            $result = mysqli_query($link,$sql);

        }


        if(!$result && $isthrow) {

            $s = 'MySQL Query Error: <b>'.$sql.'</b>. '.mysqli_error($link);


            if(defined('DEBUG') && !DEBUG) $s = str_replace($this->tablepre, '***', $s); // 防止泄露敏感信息


            throw new Exception($s);

        }

        $_ENV['_sqlnum']++;

        return $result;

    }


    /**

     * 获取第一条数据

     * @param string $sql SQL 语句

     * @param string $link 打开的连接

     * @return array

     */

    public function fetch_first($sql, $link = NULL) {

        empty($link) && $link = $this->rlink;

        $query = $this->query($sql, $link);

        return mysqli_fetch_assoc($query);

    }


    /**

     * 获取多条数据 (特殊情况会用到)

     * @param string $sql SQL 语句

     * @param string $link 打开的连接

     * @return array

     */

    public function fetch_all($sql, $link = NULL) {

        empty($link) && $link = $this->rlink;

        $query = $this->query($sql, $link);

        $ret = array();

        while($row = mysqli_fetch_assoc($query)) {

            $ret[] = $row;

        }

        return $ret;

    }


    /**

     * 获取结果数据

     * @param resource $query 查询结果集

     * @param int $row 第几列

     * @return int

     */

    public function result($query, $row) {

        //~ return mysqli_num_rows($query) ? intval(mysqli_data_seek($query, $row)) : FALSE;

        if (!mysqli_num_rows($query)) return FALSE;

        mysqli_data_seek($query, $row);

        $rowdata = mysqli_fetch_row($query);

        return $rowdata[$row];

    }


    /**

     * 获取 mysql 版本

     * @return string

     */

    public function version() {

        return mysqli_get_server_info($this->rlink);

    }


    /**

     * 关闭读写数据库连接

     */

    public function __destruct() {

        if(!empty($this->wlink)) {

            mysqli_close($this->wlink);

        }

        if(!empty($this->rlink) && !empty($this->wlink) && $this->rlink != $this->wlink) {

            mysqli_close($this->rlink);

        }

    }


    /**

     * 将数组转换为 where 语句

     * @param array $arr 数组

     * @return string

     * in: array('id'=> array('>'=>'10', '<'=>'200'))

     * out: WHERE id>'10' AND id<'200'

     * 支持: '>=', '<=', '>', '<', 'LIKE', 'IN' (尽量少用,能不用则不用。'LIKE' 会导致全表扫描,大数据时不要使用)

     * 注意1: 为考虑多种数据库兼容和性能问题,其他表达式不要使用,如:!= 会导致全表扫描

     * 注意2: 高性能准则要让SQL走索引,保证查询至少达到range级别

     */

    private function arr2where($arr) {

        $s = '';

        if(!empty($arr)) {

            foreach($arr as $key=>$val) {

                if(is_array($val)) {

                    foreach($val as $k=>$v) {

                        if(is_array($v)) {

                            if($k === 'IN' && $v) {

                                foreach($v as $i) {

                                    $i = addslashes($i);

                                    $s .= "$key='$i' OR "; // 走索引时,OR 比 IN 快

                                }

                                $s = substr($s, 0, -4).' AND ';

                            }

                        }else{

                            $v = addslashes($v);

                            if($k === 'LIKE') {

                                $s .= "$key LIKE '%$v%' AND ";

                            }else{

                                $s .= "$key$k'$v' AND ";

                            }

                        }

                    }

                }else{

                    $val = addslashes($val);

                    $s .= "$key='$val' AND ";

                }

            }

            $s && $s = ' WHERE '.substr($s, 0, -5);

        }

        return $s;

    }


    /**

     * 将数组转换为SQL语句

     * @param array $arr 数组

     * @return string

     * in: array('cid'=>1, 'aid'=>2)

     * out: cid='1',aid='2'

     */

    private function arr2sql($arr) {

        $s = '';

        foreach($arr as $k=>$v) {

            $v = addslashes($v);

            $s .= "$k='$v',";

        }

        return rtrim($s, ',');

    }


    /**

     * 将键名转换为数组

     * @param string $key 键名

     * @return array

     * in: article-cid-1-aid-2

     * out: array('article', array('cid'=>1, 'aid'=>2), 'cid=1 AND aid=2')

     */

    private function key2arr($key) {

        $arr = explode('-', $key);


        if(empty($arr[0])) {

            throw new Exception('table name is empty.');

        }


        $table = $arr[0];

        $keyarr = array();

        $keystr = '';

        $len = count($arr);

        for($i = 1; $i < $len; $i = $i + 2) {

            if(isset($arr[$i + 1])) {

                $v = $arr[$i + 1];

                $keyarr[$arr[$i]] = is_numeric($v) ? intval($v) : $v; // 因为 mongodb 区分数字和字符串


                $keystr .= ($keystr ? ' AND ' : '').$arr[$i]."='".addslashes($v)."'";

            } else {

                $keyarr[$arr[$i]] = NULL;

            }

        }


        if(empty($keystr)) {

            throw new Exception('keystr name is empty.');

        }

        return array($table, $keyarr, $keystr);

    }

}

?>







第三步:修改网站的配置文件 mysqli ,/网站根目录/twcms/config/config.inc.php

// 数据库配置,type 为默认的数据库类型,可以支持多种数据库: mysql|pdo_mysql|pdo_sqlite|postgresql|mongodb

'db' => array(

'type' => 'mysqli',

// 主数据库

'master' => array(

'host' => 'localhost',


#1楼
发帖时间:2021-11-14   |   查看数:0   |   回复数:3
bjwindy
你这步骤很多人操作不了,只把数据库连接方式改成mysqli就可以使用7.0以上版本的php了。可以参考下面的帖子
http://www.twcms.com/bbs/thread-index-fid-44-tid-1019-page-1.htm
2022-2-21 #2楼
skywhales2
非常好,必须赞
2022-10-24 #3楼
lxz168
撸金币回复
2023-1-1 #4楼
游客组