自上次新手村外白如意被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/
少侠有缘再见还请留赞