[英]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.