PHP Mysql
一、连接Mysql
PHP八种数据类型,已经学了六种,还有资源型和对象没学
resource mysql_connect( [ string $server [,string $username [,string $password [,bool $new_link]]]] );
第四个参数$new_link是否重新链接
$con = mysql_connect('localhost', 'root', ''); if($con){ print_r($con); // Resource id #3 }else{ echo '没有链接上'; }
返回值是Resource id #3,Resource就是资源的意思
第一次链接$con,第二次$other又再一次链接上了服务器,两次返回的都是"Resource id #3",说明$con和$other两个链接都是一个资源,
因为链接是很耗费服务器资源的,出于减少链接的目的,对于同样的参数的mysql_connect()函数调用返回的是一个资源。
$other = mysql_connect('localhost', 'root', ''); if($other){ print_r($other); // Resource id #3 }else{ echo '没有链接上'; } $other = mysql_connect('localhost', 'root', ''); if($other){ print_r($other); // Resource id #3 }else{ echo '没有链接上'; }
如果非要链接2次,得到不同的资源,要用第四个参数$new_link是否重新链接为true
强制重新链接,打印链接返回一个新资源Resource id #5
$con = mysql_connect('localhost', 'root', '', true); if($con){ print_r($con); // Resource id #3 }else{ echo '没有链接上'; } $other = mysql_connect('localhost', 'root', '', true); if($other){ print_r($other); // Resource id #5 }else{ echo '没有链接上'; }
mysql默认占用的是3306
mysql对外链接,和apache一样需要占一个端口,端口就是商场的柜台号,你想为客户服务,就得租个柜台
mysql默认占用的是3306,mysql_connect没有指定端口,PHP会默认用3306来链接。
$con = mysql_connect('localhost:3306', 'root', '', true); if($con){ print_r($con); // Resource id #3 }else{ echo '没有链接上'; }
如果端口是3307 mysql_connect( 'localhost:3307', 'root', '', true ) 可以这样来指定端口
在PHP中还可以用mysqli和PDO来链接mysql服务器
mysql_connect系列函数,是面向过程的写法
mysqli是把链接mysql的功能封装成类,是面相对象的写法
PDO是一个统一的数据库接口,屏蔽了数据库之间的不一致,无论什么数据库用PDO的写法都一致,PDO也是面相对象的用法。
二、资源的概念及发送sql查询
创建数据库db_orange,数据表regist,为下面学习做准备
create database db_orange charset utf8; create table regist( id int(8) unsigned primary key auto_increment, name varchar(100) not null default '', age varchar(10) not null default '' )engine=myisam default charset=utf8;
1、Resource资源型
链接上mysql服务器后,遇到一个新的概念叫Resource是资源型
资源型的变量是什么?
资源型非常容易理解,资源就是一个管道。
以PHP链接Mysql为例,PHP客户端和mysql服务器之间建立起了链接,这个链接就是资源型,资源型就是通道,PHP操作Mysql沿着通道走。
一旦链接mysql之后,既通道形成,我们就可以沿着通道发送sql语句了。
2、发送添加语句
连上mysql之后,只要是合法的sql语句都可以通过通道发送给mysql服务器,具体用 mysql_query() 函数发送sql语句
1. mysql_query( 'use db_orange', '资源' ) 选则数据库,返回一个布尔值
2. mysql_query( 'set names utf8', '资源 ') 设置字符集
3. mysql_query( $sql, '资源' ) 增加一条数据,返回一个布尔值
$con = mysql_connect('localhost:3306', 'root', ''); if(!$con){ echo '链接失败'; exit; } $rs = mysql_query('use db_orange', $con); // var_dump($rs); // bool(true) mysql_query('set names utf8', $con); $sql = "insert into regist (id, name)values('', '".rand(1, 100).time()."')"; if(mysql_query($sql, $con)){ echo '添加数据成功'.mysql_insert_id(); }else{ echo '添加失败'; }
3、删除数据
从地址栏上接收一个id index.php?id=1
$con = mysql_connect('localhost:3306', 'root', ''); if(!$con){ echo '链接失败'; exit; } $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); $id = isset($_GET['id']) ? $_GET['id']: ''; $sql = "delete from regist where id = $id"; if($id){ $rs = mysql_query($sql, $con); // 发送sql语句,沿着con通道进行 echo '删除成功'; }else{ echo '没有传id'; }
4、一个非常简单sql注入
1. php?id=16 or 1
2. sql语句是 delete from regist where id = 9 or 1
9 or 1 1恒为真,所有行都满足条件,都被删除了
3. sql语句变成 delete from regist where id = true
因此所有的行都满足条件,都被删除了
4. delete from regist where id = 33 or 0 就不会出现全部删除的情况
$con = mysql_connect('localhost:3306', 'root', ''); if(!$con){ echo '链接失败'; exit; } $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); $id = isset($_GET['id'])?$_GET['id']:''; $sql = "delete from regist where id = $id"; if(isset($_GET['id'])){ $rs = mysql_query($sql, $con); echo $sql; }
5、处理sql注入
合法的id应该是整形,不应该是字符串,因此处理也比较简单
字符串直接加0变成整形了,字符串加0变成整形了,就不会再有注入的功能了
$id = isset($_GET['id']) ? $_GET['id']+0 : '';
$con = mysql_connect('localhost:3306', 'root', ''); if(!$con){ echo '链接失败'; exit; } $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); $id = isset($_GET['id']) ? $_GET['id']+0 : ''; $sql = "delete from regist where id = $id"; if($id){ $rs = mysql_query($sql, $con); echo '删除成功'; }else{ echo '没有传id'; }
用intval()函数也可以处理这个注入问题
$con = mysql_connect('localhost:3306', 'root', ''); if(!$con){ echo '链接失败'; exit; } $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); $id = isset($_GET['id']) ? intval($_GET['id']) : ''; // intval函数处理$_GET['id'] echo $sql = "delete from regist where id = $id"; if($id){ $rs = mysql_query($sql, $con); echo '删除成功'; }else{ echo '没有传id'; }
三、修改表数据
update语句
$con = mysql_connect('localhost:3306', 'root', ''); if(!$con){ echo '链接失败'; exit; } mysql_query('set names utf8', $con); // 选择数据库db_orange,$con是资源(通道),沿着通道把sql语句发送给mysql服务器 // 返回bool值,这里还可以进行判断 $rs = mysql_query('use db_orange', $con); if(!$rs){ echo '请检查选择的数据库'; exit; } $sql = "update regist set name = '新名子' where id = 41"; if(mysql_query($sql, $con)){ echo '修改成功:',$sql; }else{ echo '修改失败:',$sql; }
表单提交修改数据,为后面学习表单添加注入语句做准备
<?php $con = mysql_connect('localhost:3306', 'root', ''); $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); if($_POST){ $sql = "update regist set name = '".$_POST['user']."', age = '".$_POST['age']."' where id = '".$_POST['uid']."' "; if(mysql_query($sql, $con)){ echo '修改成功:',$sql; }else{ echo '修改失败:',$sql; } } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>表单提交修改</title> </head> <body> <form method="post" action=""> ID:<input type="text" name="uid" value="" /><br/> 姓名:<input type="text" name="user" value="" /><br/> 年龄:<input type="text" name="age" value="" /><br/> <input type="submit" value="提交" /> </form> </body> </html>
sql注入问题
1. 表单输入 努力同学', age='10000' where id = 5# 5是要修改的id
2. 打印出真正的sql语句,后面有一个#井号 update regist set name = '努力同学', age='90000' where id = 5#', age = '100' where id = '5'
3. 在sql里#号是注释,意思是#号后面的 ', age = '100' where id = '5' 就不发挥作用了
<?php $con = mysql_connect('localhost:3306', 'root', ''); $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); if($_POST){ $sql = "update regist set name = '".$_POST['user']."', age = '".$_POST['age']."' where id = '".$_POST['uid']."'"; if(mysql_query($sql, $con)){ echo '修改成功:',$sql; }else{ echo '修改失败:',$sql; } } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>sql注入问题</title> </head> <body> <form method="post" action=""> ID:<input type="text" name="uid" value="5" /><br/> 姓名:<input type="text" name="user" value="努力同学', age='90000' where id = 5#" /><br/> 年龄:<input type="text" name="age" value="100" /><br/> <input type="submit" value="提交" /> </form> </body> </html>
更严重的sql注入问题
Discuz论坛数据表中有一个groupid列,groupid=1 是超级管理员
1. 给regist表添加一个groupid列 Alter table regist add groupid tinyint unsigned not null default '0';
2. 模拟一下通过注入成为超级管理员,表单输入 ', age='3', groupid=1 where id = 4#
<?php $con = mysql_connect('localhost:3306', 'root', ''); $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); if($_POST){ $sql = "update regist set name = '".$_POST['user']."', age = '".$_POST['age']."' where id = '".$_POST['uid']."'"; if(mysql_query($sql, $con)){ echo '修改成功:',$sql; }else{ echo '修改失败:',$sql; } } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>更严重的sql注入问题</title> </head> <body> <form method="post" action=""> ID:<input type="text" name="uid" value="5" /><br/> 姓名:<input type="text" name="user" value="超级管理员', age='3', groupid=1 where id = 40#" /><br/> 年龄:<input type="text" name="age" value="100" /><br/> <input type="submit" value="提交" /> </form> </body> </html>
提交后,一下子就变成超级管理员了:
修改成功:update regist set name = '超级管理员', age='3', groupid=1 where id = 4#', age = '100' where id = '5'
之所以出现问题是因为,这个单引号被看成sql语句中的一部分了 超级管理员', age='3', groupid=1 where id = 4#
解决这个注入问题
提交表单的时候,难免有真的单引号,把单引号转义成字符串的单引号
用 addslashes() 函数转义一下表单提交的数据 $_POST['name'] = addslashes($_POST['name']);
注入是因为利用传值故意传一些sql语句的关键字,如果单引号、井号等
转义,是把单引号转成\',这样单引号就和普通的字符一样了,没有语法含义了。
<?php $con = mysql_connect('localhost:3306', 'root', ''); $rs = mysql_query('use db_orange', $con); mysql_query('set names utf8', $con); if($_POST){ $_POST['user'] = addslashes($_POST['user']); $_POST['age'] = addslashes($_POST['age']); $_POST['groupid'] = addslashes($_POST['groupid']); $id = $_POST['uid'] + 0; $sql = "update regist set name = '".$_POST['user']."', age = '".$_POST['age']."', groupid = '".$_POST['groupid']."' where id = '".$_POST['uid']."'"; if(mysql_query($sql, $con)){ echo '修改成功:',$sql; }else{ echo '修改失败:',$sql; } } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>转义</title> </head> <body> <form method="post" action=""> ID:<input type="text" name="uid" value="5" /><br/> 姓名:<input type="text" name="user" value="超级管理员', age='3', groupid=1 where id = 4#" /><br/> 年龄:<input type="text" name="age" value="100" /><br/> 管理权限:<input type="text" name="groupid" value="9" /><br/> <input type="submit" value="提交" /> </form> </body> </html>
测试一下,这个注入的问题,用PDO链接数据库的方式也是存在的,最终还要通过转义解决
<?php $con = new PDO("mysql:host=localhost;dbname=db_orange", "root", ""); $con->exec('set names utf8'); if($_POST){ $sql = "update regist set name = '".$_POST['user']."', age = '".$_POST['age']."' where id = '".$_POST['uid']."'"; //$sql = "update regist set name = '".addslashes($_POST['user'])."', age = '".$_POST['age']."' where id = '".$_POST['uid']."'"; if($con->exec($sql)){ echo '修改成功:',$sql; }else{ echo '修改失败:',$sql; } } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>PDO方式下测试注入问题</title> </head> <body> <form method="post" action=""> ID:<input type="text" name="uid" value="5" /><br/> 姓名:<input type="text" name="user" value="同学', age='999', groupid=1 where id = 3#" /><br/> 年龄:<input type="text" name="age" value="100" /><br/> <input type="submit" value="提交" /> </form> </body> </html>
四、查询表数据
对于增删改insert, delete, update语句用法一样,只要把sql语句写好,然后mysql_query( $sql )执行就可以了,返回值只有true/false两种情况
而对应select语句,执行失败了返回false,如果执行成功返回资源型。
故意写错select语句中的数据表名称,返回布尔值false
$conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); $sql = 'select * from stusss'; // 故意写错数据表名 $result = mysql_query($sql, $conn); var_dump($result); // bool(false),返回false
链接成功返回resource资源型
$conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); $sql = 'select * from regist'; $result = mysql_query($sql, $conn); var_dump($result); // resource(4) of type (mysql result),返回资源型
此时的$result是通道,是豆浆的吸管
沿着通道把资源取出来,有三个专门的工具函数(常用的前两个)
mysql_fetch_assoc()
mysql_fetch_row()
mysql_fetch_array()
其实还有一个
mysql_fetch_object()
mysql_fetch_assoc()函数
每执行一次取一条数据,游标向下移动,直到数据取完返回false
$conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); $sql = 'select * from regist'; $result = mysql_query($sql, $conn); // 参数传 资源 print_r(mysql_fetch_assoc($result)); // Array ( [id] => 1 [name] => 同学', age='999', grou [age] => 100 [groupid] => 0 ) print_r(mysql_fetch_assoc($result)); // Array ( [id] => 2 [name] => 员', age='3', groupid [age] => 10000 [groupid] => 10 )
用while循环一次性把数据取出来
$conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); $sql = 'select * from regist'; $result = mysql_query($sql, $conn); while($row = mysql_fetch_assoc($result)){ print_r($row); }
这四条语句之间的区别
mysql_fetch_assoc() 取出来的是关联数组,数组key是表的字段名
mysql_fetch_row() 取出来的是索引数组,索引从0开始依次增加
mysql_fetch_array() 取出的结果又有关联索引,又有索引索引,是前两着的并集
mysql_fetch_object() 返回的是对象,这样取值 $obj->id
$conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); $sql = 'select * from regist'; $result = mysql_query($sql, $conn); print_r(mysql_fetch_assoc($result)); print_r(mysql_fetch_row($result)); print_r(mysql_fetch_array($result)); print_r(mysql_fetch_object($result));
五、PHP操作mysql常用函数
严重注意:
对应insert、detele、update、select的操作,返回值是true/false两种情况,
其中insert操作返回true,代表数据肯定是插入成功了
对于update和detele操作返回true,代表这个sql语句的语法没问题执行成功了
是从语法角度,执行成功了
但是语句未必能影响到数据,比如 where id = 999 条件不对
要想真正判断有没有删/改数据,要看实际影响行数为准。
如何获取受影响行数?
在执行delete或update语句后立即调用 mysql_affected_rows() 函数,可以得到delete/update语句影响的行数。
$conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); $id = isset($_GET['id']) ? $_GET['id'] : 0; $sql = 'delete from regist where id ='. $id; mysql_query($sql, $conn); $cnt = mysql_affected_rows($conn); if($cnt){ echo '删除了'.$cnt.'行数据'; // 删除一条成功后返回,删除了1行数据 }else{ echo '没有数据被删掉'; }
获取还剩下多少条数据
循环计算删除成功后还剩下多少行数据
$conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); // 删除数据 // $id = isset($_GET['id'])? $_GET['id']:0; // $sql = 'delete from regist where id ='. $id; // mysql_query($sql, $conn); // $cnt = mysql_affected_rows($conn); // if($cnt){ // echo "删除了 $cnt 行数据<br>"; // }else{ // echo "没有数据被删掉<br/>"; // } // 还剩下多少条数据 $sql = 'select id from regist'; $result = mysql_query($sql, $conn); $num = 0; while(mysql_fetch_assoc($result)){ $num++; } echo "还剩下 $num 条数据";
int mysql_num_rows( resource $result ) 作用返回select语句的结果的行数
返回int类型,参数是select语句执行返回结果集的资源(不是链接数据库的资源)
在运行select后立即调用 mysql_num_rows() 可以得到这次select的行数
// 链接数据库 $conn = mysql_connect('localhost:3306', 'root', ''); mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); // 还剩下多少条数据 $result = mysql_query('select id from regist', $conn); $num = mysql_num_rows($result); echo "还剩下 $num 条数据";
mysql的调错
当mysql_query执行失败时,
可以用mysql_errno()打印出错误号,和mysql_error()函数打印出错误信息
$conn = mysql_connect('localhost:3306', 'root', ''); mysql_query('use xdb_orange', $conn); $result = mysql_query('select id from regist', $conn); if($result){ echo '查询成功'; }else{ echo '查询失败<br/>'; echo mysql_error(),'<br/>'; // No database selected echo mysql_errno(); // 1046 }
账户密码不对
$conn = mysql_connect('localhost:3306', 'root', 'x'); // Warning: mysql_connect(): Access denied for user 'root'@'localhost' (using password: YES) in……
释放资源
mysql服务器链接资源是有限的,因此处理完业务逻辑后,要把链接资源释放
mysql_close() 释放资源
mysql链接是有限的,默认安装一般同时只能链15个,完成数据增删改查后,要及时的关闭数据库的链接资源。
对于PHP在运行结束后,一切资源会自动释放,对于小网站不会有太大问题,对应大网站来说就比较明显了。
$conn = mysql_connect('localhost:3306', 'root', ''); mysql_query('use db_orange', $conn); $result = mysql_query('select id from regist', $conn); if($result){ echo '链接成功'; } // mysql_close($conn); // 数据要及时关闭 // 故意让while循环5秒 $i = 0; while($i<5){ echo $i++; sleep(1); } if(mysql_close($conn)){ echo '关闭成功'; }
六、查询与修改综合运用
<?php // 链接数据库 $conn = mysql_connect('localhost:3306', 'root', ''); if(!$conn){ echo '链接失败'; exit; } mysql_query('set names utf8', $conn); mysql_query('use db_orange', $conn); // 查询数据库 $id = isset($_GET['id'])?$_GET['id']:0; $sql = 'select * from regist where id ='.$id; $result = mysql_query($sql, $conn); // 返回资源类型 if(!$result){ echo '查询失败'; // 资源 exit; } $row = mysql_fetch_assoc($result); if(empty($row)){ var_dump($row); echo '没有这个用户'; // 数据是空的 exit; } // 修改数据 if($_POST){ $sql = "update regist set name = '".$_POST['user']."', age = '".$_POST['age']."', groupid = '".$_POST['groupid']."' where id = '".$_POST['id']."'"; if(mysql_query($sql)){ echo '修改成功'; header("Location: 004.php"); } } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <form action="edit.php?id=<?php echo $row['id']?>" method="post"> <input type="hidden" name='id' value="<?php echo $row['id']?>"> user:<input type="text" name='user' value="<?php echo $row['name']?>"><br/> age:<input type="text" name='age' value="<?php echo $row['age']?>"><br/> groupid:<input type="text" name='groupid' value="<?php echo $row['groupid']?>"><br/> <input type="submit" value="提交"> </form> </body> </html>