根据用户输入构建动态的PHP准备语句

时间:2019-05-14 16:42:03

标签: php mysql mysqli prepared-statement

我有一个包含学生记录的数据库,大约有50列,所有这些都需要搜索。我熟悉如何通过附加字段来构建动态搜索查询。我似乎找不到使用本质上是动态的预备语句搜索MySQL数据库的任何示例。例如,在搜索时,您将仅输入需要搜索的字段,因此,如何根据用户输入构建动态的准备好的语句?

这是一个不使用预准备语句就如何执行此操作的示例,但是这很容易发生SQL注入。我需要用一个函数“清理”每个变量。但是,有了准备好的声明,这将更加安全。

function clean($con,$var){
    $var = trim($var);
    $var = strtolower($var);
    $var = mysqli_real_escape_string($con,$var);
    return $var;
}

if(isset($_REQUEST['sub'])){

    if(isset($_REQUEST['student_fname'])){
        $student_fname = null;
        if($_REQUEST['student_fname'] != ''){
            $student_fname = '&& `student_fname` = \''.clean($con,$_REQUEST['student_fname']).'\' ';
        }
    }
    if(isset($_REQUEST['student_lname'])){
        $student_lname = null;
        if($_REQUEST['student_lname'] != ''){
            $student_lname = '&& `student_lname` = \''.clean($con,$_REQUEST['student_lname']).'\' ';
        }
    }

    $sql = "SELECT * FROM `student_records` 
    WHERE 1=1 
    $student_fname
    $student_lname
    ORDER BY class ASC, student_lname ASC";
    echo $sql;
}

1 个答案:

答案 0 :(得分:3)

与许多事情一样,使用PDO会容易很多。但是,这就是我要解决的方法。您需要在此处构建3个结构。 SQL,参数类型列表(“ s”或“ i”)以及参数本身列表。这一切都非常简单。

如果您以前没有做过此操作,则将其放入bind_param()函数可能会有些棘手,但是一旦将所有参数都放在一个as detailed elsewhere中,argument unpacking operator就会很好用数组。

对于POST变量,请记住empty()会检查array元素是否存在以及是否为非空元素。这将为您节省额外的支票,唯一的警告是empty("0") === true

<?php
if (isset($_REQUEST['sub'])) {
    $types = "";
    $where = [];
    $params = [];

    if (!empty($_REQUEST['student_fname'])) {
        $types .= 's';
        $where[] = 'student_fname = ?';
        $params[] = $_REQUEST['student_fname'];
    }
    if (!empty($_REQUEST['student_lname'])) {
        $types .= 's';
        $where[] = 'student_lname = ?';
        $params[] = $_REQUEST['student_lname'];
    }

    if (count($where)) {
        $where = "AND " . explode(" AND ", $where);
    } else {
        $where = "";
    }

    $sql = "SELECT * FROM student_records
    WHERE 1=1 
    $where
    ORDER BY class ASC, student_lname ASC";

    $stmt = $con->prepare($sql);
    $stmt->bind_param($types, ...$params);
    $stmt->execute();
    while ($res = $stmt->get_result()) {
        // ....
    }
}

请注意,此代码允许某人列出整个数据库。如果这不是您的意图,请将所有内容包装在if (count($where))语句中。


在PDO中,事情变得容易得多,不需要绑定,并且参数已经作为数组传递了。

<?php
if(isset($_REQUEST['sub'])){
    $where = [];
    $params = [];

    if(!empty($_REQUEST['student_fname'])) {
        $where[] = 'student_fname = ?';
        $params[] = $_REQUEST['student_fname'];
    }
    if(!empty($_REQUEST['student_lname'])){
        $where[] = 'student_lname = ?';
        $params[] = $_REQUEST['student_lname'];
    }

    if (count($where)) {
        $where = "AND " . explode(" AND ", $where);
    } else {
        $where = "";
    }

    $sql = "SELECT * FROM student_records
    WHERE 1=1 
    $where
    ORDER BY class ASC, student_lname ASC";

    $stmt = $con->prepare($sql);
    $stmt->execute($params);
    $data = $stmt->fetchAll(\PDO::FETCH_ASSOC);
}