PHP 的 mysql_query() 返回值探究

一、前言

前段时间学校 PHP 结课的时候做了一个课程设计,一个在线留言板,今天在把该程序部署到服务器上的时候遇到了一些问题,解决之后也算是对 mysql_query() 这个函数有了更深的认识,写个博文记录一下。

在写这个留言板的时候尝试了用面向对象的思想来写,自然的,数据库的操作也封装成了类的一个个方法,关于 Query 的方法我是这么写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/** 
* 请求一条mysql
* @param string $sql 执行的语句
* @param bool $fetchfirst 是否只返回第一条结果
*
* @return 查询结果 (false为无结果)
*/
public function Query($sql, $fetchfirst=false)
{
$PHP = substr(PHP_VERSION, 0, 1);
$resultset = array();

if ($PHP < 7){
$result = mysql_query($sql) or $this->err($sql);
if (!($result instanceof mysql_result))
return $result;

while($row = mysql_fetch_array($result)) {
if ($fetchfirst) return $row;
array_push($resultset, $row);
}
} else {
$result = mysqli_query($this->link, $sql) or $this->err($sql);
if (!($result instanceof mysqli_result))
return $result;

while($row = mysqli_fetch_array($result)) {
if ($fetchfirst) return $row;
array_push($resultset, $row);
}
}
return $resultset;
}

由于自己电脑上的开发环境用的是 PHP7,而学校机房用的是旧的 PHP5,而在 PHP7 中,mysql 系列的函数都被废弃了,取而代之的是 mysqli 系列函数,因此在这个方法上我也加了关于 PHP 版本的判断,根据版本来使用不同的请求函数。

二、问题出现

整个程序在本地跑起来是没有任何的问题的,增删查改都运行正常,而今天部署到服务器上 (环境是PHP5) 后,数据库的 Query 操作都出现了异常,无法正常取得数据。

以用户登录为例:

1
2
3
4
5
6
7
8
9
10
11
$db = Database::getInstance();
$Result = $db->Select("*", null, "user", "username='$username' and password='$password_md5'", true);
if ($Result){
//登录成功设置Session... 然后跳到主页
$_SESSION["userid"] = $Result['id'];
header('Location: index.php');
exit();
} else {
$HintType = 'error';
$HintMsg = '用户名或密码不正确!';
}

这段代码在我本地(PHP7)运行起来没有任何的问题,而在服务器(PHP5)上,不管用户是否输入了正确的密码,结果都会跳转到主页 index.php 去。很显然,问题出现在返回的值 $Result 上。

三、解决问题

回过头来看看我是怎么处理 mysql_query() 的返回值的:

1
2
3
4
5
6
7
8
9
10
11
if ($PHP < 7){
$result = mysql_query($sql) or $this->err($sql);
if (!($result instanceof mysql_result))
return $result;
//遍历获取结果...
} else {
$result = mysqli_query($this->link, $sql) or $this->err($sql);
if (!($result instanceof mysqli_result))
return $result;
//遍历获取结果...
}

在此以前,我对于 mysql_query() 的理解是,有结果返回,他就是个 mysql_result 类型,否则,他就是个 bool 类型,代表执行的成功与失败,所以我用 instanceof mysql_result 判断它是否是资源类型,如果不是,则直接返回;如果是,则继续下一步的遍历获取结果。

于是,我在 login 的代码打了个断点,输出 $Result 的值然后 die() 掉:

1
2
3
4
5
$db = Database::getInstance();
$Result = $db->Select("*", null, "user", "username='$username' and password='$password_md5'", true);
echo $Result;
die();
//....

结果让我意外,尽管我用户名和密码乱打一通,但是返回的 $Result 居然是个资源类型(输出值大概是 Resource #XXXXXX)。于是我赶紧上 php 官网查文档,得知

mysql_query() 仅对 SELECT,SHOW,DESCRIBE, EXPLAIN 和其他语句 语句返回一个 resource,如果查询出现错误则返回 FALSE

对于其它类型的 SQL 语句,比如INSERT, UPDATE, DELETE, DROP 之类, mysql_query() 在执行成功时返回 TRUE,出错时返回 FALSE

来源:http://php.net/manual/zh/function.mysql-query.php#refsect1-function.mysql-query-returnvalues

登录查表用的是 SELECT 语句,所以它必定返回的是个 Resource 资源类型,难怪到了 PHP5 上查询会出问题。所以,我将查询的方法做了以下修改:

1
2
3
4
5
6
7
8
if ($PHP < 7){
$result = mysql_query($sql) or $this->err($sql);
if (is_bool($result))
return $result;
//遍历获取结果...
} else {
//...
}

使用 is_bool() 判断返回值 $result 是否是布尔值 true or false,如果不是,那它必定就是资源类型,然后进一步遍历获取查询结果。保存之,再重新尝试登录,果然问题解决了。程序其它地方的查询问题也一并解决了。

四、继续深究

既然 SELECT 查询必定返回的是一个资源值,为什么我在本地 PHP7 的环境没有遇到以上问题?我继续在 php 官网查询了 mysqli_query() 的文档:

失败时返回 FALSE,通过mysqli_query() 成功执行SELECT, SHOW, DESCRIBEEXPLAIN查询会返回一个mysqli_result 对象,其他查询则返回TRUE

来源:http://php.net/manual/zh/mysqli.query.php#refsect1-mysqli.query-returnvalues

所以看出区别了,mysql_query() 返回的是一个资源类型,而 mysqli_query() 返回的是一个资源对象

所以难怪我在 PHP7 中不会遇到这样的 BUG。因为 instanceof 主要就是用来确认一个变量是否是某个类的实例。而对于没有结果的查询,mysqli_fetch_array 则会返回一个空数组,当 if 判断一个空数组的时候,就会返回 false

继续查看关于 Resource 资源类型 的文档说明,得知这个 Resource 它还分为 数据库连接资源文件资源文档资源,可以用 get_resource_type() 来获取它的具体类型,又涨姿势了。

最后,我的 Query 方法改成了如下代码:(其实只改动了一处)

1
2
3
4
5
6
7
8
9
10
11
if ($PHP < 7){
$result = mysql_query($sql) or $this->err($sql);
if (is_bool($result)) //原来是 !($result instanceof mysql_result)
return $result;
//遍历获取结果...
} else {
$result = mysqli_query($this->link, $sql) or $this->err($sql);
if (!($result instanceof mysqli_result)) //这里没改,维持原状
return $result;
//遍历获取结果...
}

五、结语

自己在试一个函数的时候,千万不能根据各种情况试了它的返回值之后就以为就是那样子的,一定要以官方文档为准←_←

程序的兼容性真的很重要,只顾着测试 PHP7 是否运行正常,而没有在 PHP5 上进行测试。假如老师的电脑上用的是 PHP 5… emmmmmm…. 不知道会不会影响我的期末分数((((

遇到问题的时候,查 Google,查文档!!




后话:

这个留言板已经成功部署到服务器上啦,地址在此:https://project.crazykid.moe/php-guestbook/

同时也将源码挂到 Github 上了,repo地址:https://github.com/CrazyKidCN/PHP-GuestBook

初学者,请各位大佬多多指教> <