[英]Creating a prepared statement for PHP query
這是我目前的代碼:
$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 注入是開放的。 為了避免它們,應始終准備好接收外部值的 SQL 語句以供執行。 因此,應避免使用mysqli::query (因為它不允許任何准備)。
這里有兩個關於“如何使$resultSet
作為准備好的語句”的示例,描述了在 MySQLi 中准備 SQL 語句的兩種適用方法,條件是mysqlnd ("MySQL Native Driver") 的存在。
方法 1 - 如果安裝了mysqlnd驅動程序:
此方法使用mysqli_stmt::get_result和mysqli_result::fetch_all 。
為了安裝mysqlnd驅動程序,必須在 PHP 配置文件( “php.ini” )中取消注釋條目“extension=php_mysqli_mysqlnd.dll” (或類似的),並且必須重新啟動 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_stmt::store_result 、 mysqli_stmt::bind_result和mysqli_stmt::fetch 。
<?php
require 'connection.php';
$saleId = 1;
$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';
$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,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>
connection.php(包含在上面的兩個示例中):
有關詳細信息,本文將展示如何在 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 切換到PDO 。 這是一個非常好的 PDO 教程。
您嘗試使用if($stmt = $link -> prepare(...)){...}
應用一些錯誤處理。 盡管如此,為了能夠正確處理您當前和未來的代碼可能引發的任何類型的錯誤,我建議您仔細閱讀本教程以及該網站上與 MySQLi 相關的所有文章。
最后,盡可能避免從 PHP 代碼打印 HTML 代碼(例如使用echo
)。 嘗試以一種干凈的方式將 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,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; }
.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>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.