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>