让随手写的论坛用上 SQLite

Author Avatar
学神之女 1 月 12 日

废话连篇

上次暑假我写了个 PHP 论坛,嗯,随手写的 。现在改成了 SQLite3,可以看看这个 版本发布

实际上不止是核心数据库变化,我还把配置文件单放在了properties.php里,很多变量的命名也有变化,加入了安装和后台(后台是个残废),包括 api.php 的返回函数页变得更加的合理了。(简直是个体力活)

不过我不想说那么多,所以本文就是关于我让基于 JSON 数据库的自制论坛用上 SQLite 作为数据库的过程,其他内容可以自己查阅 推送产生的修改 ,同样,博主是个语文渣,文笔不好,本文会很烂。

小声说下为什么我当初写这个论坛只用了不到一星期而改论坛却用了一个学期

技术细节

首先,SQLite 代码的执行需要用 SQL 指令,但本人不会,就借助 这个库 (找不到哪个库了,去我的 GitHub 库找代码复制粘贴吧,下面的 bug 都修好了)的代码实现了 SQL 指令生成。

并不是拿来就用的,这个生成器的 addQuotes 函数的 BUG 很多。这个函数极为虐人,它是很多 SyntaxError 的源头,不过只要加上一下代码,就可以避免很多 SyntaxError:

if( is_bool($el) ) $el = $el?'1':'0';//不能用true,false,会制造更多bug
if( is_null($el) ) $el = 'NULL';

然后,老样子,自己封装了一个 class 来连接数据库,不过 SQLite3 需要 new,所以就没有直接扩展,而选择了静态:

class Database{
    //还是静态好,不需要new
    public static $sqlite;
    public static function launch($file){
        self::$sqlite = new SQLite3($file);
    }
    public static function query($sql,$default=null){
        //单个查询,直接返回第一个
        $ret = self::$sqlite->query($sql);
        return $ret?$ret->fetchArray():$default;
    }
    public static function multiple_query($sql,$default=null){
        //多个查询
        $ret = self::$sqlite->query($sql);
        if(!$ret) return $default;
        $rows = [];
        while($row = $ret->fetchArray()) array_push($rows,$row);
        return $rows;
    }
    public static function execute($sql){
        return self::$sqlite->exec($sql);
    }
    public static function shutdown(){
        self::$sqlite->close();
    }
    /*兼容部分旧版函数,因为全用新函数太麻烦了*/
    private static function addQuotes($el) {
        if( is_array($el) ) {
            $el = array_map(array($this, __FUNCTION__), $el);
        }
        if( is_string($el) ) {
            $el = '\'' . SQLite3::escapeString($el) . '\'';
        }
        if( is_bool($el) ){
            $el = $el?'1':'0';
        }
        if( is_null($el) ){
            $el = 'NULL';
        }
        return $el;
    }
    public static function select($from,$key=null,$value=null){
        if(empty($from)) return null;
        if(empty($key)) return self::$sqlite->query("select * from ".$from)->fetchArray();
        $ret = self::$sqlite->query("select * from ".$from." where ".$key."=".self::addQuotes($value));
        if(!$ret) return null;
        $rows = [];
        while($row = $ret->fetchArray()) array_push($rows,$row);
        return $rows;
    }
    public static function insert($into,$column){
        if(empty($into)||empty($column)) return false;
        $status = self::$sqlite->exec((new SQL)->insert([$column])->into($into)->getSql());
        return $status?$column:false;
    }
    public static function update(){
        /**
         * 我也不会实现,算了
         */
    }
    public static function delete(){
        /**
         * 本来就没实现,这里更不想实现了
         */
    }
}

除此外,已经写好的查询语句还要做一下改动:

<?php
//SQL指令: select * from table where column = value;
Database::select('table','column','value');//原写法,可保留
Database::query((new SQL)->select('*')->from('table')->where('column=','value')->getSql());//也可以是multiple_query
//SQL指令: insert into table (first_column, second_column) VALUES ('value a','value b');
Database::insert('table',['first_column'=>'value a','second_column'=>'value_b']);//原写法,可保留,注意数组要转换
Database::execute((new SQL)->insert([['first_column'=>'value a','second_column'=>'value b']])->into('table')->getSql());
//SQL指令: UPDATE table set another_column=new_value where column=value;
$new_column['another_column']='new_value';
Database::update('table','column','value',$new_column);//原写法,很复杂
Database::execute((new SQL)->update('table')->where('column=','value')->set('another_column','new_value')->getSql());
//别的指令旧版就没实现过

目前还不安全,因为有时候一些危险数据可以造成 SQL 注入,如这样提供用户名:恒河猴恍恍惚惚'; DELETE FROM table; #

百度了一圈也没有好的解决方案,所以在 PHP 官网上找到了这样的转义方法解决:

<?php echo SQLite3::escapeString('恒河猴恍恍惚惚\'; DELETE FROM table; #');//输出: 恒河猴恍恍惚惚''; DELETE FROM table; #

综上所述这简单的改动还不算难,但都浪费了我很长时间去找,所以说……去年写代码至今。(其实跨年时我在写作业)

    学神之女
    学神之女   2019-01-17, 22:51

    看上去文章有点草,以后再改进吧。