php新特性与mysqli融合轻松打造数据库插入类

761 阅读5分钟
原文链接: mp.weixin.qq.com

自上次新手村外白如意被mysql狠狠教训,内心一想起数据库竟有点发怵。只听信无望山php掌门之意,潜心修炼内功心法,数日后顿悟原力。

——《编程话江湖,原力铸神兵》

php5.6引入前置... 操作符,使得函数参数个数具有弹性

myfun(...$p);myfun($p[0],$p[1],$p[2],...,$p[n]);

mysql4.1引入预处理语句,为执行做好准备,带来了占位符?,也能有效防止SQL注入攻击。现在有一个Student表,在数据中查看表结构

DESC Student;+----------+-------------+------+-----+---------+----------------+| Field    | Type        | Null | Key | Default | Extra          |+----------+-------------+------+-----+---------+----------------+| id       | int(11)     | NO   | PRI | NULL    | auto_increment || name     | varchar(50) | YES  |     | NULL    |                || age      | int(11)     | YES  |     | NULL    |                || sex      | int(11)     | YES  |     | NULL    |                || grade    | varchar(50) | YES  |     | NULL    |                || add_time | time        | YES  |     | NULL    |                |+----------+-------------+------+-----+---------+----------------+6 rows in set

主键为id整型自动+1,其中姓名name 和级别grade为字符串类型。让我们看看插入语句的写法

/* 常规写法 */INSERT INTO Student(name,grade) VALUES('白如易','新手村一级');INSERT INTO Student(name,age,grade) VALUES('白如易',18,'新手村一级');/* 预处理语句写法,用?占位 并未执行插入 */INSERT INTO Student(name,grade) VALUES(?,?);INSERT INTO Student(name,age,grade) VALUES(?,?,?);INSERT INTO Student(name,age,sex,grade,add_time) VALUES(?,?,?,?,?);

注意,字段个数增加,相应的参数也在增加,后面将运用这一点

php通过mysqli扩展实现预处理语句,也是执行插入语句

//$mysql为数据库句柄$stmt = $mysqli->stmt_init();  $sql = "INSERT INTO Student(name,grade) VALUES(?,?);";$stmt->prepare($sql);$stmt->bind_param('is',$name,$grade);$result = $stmt->execute();

用?进行了预处理语句占了坑,后面需要绑定相应的数据,并在这之前指明数据类型,php支持提供i、d、s、b四种对应参数类型,这四个类型是从php的角度看待的

<?phpi-对应变量为整数类型d-对应变量为浮点数类型s-对应变量为字符串类型b-对应变量为二进制大文件类型且被打包

当我们需要插入需要增加一个age,上方的代码必须人为修改

<?php$sql = "INSERT INTO Student(name,grade,age) VALUES(?,?,?);";$stmt->prepare($sql);$stmt->bind_param('isi',$name,$grade,$age);

当插入字段有多少,代码就会有多长。

很快,白如易从中发现了要害——

白如易悟到,对于任何一张邪恶的mysql数据表,它的表字段和结构可以认为是不变的。当我们插入数据时,只须数据表已存在字段相同则切中要害,字段变则参数变,字段长则参数长,步调一致,万变不离其宗。

总体脉络

<?phpclass myDB{    public $tableName = '';    public function __construct($tableName){        $this->tableName = $tableName;    }    public function db(){        //获取数据库句柄    }    public function add($data){        //插入数据    }}

第一步,获取数据库操作句柄变量

php中的mysqli扩展提供连接数据库功能,有面向过程和面向对象两种写法,更推荐使用面向对象的写法,变量自定义命名为$mysqli

<?phppublic function db(){    $config = array(        'host'=>'127.0.0.1',        'user'=>'root',        'password'=>'root',        'database'=>'test',        'port'=>3306,    );    $set = array_values($config);    $mysqli = new mysqli(...$set);    $query = 'SET NAMES UTF8';    $mysqli->query($query);    if($mysqli->errno){        printf("连接数据库错误<br/> %s",$mysqli->error);        exit;    }    return $mysqli;}

第二步,创建添加方法

自定义名称为add。根据输入数据,从中获取能够进入数据库的有效数据,并且根据有效数据为其组织相应的参数类型列表、和组装预处理SQL。

<?phppublic function add($data){    $mysqli = $this->db();    $tableName = $this->tableName;    //1-获取预处理SQL、参数类型列表和有效数据    //$sql、$typeList、$needData    $dataArr = $this->filterFromTable($tableName,$data);    extract($dataArr);    //2-填入预处理SQL、参数类型列表和有效数据    $stmt = $mysqli->stmt_init();      $stmt->prepare($sql);    $stmt->bind_param($typeList,...$needData);    //TO DO 3-执行插入    return $insert_id;}    

其中,$dataArr 数组包含三个元素:

<?php/* * $sql - String类型。 INSERT INTO 表(字段1,...,字段N) VALUES(?,...?); * $typeList - String类型。i、d、s、b组合的字符串 * $needData - Array类型。为数组下标的参数数组 */$dataArr = array(    'sql'=>$sql,    'typeList'=>$typeList,    'needData'=>$needData,);

获取上面三个元素的函数我们命名为filterFromTable()。这个函数肩负起上方所有的职责,首先要获取数据库表字段列表和其i、d、s、b类型,接着循环数据库列表字段或插入数据,比对找出其中登记在库的参数重组数据,并且在循环时可顺便拼接 好SQL语句的字段列表和问号列表

<?phppublic function filterFromTable($tableName,$data){    $sqlField = '';//String类型,例如 name,age,grade    $sqlQ = '';//String类型,例如 ?,?,?    $typeList = '';    $needData = array();    //根据表结构获取字段类型列表    $fieldTypeArr = $this->fieldTypeArr($tableName);    //第一种、循环Table存在字段    foreach($fieldTypeArr as $field=>$type){        $param = @$data[$field]?:'编程夜未眠';//传入参数存在该字段        if($param != '编程夜未眠'){            $sqlField .= $field.',';            $sqlQ .= '?,';            $typeList .= $type;            $needData[] = $param;        }    }    $sqlField = substr( $sqlField,0,strlen($sqlField)-1 );     $sqlQ = substr( $sqlQ,0,strlen($sqlQ)-1 );     $sql = "INSERT INTO {$tableName}({$sqlField})     VALUES({$sqlQ})";    $dataArr = array(        'sql'=>$sql,        'typeList'=>$typeList,        'needData'=>$needData,    );    return $dataArr;}

其中,fieldTypeArr()用来获取数据库字段对应i、d、s、b类型,需要根据mysql字段类型做一个转换,该函数返回内容为

//print_r(fieldTypeArr($tableName));Array(    [id] => i    [name] => s    [age] => i    [sex] => i    [grade] => s    [add_time] => s)

上述函数根据mysql字段类型,转换为i、d、s、b类型用到的自定义函数oneFieldType

<?phppublic function oneFieldType($type){    $str = '';    if(strstr($type,'int')){        $str = 'i';    }else if( strstr($type,'float') || strstr($type,'decimal') || strstr($type,'double') ){        $str = 'd';    }else if( strstr($type,'blob') ){        $str = 'b';    }else{        $str = 's';    }    return $str;}

第三步,测试

以Student表为例引入myDB.class.php类,将适用于任何表的插入操作

<?php    include 'myDB.class.php';    $Student = new myDB('Student');    $data = array(        'name'=>'白如意',        'grade'=>'新手村一级',        'girlfriend'=>'不存在数据库',        'age'=>18    );    $Student->add($data);

执行过程的中间返回值

Array(    [sql] => INSERT INTO Student(name,age,grade)         VALUES(?,?,?)    [typeList] => sis    [needData] => Array        (            [0] => 白如意            [1] => 18            [2] => 新手村一级        ))

函数调用关系

完整的代码

包括注释和辅助函数,共计107行

<?phpclass myDB{    public $tableName = '';    public function __construct($tableName){        $this->tableName = $tableName;    }    public function db(){        $config = array(            'host'=>'127.0.0.1',            'user'=>'root',            'password'=>'root',            'database'=>'test',            'port'=>3306,        );        $set = array_values($config);        $mysqli = new mysqli(...$set);        $query = 'SET NAMES UTF8';        $mysqli->query($query);        if($mysqli->errno){            printf("连接数据库错误<br/> %s",$mysqli->error);            exit;        }        return $mysqli;    }    public function add($data){        $mysqli = $this->db();        $tableName = $this->tableName;        $dataArr = $this->filterFromTable($tableName,$data);        $this->co($dataArr);        //获得$sql ,$typeList,$needData        extract($dataArr);        $stmt = $mysqli->stmt_init();          $stmt->prepare($sql);        $stmt->bind_param($typeList,...$needData);        if ( $result = $stmt->execute() ){             $insert_id = $stmt->insert_id;            echo "成功插入ID".$insert_id;        }else {              echo "执行失败".$stmt->errno;            echo '</br>';            echo $stmt->error;              $insert_id = 0;        }        return $insert_id;    }       public function filterFromTable($tableName,$data){        $sqlField = '';//字段:  a,b,c        $sqlQ = '';//问号:  ?,?,?        $typeList = '';//字段的类型: ssdib         $needData = array();        //根据表结构获取字段类型列表        $fieldTypeArr = $this->fieldTypeArr($tableName);        $this->co($fieldTypeArr);    //第一种、循环Table存在字段        foreach($fieldTypeArr as $field=>$type){            $param = @$data[$field]?:'编程夜未眠';//传入参数存在该字段            if($param != '编程夜未眠'){                $sqlField .= $field.',';                $sqlQ .= '?,';                $typeList .= $type;                $needData[] = $param;            }        }        $sqlField = substr( $sqlField,0,strlen($sqlField)-1 );         $sqlQ = substr( $sqlQ,0,strlen($sqlQ)-1 );         $sql = "INSERT INTO {$tableName}({$sqlField})         VALUES({$sqlQ})";        $dataArr = array(            'sql'=>$sql,            'typeList'=>$typeList,            'needData'=>$needData,        );        return $dataArr;    }    public function fieldTypeArr($tableName){        $arr= array();        $mysqli = $this->db();        $sql = "DESC {$tableName}";        $result_obj = $mysqli->query($sql);        while($row = $result_obj->fetch_object() ){            $type = $row->Type;            $field= $row->Field;            $str = $this->oneFieldType($type);            $arr[$field] = $str;        }        $mysqli->close();        return $arr;    }    public function oneFieldType($type){        $str = '';        if(strstr($type,'int')){            $str = 'i';        }else if( strstr($type,'float') || strstr($type,'decimal') || strstr($type,'double')   ){            $str = 'd';        }else if( strstr($type,'blob') ){            $str = 'b';        }else{            $str = 's';        }        return $str;    }    public function co($value){        echo '<pre>';        print_r($value);        echo '</pre>';    }}//End Class

这就实现了插入操作,其他数据库操作应该可以举一反三

一门语言的原力来源于自身,这样代码可能会有灵魂

封面大剑图来源于网络,再次感谢微信代码高亮工具md2all让代码可读性更好,本次使用风格atom-one-light

http://www.win4000.com/wallpaper_detail_133540_8.html

http://md.aclickall.com/


少侠有缘再见还请留赞