对于我的新网站创建少数的函数后,我很快就意识到事情用永久编程失控与所有的头文件,所以我决定去学习和转换我目前正在写的函数到面向对象的类和从MySQL转换到mysqli。到目前为止,转换并没有太糟糕,但我已经达到了一个需要多个查询的函数,一个SELECT在我更新它之前检查数据。我到目前为止所写的内容,使用准备好的语句,就像魅力一样,但它只做SELECT。我被困的地方是更新数据库的时间。这是我到目前为止所做的:
public function ban() {
$connection = Database::getConnection();
$user_id = $_POST['user'];
$feedback = '';
$query = "SELECT banned, user_name
FROM users
WHERE user_id = ?";
$stmt = $connection -> prepare($query);
// Check for a multi-user selection on POST
if (count($user_id) > 1) {
foreach ($user_id as $value) {
$stmt -> bind_param('i', $value);
if (!$result = $stmt -> execute()) {
$feedback .= "Did not execute.";
} else {
$stmt -> bind_result($banned, $user);
$stmt -> fetch();
if ($banned == 1) {
$feedback .= $user . " is already banned.<br />";
} else {
// This is where I need the code to update the database
// with the users who aren't already banned.
}
}
}
$stmt -> close();
return $feedback;
} else {
// This is where the UPDATE will be for a single person ban
// if only one was selected on POST
}
}
我可以为UPDATE注入创建/执行第二个预准备语句,在我需要代码的部分中运行另一个循环内部,或者最好避免这种情况,或者甚至可以工作吗?我敢肯定mysqli_multi_query可能是要走,不必再次重写功能,因为我发现了(写这么多的功能后),您不能使用准备好的语句与multi_query注入的最佳方式。重写不是什么大问题,但使用multi_query的帮助很少。 PHP网站有很多文档,但说实话,这非常令人困惑。
UPDATE查询看起来像这样:
$explain = mysql_real_escape_string($_POST['banExplain']);
UPDATE users
SET banned = '1', ban_reason = '$explain'
WHERE user_id = '$value'"
对此的任何帮助将不胜感激。希望我能够很好地解释我需要做的事情。如果没有,请告诉我。谢谢!
答案 0 :(得分:1)
代码示例仍然非常程序化。
您应该有一个方法来检查用户是否是user_banned以及ban_user用户的方法。
user_banned方法应该使用user_id并返回一个布尔值。
ban_user方法应该有一个原因和user_id。
应该有另一个循环的函数或方法。
将user_id转换为数组,然后您可以执行一次循环。
使用例外来处理错误。
<?php
//the model
...
public function update_users (array $user_ids)
{
$result = array();
foreach ($user_ids as $user_id) {
if (!$this->user_banned($user_id)) {
$this->ban_user($user_id, $reason);
} else {
$result[$user_id] = "already banned";
}
return $result;
}
...
//the controller
//prevent XSS, cast as integers or use filter_var or something
$user_ids = sanitize((array) $_POST['user']);
try {
$result = $obj->update_users($user_ids);
} catch (Exception $e) {
...
答案 1 :(得分:0)
当我重新阅读我的问题时,它让我考虑使用第二个准备好的声明进行更新。首先,我尝试在初始foreach
循环中包含第二个语句,但这是错误的,大概是因为你不能一次打开超过1个语句。我接下来决定将每次迭代的值(被检查的user_id)存储到一个单独的数组中,然后在关闭初始foreach
循环和准备语句之后,我使用新的准备语句运行第二个循环UPDATE
瞧!以下是现在的代码:
public function ban() {
$connection = Database::getConnection();
$user_id = $_POST['user'];
$reason = $_POST['banExplain'];
$update = array();
$feedback = '';
$query = "SELECT banned, user_name
FROM users
WHERE user_id = ?";
$query2 = "UPDATE users
SET banned = '1', ban_reason = ?
WHERE user_id = ?";
$stmt = $connection -> prepare($query);
$stmt2 = $connection -> prepare($query2);
if (count($user_id) > 1) {
foreach ($user_id as $value) {
$stmt -> bind_param('i', $value);
if (!$result = $stmt -> execute()) {
$feedback .= "Did not execute search.<br />";
} else {
$stmt -> bind_result($banned, $user);
$stmt -> fetch();
if ($banned == 1) {
$feedback .= $user . " is already banned.<br />";
} else {
$update["$user"] = $value; // Populate an array to pass to update loop
}
}
}
$stmt -> close();
// Run update query - $key from $update var is the user name
foreach ($update as $key => $value) {
$stmt2 -> bind_param('si', $reason, $value);
if (!$result2 = $stmt2 -> execute()){
$feedback .="Did not execute update.<br />";
} else {
$feedback .= $key . " is banned.<br />";
}
}
$stmt2 -> close();
return $feedback;
} else {
// Executes for single selection requests
$stmt -> bind_param('i', $user_id);
if (!$result = $stmt -> execute()) {
$feedback .= "Did not execute search.<br />";
} else {
$stmt -> bind_result($banned, $user);
$stmt -> fetch();
if ($banned == 1) {
$feedback .= $user . " is already banned.<br />";
} else {
$update["$user"] = $user_id;
}
}
$stmt -> close();
// Runs loop simply for the user name in the $key var
foreach ($update as $key => $value) {
$stmt2 -> bind_param('si', $reason, $value);
if (!$result2 = $stmt2 -> execute()){
$feedback .="Did not execute update.<br />";
} else {
$feedback .= $key . " is banned.<br />";
}
}
$stmt2 -> close();
return $feedback;
}
}
对于看起来如此简单的任务来说相当冗长,而且我认为mysqli::multi_query
可能仍然是解决此问题的最佳方式(在使用mysqli::real_escape
添加所需的安全性之后)但除了这个功能的庞大之外,它的工作原理是安全的(据我所知),同时将服务器请求成本保持在最低水平。
我现在将采用user934258的建议并尝试将其分解为更小,更容易消化和维护类功能。如果/当我完成时,我会在此处跟进以显示结果,这将有望帮助其他人摆脱同样的困境。如果其他人有一些好的解决方案,请告诉我。我总是试图改进我的编码
谢谢大家,希望这至少可以帮助处于相同情况的其他人。
好吧,thanks to user934258我回去重新检查了这个/这些方法的编码。通过采纳他的建议,我能够从禁止方法中删除大约25行代码。但是,只有几个额外的行,并传递/注入一个额外的参数,我能够使代码多用途,以结合unban方法,反过来删除大约77行不需要的重复代码(unban是镜像副本除了它在数据库中检查了零而不是一个,并且显示了稍微不同的响应。)对于将来可能会发现此主题有用的人,这是我的代码的最终产品。
从脚本调用的方法:
public function ban($arg) { // 0 = Unban 1 = Ban
$this -> user_id = $_POST['user']; // initially set user id as global variable
$user_id = $this -> user_id;
$this -> banReason = mysqli_real_escape_string($_POST['banExplain']);
if (!$this -> check_ban($user_id, $arg)) {
$this -> feedback .= Admin::CONNFAIL;
return $this -> feedback; // Returned connection failure on select
} elseif (!$this -> ban_user($arg)) {
$this -> feedback .= Admin::UPFAIL;
return $this -> feedback; // Returned connection failure on update
} else {
return $this -> feedback;
}
}
检查DB禁止值的方法:
private function check_ban($user_id, $arg) {
$connection = Database::getConnection();
$query = "SELECT banned, user_name
FROM users
WHERE user_id = ?";
$stmt = $connection -> prepare($query);
foreach ($user_id as $value) {
$stmt -> bind_param('i', $value);
if (!$result = $stmt -> execute()) {
return FALSE;
} else {
$stmt -> bind_result($banned, $user);
$stmt -> fetch();
if ($arg == 1 && $banned == 1) {
$this -> feedback .= $user . " is already banned.<br />";
} elseif ($arg == 0 && $banned == 0) {
$this -> feedback .= $user . " is not currently banned.<br />";
} else {
$this -> update["$user"] = $value; // Populate array to be un/banned
}
}
}
$stmt -> close();
return TRUE;
}
最后使用un / ban更新数据库的方法:
private function ban_user($arg) {
$connection = Database::getConnection();
$update = $this -> update;
$reason = $this -> banReason;
$query = "UPDATE users
SET banned = ?, ban_reason = ?
WHERE user_id = ?";
$stmt = $connection -> prepare($query);
foreach ($update as $key => $value) {
$stmt -> bind_param('isi', $arg, $reason, $value);
if (!$result = $stmt -> execute()) {
return FALSE;
} elseif ($arg == 0) {
$this -> feedback .= $key . " is unbanned.<br />";
} else {
$this -> feedback .= $key . " is banned.<br />";
}
}
$stmt -> close();
return TRUE;
}
请注意,在这些方法中进行的错误检查/过滤/清理非常少。我有几个原因:在脚本中检查变量的值,我是唯一一个被授予权限的人,并且$user_id
等变量的值被预先填充到复选框和下拉列表中列出了db中的确切值,因此错误$user_id
的可能性很小甚至没有。
正如我在其他帖子中提到的,如果有人能为我所做的事情提供更好的解决方案,那么请告诉我。
感谢这样一个伟大的社区!