PHP中使用MySQL参数化查询方法详解

MySQL参数化查询是杜绝SQL注入漏洞的根本方法。与拼接字符串的查询方式相比,参数化查询的方法也相对比较复杂。鉴于目前网络上的教程不能清晰地讲明MySQL参数化查询的方法,笔者在学习上述内容后原创地总结本文,以期帮助后来者掌握有关知识。

本文基于PHP 7+版本编写。

1.PHP中MySQL的面向对象模型:mysqli类

class DataBaseConnectionException extends Exception {}//一个异常类...
function CreateDatabaseObject() : mysqli
{
    $address = '127.0.0.1';
    $username = 'some name';
    $password = 'some password';
    $databasename = 'your database name';
    $mysqli = new mysqli($address, $username, $password, $databasename);
    //mysqli的构造函数还有两个参数,它们是:
    //port:连接服务器使用的端口,mysql服务器端没改过就用默认值
    //socket:连接远程服务器时,使用TCP/IP方法连接数据库
    //       连接本地服务器时,可使用socket或管道(pipe)连接数据库
    //       此时使用这一参数规定socket/pipe
    if($mysqli->connect_errno)
        throw new DataBaseConnectionException('database error:'.$mysqli->connect_error);
    $mysqli->set_charset('utf8');
}

创建mysqli类的方法如上所示。一旦使用new创建一个mysqli类对象以后,即可使用箭头操作符调用其成员方法。上例中,使用set_charset方法设置编码为UTF-8。

$mysqli->connect_errorno 可获取错误码。当没有错误发生时,其值为零
$mysqli->connect_error      为一字符串,内容为错误消息。没有错误发生时,其值为NULL

2.执行参数化查询

class UserNotFoundException extends Exception {}//一个异常类...
function get_usr_info($usr_name) : array
{
    $mysqli = CreateDatabaseObject();
    //创建了一个mysqli类的对象
    $_name = $mysqli->real_escape_string($usr_name);
    //参数化查询与real_escape_string并不冲突
    $query = $mysqli -> prepare("SELECT * FROM users WHERE name = ? ");
    //使用prepare方法准备语句。问号为即将替换为具体数据的参数
    //可以有任意数量问号,字符串不需要打引号
    if(!$query)
        throw new Exception("prepare query error:".$mysqli->error);
    //如果查询语句语法错误,此处就会出错,prepare函数返回false
    $query -> bind_param('s', $_name);
    //绑定  s表示将要传入一个字符串类型
    if($query->execute()) //这个函数返回值是查询是否成功
    {
        $result = $query->get_result();
        if($result->num_rows > 1)//获取行数
            throw new Exception("username not unqiue");
         else if($result->num_rows == 0)
            throw new UserNotFoundException("user not found");
        return $result->fetch_array($result,MYSQLI_ASSOC);
        //第一种获取结果的方法
    }
    else
        throw new Exception("query error:".$mysqli->error);
        //查询失败时,使用$mysqli->error获取错误消息
    $query->close();
    $mysqli->close();
    //显式地关闭查询和连接对象
}

创建查询

$query = $mysqli -> prepare(“SELECT * FROM users WHERE name = ? “);

使用prepare对象创建一个prepared statement。需要在后期绑定的参数全部以问号代替。对于字符串类型的变量,问号外面不需要引号。特别需要注意的是,如果查询语句语法错误,此处就会出错,prepare函数返回false。如果不对这一错误进行处理,将在bind_params方法处产生一个很难理解的异常信息:” Uncaught Error: Call to a member function bind_param() on boolean”。如本节前所给出的示例代码,当prepare语句出错时,可以利用mysqli::error变量来获取具体的错误信息。

参数绑定

$query -> bind_param(‘s’, $_name);
使用mysqli_stmt::bind_param()来绑定参数。s表示将要绑定的参数是一个字符串。
标记 含义
i 32位带符号整数
s 字符串
d 双精度浮点数
b blob类型,即二进制数据

例如连续的三个参数分别为整数、字符串、浮点数,则有格式串’isd’。对于同一个mysqli_stmt对象,只能进行一次参数绑定,但下面的mysqli_stmt::execute(),即执行查询,却可以反复调用。

执行查询并获取结果

$query->execute()

mysqli_stmt::execute()方法将执行查询语句,获取结果。其返回值代表查询是否成功。同样可以使用变量mysqli::error获取错误信息。(注意是数据库连接对象,不是statement对象)

第一种获取查询结果的方法如本节开头所示:使用mysqli_stmt::get_result()方法,即程序中的$query->get_result()即可获得类似于非参数化查询的结果,然后使用fetch_array等函数进行处理。

第二种方法如下所示。使用mysqli_stmt::bind_result()绑定查询结果中的每一列到一个变量,然后使用fetch()从查询结果的第一行开始读取

//第二种获取结果的方法
$query -> bind_result($name, $password);
//sql语句查询结果有几列绑定几个变量
while ($query->fetch()) 
{//每次读取一行
    printf("%s %s\n", $name, $password);
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注