微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为PHP查询创建准备好的语句

如何解决为PHP查询创建准备好的语句

这是我当前拥有的代码

$resultSet = $link->query("SELECT kudos.sale_id as TheID,kudos.ident_id AS TheIdent from kudos,sales_gdpr where kudos.sale_id = $id AND sales_gdpr.id = kudos.sale_id");
                                    
if($stmt = $link -> prepare("SELECT COUNT(*) FROM kudos WHERE sale_id=? AND ident_id=?")) 
{
    $stmt -> bind_param("is",$id,$myIdent);
    $stmt -> execute();
    $stmt -> bind_result($count);
    $stmt -> fetch();
    $stmt -> close();
}

if ($count == 0) { // Not liked
    echo "<a style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </a>";
} else { // Has liked
    echo "<b style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </b>";
}

我已经将SELECT COUNT做为准备好的陈述,但是我不知道如何将$resultSet做为准备好的陈述。该代码现在可以正常运行,但是由于kudos.sale_id = $id部分,我认为它对sql注入很脆弱。有人可以帮我吗?

解决方法

其中直接直接将外部值传递到SQL语句(如SELECT ... FROM ... WHERE id = $id)中的代码对SQL injections开放。为了避免它们,应始终准备接收外部值的SQL语句以供执行。因此,应避免使用mysqli::query(因为它不允许进行任何准备)。

这里有两个关于“如何使$resultSet作为准备好的语句” 的示例,描述了在MySQLi中准备SQL语句的两种适用方法,条件是{ {3}} (“ MySQL本机驱动程序”)

方法1-如果安装了 mysqlnd 驱动程序:

此方法使用mysqlndmysqli_stmt::get_result

为了安装 mysqlnd 驱动程序,条目“ extension = php_mysqli_mysqlnd.dll” (或类似名称)必须在PHP配置文件( “ php.ini” )和Web服务器,以及 mysql服务

<?php
require 'connection.php';

/*
 * Save the values,with which the database data will be filtered,into variables.
 * These values will replace the parameter markers in the sql statement.
 * They can come,for example,from a POST request of a submitted form.
 */
$saleId = 1;

/*
 * The SQL statement to be prepared. Notice the so-called markers,* e.g. the "?" signs. They will be replaced later with the
 * corresponding values when using mysqli_stmt::bind_param.
 *
 * @link http://php.net/manual/en/mysqli.prepare.php
 */
$sql = 'SELECT 
            k.sale_id AS saleId,k.ident_id AS identId 
        FROM 
            kudos AS k,sales_gdpr AS s 
        WHERE 
            k.sale_id = ? AND 
            s.id = k.sale_id';

/*
 * Prepare the SQL statement for execution - ONLY ONCE.
 *
 * @link http://php.net/manual/en/mysqli.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Bind variables for the parameter markers (?) in the
 * SQL statement that was passed to prepare(). The first
 * argument of bind_param() is a string that contains one
 * or more characters which specify the types for the
 * corresponding bind variables.
 *
 * @link http://php.net/manual/en/mysqli-stmt.bind-param.php
 */
$statement->bind_param('i',$saleId);

/*
 * Execute the prepared SQL statement.
 * When executed any parameter markers which exist will
 * automatically be replaced with the appropriate data.
 *
 * @link http://php.net/manual/en/mysqli-stmt.execute.php
 */
$statement->execute();

/*
 * Get the result set from the prepared statement.
 *
 * NOTA BENE:
 * Available only with mysqlnd ("MySQL Native Driver")! If this
 * is not installed,then uncomment "extension=php_mysqli_mysqlnd.dll" in
 * PHP config file (php.ini) and restart web server (I assume Apache) and
 * mysql service. Or use the following functions instead:
 * mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
 *
 * @link http://php.net/manual/en/mysqli-stmt.get-result.php
 * @link https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
 */
$result = $statement->get_result();

/*
 * Fetch all data at once and save it into an array.
 *
 * @link http://php.net/manual/en/mysqli-result.fetch-all.php
 */
$fetchedData = $result->fetch_all(MYSQLI_ASSOC);

/*
 * ...or fetch and save one row at a time.
 *
 * @link https://secure.php.net/manual/en/mysqli-result.fetch-array.php
 */
// while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
//     $fetchedData[] = $row;
// }

/*
 * Free the memory associated with the result. You should
 * always free your result when it is not needed anymore.
 *
 * @link http://php.net/manual/en/mysqli-result.free.php
 */
$result->close();

/*
 * Close the prepared statement. It also deallocates the statement handle.
 * If the statement has pending or unread results,it cancels them
 * so that the next query can be executed.
 *
 * @link http://php.net/manual/en/mysqli-stmt.close.php
 */
$statement->close();

/*
 * Close the previously opened database connection.
 *
 * @link http://php.net/manual/en/mysqli.close.php
 */
$connection->close();
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 10px; font-family: "Verdana",Arial,sans-serif; }
            .result-set { border-collapse: separate; border: 1px solid #ccc; }
            .result-set thead th { padding: 10px; background-color: #f3f3f3; }
            .result-set tbody td { padding: 5px; }
        </style>
    </head>
    <body>

        <h3>
            Result set
        </h3>

        <table class="result-set">
            <thead>
                <tr>
                    <th>Sale ID</th>
                    <th>Ident ID</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($fetchedData) {
                    foreach ($fetchedData as $item) {
                        $saleId = $item['saleId'];
                        $identId = $item['identId'];
                        ?>
                        <tr class="result-set-record">
                            <td><?php echo $saleId; ?></td>
                            <td><?php echo $identId; ?></td>
                        </tr>
                        <?php
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="2">
                            No records found
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>

方法2-如果未/无法安装 mysqlnd 驱动程序:

此方法使用mysqli_result::fetch_allmysqli_stmt::store_resultmysqli_stmt::bind_result

<?php
require 'connection.php';

$saleId = 1;

$sql = 'SELECT 
            k.sale_id AS saleId,sales_gdpr AS s 
        WHERE 
            k.sale_id = ? AND 
            s.id = k.sale_id';

$statement = $connection->prepare($sql);

$statement->bind_param('i',$saleId);

$statement->execute();

/*
 * Transfer the result set resulted from executing the prepared statement.
 * E.g. store,e.g. buffer the result set into the (same) prepared statement.
 *
 * @link http://php.net/manual/en/mysqli-stmt.store-result.php
 * @link https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
 */
$result = $statement->store_result();

/*
 * Bind the result set columns to corresponding variables.
 * E.g. these variables will hold the column values after fetching.
 *
 * @link http://php.net/manual/en/mysqli-stmt.bind-result.php
 */
$statement->bind_result($boundSaleId,$boundIdentId);

/*
 * Fetch results from the result set (of the prepared statement) into the bound variables.
 *
 * @link http://php.net/manual/en/mysqli-stmt.fetch.php
 */
$fetchedData = [];
while ($statement->fetch()) {
    $fetchedData[] = [
        'saleId' => $boundSaleId,'identId' => $boundIdentId,];
}

/*
 * Free the stored result memory associated with the statement,* which was allocated by mysqli_stmt::store_result.
 *
 * @link http://php.net/manual/en/mysqli-result.free.php
 */
$statement->free_result();

$statement->close();

$connection->close();
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,sans-serif; }
            .result-set { border-collapse: separate; border: 1px solid #ccc; }
            .result-set thead th { padding: 10px; background-color: #f3f3f3; }
            .result-set tbody td { padding: 5px; }
        </style>
    </head>
    <body>

        <h3>
            Result set
        </h3>

        <table class="result-set">
            <thead>
                <tr>
                    <th>Sale ID</th>
                    <th>Ident ID</th>
                </tr>
            </thead>
            <tbody>
                <?php
                if ($fetchedData) {
                    foreach ($fetchedData as $item) {
                        $saleId = $item['saleId'];
                        $identId = $item['identId'];
                        ?>
                        <tr class="result-set-record">
                            <td><?php echo $saleId; ?></td>
                            <td><?php echo $identId; ?></td>
                        </tr>
                        <?php
                    }
                } else {
                    ?>
                    <tr>
                        <td colspan="2">
                            No records found
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </tbody>
        </table>

    </body>
</html>

connection.php(包括在以上两个示例中):

有关详细信息,mysqli_stmt::fetch显示了如何报告mysqli中的错误。

<?php

/*
 * This page contains the code for creating a mysqli connection instance.
 */

// Db configs.
define('HOST','localhost');
define('PORT',3306);
define('DATABASE','tests');
define('USERNAME','anyusername');
define('PASSWORD','anypassword');

/*
 * Enable internal report functions. This enables the exception handling,* e.g. mysqli will not throw PHP warnings anymore,but mysqli exceptions
 * (mysqli_sql_exception).
 *
 * MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
 * MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
 *
 * @link http://php.net/manual/en/class.mysqli-driver.php
 * @link http://php.net/manual/en/mysqli-driver.report-mode.php
 * @link http://php.net/manual/en/mysqli.constants.php
 */
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

/*
 * Create a new db connection.
 *
 * @see http://php.net/manual/en/mysqli.construct.php
 */
$connection = new mysqli(HOST,USERNAME,PASSWORD,DATABASE,PORT);

注释/建议

我没有测试代码,但是它们应该可以工作。

我可以自由使用我的命名/编码约定。

我强烈建议您从MySQLi切换到this articlePDO是非常好的PDO教程。

您尝试使用if($stmt = $link -> prepare(...)){...}进行一些错误处理。但是,为了能够正确处理当前和将来的代码可能引起的任何类型的错误,我建议您通读Here以及该网站上与MySQLi相关的所有文章。

最后,尽可能避免从PHP代码中打印(例如,与echo类似)HTML代码。尝试以干净的方式将PHP与HTML分开。因此,不要像这样:

if ($count == 0) {
    echo "<a style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </a>";
} else {
    echo "<b style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </b>";
}

您可以这样做:

<?php
    //...

    $count = ...;
    $numRows = ...;
?>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,sans-serif; }
            .num-rows-link { color: #fff; background-color: #f3f3f3; border: 1px solid #ccc; }
            .num-rows-span { color: #fff; background-color: #f3f3f3; }
        </style>
    </head>
    <body>

        <?php
            if ($count == 0) {
        ?>
            <a class="btn num-rows-link">? <?php echo $numRows; ?></a>
        <?php
            } else {
        ?>
            <span class="btn num-rows-span">? <?php echo $numRows; ?></span>
        <?php
            }
        ?>

    </body>
</html>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。