如何使用mysqli准备好的语句?
Posted
技术标签:
【中文标题】如何使用mysqli准备好的语句?【英文标题】:How to use mysqli prepared statements? 【发布时间】:2012-03-26 14:59:41 【问题描述】:我正在尝试准备好的语句,但下面的代码不起作用。我收到错误消息:
致命错误:在非对象上调用成员函数 execute() /var/www/prepared.php 第 12 行
<?php
$mysqli = new mysqli("localhost", "root", "root", "test");
if ($mysqli->connect_errno)
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// insert one row
$stmt->execute(array('one',1));
// insert another row with different values
$stmt->execute(array('two',1));
?>
另外,我需要为准备好的语句使用 mysqli 吗?谁能指出一个完整的示例,说明从连接到插入到选择以及错误处理的准备语句?
【问题讨论】:
“谁能给我一个完整的例子,说明从连接到插入到选择错误处理的准备语句” 是的。 php.net/manual/en/mysqli.prepare.php。注意 "mysqli_prepare()
行返回一个语句对象或 FALSE
如果发生错误。".
【参考方案1】:
来自mysqli::prepare
docs:
在执行语句或获取行之前,必须使用 mysqli_stmt_bind_param() 和/或 mysqli_stmt_bind_result() 将参数标记绑定到应用程序变量。
bind_param
docs.
即:
$name = 'one';
$age = 1;
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
$stmt->bind_param('si', $name, $age);
// *now* we can execute
$stmt->execute();
【讨论】:
哦,还有@Tomalak 建议首先检查$stmt
是否为FALSE(即发生错误)。【参考方案2】:
我还需要为准备好的语句使用 mysqli。任何人都可以指出一个完整的示例,说明从连接到插入到选择以及错误处理的准备语句
您也可以使用我更喜欢的 PDO。事实上,您在代码示例中似乎混淆了 PDO 和 Mysqli。
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (?,?)");
$stmt->execute(array($name1, $age1));
$stmt->execute(array($name2, $age2));
与 mysqli 不同,您不必调用单独的绑定函数,但如果您喜欢/想要/需要使用该功能,则可以使用它。
关于 PDO 的另一个有趣的事情是命名占位符,它可以在复杂的查询中减少混乱:
$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (:name,:age)");
$stmt->execute(array(':name' => $name1, ':age' => $age1));
$stmt->execute(array(':name' => $name2, ':age' => $age2));
【讨论】:
【参考方案3】:谁能给我一个完整的例子,说明从连接到插入到选择以及错误处理的准备语句?
这个问题很老,但直到最近我才完成了一些研究,产生了一系列完全涵盖该请求的文章,所以你去吧:
连接
mysqli 连接的重要性经常被忽视,被贬低为一行。而正确的连接代码可以解决许多问题,从安全性到可用性。
鉴于您的代码是通常的 PHP 程序,这里有一个简单的 mysqli 连接代码将包含在您的脚本中:
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->set_charset($charset);
catch (\mysqli_sql_exception $e)
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
unset($host, $db, $user, $pass, $charset); // we don't need them anymore
完整的解释可以在我的文章How to connect properly using mysqli 中找到(以及许多有用的提示),但只是一个小引用来突出最重要的部分:
为连接设置正确的字符集将消除整类错误,例如奇怪的字符/问号而不是您的数据、空的 json_encode() 输出、存储表情符号的问题等。 设置正确的错误报告模式将消除神秘的错误消息,如 mysqli_fetch_assoc() 需要参数... / 调用成员函数 bind_param()...,而是为您提供来自 MySQL 的实际错误消息。 安全性不是笑话,不应该有机会向外界泄露您的数据库详细信息
插入
Insert query比较简单,其他答案已经介绍过了。
您只需用问号替换查询中的所有变量(以及周围的引号!),然后准备查询,然后将所有变量及其类型推入bind_param()
,最后执行查询。
只是一个快速提示:MySQL 很乐意将所有变量作为字符串接受,所以不要为某个变量寻找正确的类型,只需使用 "s" 即可。
所以基本上插入是这样的
$sql = "INSERT INTO users (name, email, password) VALUES (?,?,?)";
$stmt= $conn->prepare($sql);
$stmt->bind_param("sss", $name, $email, $password_hash);
$stmt->execute();
同样的原则也适用于所有其他查询类型,例如 UPDATE 或 DELETE。
选择
运行select query 几乎相同,但有一个小技巧。由于某些未知的原因,您不能在准备好的语句中使用熟悉的 fetch 函数。所以你需要先得到mysqli_result,然后才能使用fetch_assoc()
、fetch_obj()
等:
$sql = "SELECT * FROM users WHERE id=?"; // SQL with parameters
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$user = $result->fetch_assoc(); // fetch data
提示:绝对不需要熟悉的mysqli_num_rows()
函数。如果你想到了,你总是可以使用数据本身来查看你的查询是否返回了任何行:
$user = $result->fetch_assoc();
if ($user)
// found!
多行也是如此,感谢
另一个提示:有一个方便的函数fetch_all()
可以一次性获取所有选定行的数组。例如,如果查询返回多行,您可以通过将最后一行更改为
$users = $result->fetch_all(MYSQLI_ASSOC); // fetch data
错误处理
错误处理是最重要但又有点令人惊讶的部分。尽管有许多文章和示例都说了,但作为一项规则,您根本不应该编写任何错误处理代码。这听起来绝对是疯狂的,但这正是事情必须要做的事情。大多数时候,您需要做的只是报告错误。并且 mysqli/PHP 已经可以为您完成,无需帮助。因此,您不应该编写任何验证查询执行结果的代码——如果出现错误,mysqli 会自动报告,这要归功于#Connection 部分中提到的mysqli_report()
函数调用。同样,这个原理的完整解释可以在另一篇文章中找到,献给将军PHP error reporting。
在极少数情况下,您确实需要处理错误,即在出现错误时执行一些操作,而不是仅仅报告错误,然后包装您的查询(es)try..catch
。
【讨论】:
【参考方案4】:试试这个,清理传入的数据。 并且不要忘记 php 标签。
function clean($data)
$data = trim(strip_tags(htmlspecialchars($data)));
return $data;
$field1 = isset($_POST['field1']) ? clean($_POST['field1']): NULL;
$field2 = isset($_POST['field2']) ? clean($_POST['field2']): NULL;
$field3 = isset($_POST['field3']) ? clean($_POST['field3']): NULL;
$field4 = isset($_POST['field4']) ? clean($_POST['field4']): NULL;
$field5 = isset($_POST['field5']) ? clean($_POST['field5']): NULL;
$field6 = isset($_POST['field6']) ? clean($_POST['field6']): NULL;
$field7 = isset($_POST['field7']) ? clean($_POST['field7']): NULL;
$database = new mysqli("localhost", "username", "password", "database");
if ($database->errno) die("Error opening database: " . $database->error());
$query = 'INSERT INTO `tablename` (`field1`, `field2`, `field3`, `field4`, `field5`, `field6`, `field7`) VALUES (?, ?, ?, ?, ?, ?, ?)';
$result = $database->prepare($query);
$result->bind_param('sssssss', $field1, $field2, $field3, $field4, $field5, $field6, $field7);
$result->execute();
$database->close();
header("Location: http://www.somewebsite.com");
【讨论】:
这个函数不会清理任何东西,与mysqli准备好的语句无关 这是一个经过测试且功能正常的脚本,请您解释一下您声明的原因。以上是关于如何使用mysqli准备好的语句?的主要内容,如果未能解决你的问题,请参考以下文章
如何在mysqli准备好的语句中使用SQL LIKE子句[重复]