这是我创建的用于查询的类:
<?php
mysqli_report(MYSQLI_REPORT_INDEX | MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
class DBConnect {
private $dbcon;
private $paramquery;
private $result;
public function __construct() {
try {
$this->dbcon = mysqli_init();
mysqli_real_connect($this->dbcon, '127.0.0.1', '', '', '', 3306, '', MYSQLI_CLIENT_COMPRESS);
$this->paramquery = $this->dbcon->stmt_init();
} catch (mysqli_sql_exception $e) {
exit('Database Connection Failed');
}
}
public function dbquery($querysql, $querydata) {
try {
mysqli_ping($this->dbcon);
$this->paramquery->prepare($querysql);
array_walk($querydata, function(&$escval){$escval = mysqli_real_escape_string($this->dbcon, $escval);}); //Problem
call_user_func_array(array($this->paramquery, 'bind_param'), $querydata); //Problem
$this->paramquery->execute();
} catch (mysqli_sql_exception $e) {
exit('Database Query Failed');
}
$this->result = $this->paramquery->get_result(); // problem
if ($this->result) {
$drs = $this->result->fetch_array();
$this->result->free_result();
return $drs;
}
}
public function __destruct() {
if (($this->dbcon !== null) && ($this->paramquery !== null) && ($this->result !== null)) {
$this->paramquery->close();
$this->dbcon->close();
}
unset($this->result);
unset($this->paramquery);
unset($this->dbcon);
}
}
?>
index.php文件代码是这样的:
<?php
require_once('connection.php');
$DBX = new DBConnect();
$DBX->dbquery('INSERT INTO `xathx_key` (`license`, `client`, `server`, `uniquex`) VALUES (?, ?, ?, ?)', array('ssss', '1', '3', '5', '7'));
var_dump($DBX);
unset($DBX)
?>
我正在尝试在此实例中执行INSERT查询。当查询成功执行时,我想获取成功结果或标志。但是在对象的var_dump中,我得到了一些不相关的数据,如果使用echo,则会收到一个错误,指出该对象无法转换为字符串。我只想为查询执行失败,损坏或问题得到0,为完成,成功,确定状态得到1。我什么时候在代码中出错?
编辑:你们能告诉我这个简单脚本有什么问题吗?该脚本的主要目标是连接到mysql服务器,并尽可能安全地执行所有可能的查询。
答案 0 :(得分:4)
此脚本的主要目标是连接到mysql服务器,并尽可能安全地执行所有可能的查询。
目标是一个很好的目标,但是可以从许多改进中受益。
免责声明:将有很多链接到我自己的网站,因为我在20多年的时间里一直在帮助使用PHP的人,并且对撰写有关最常见问题的文章很着迷。
首先,您需要更改错误报告的概念。对于程序员,您的exit()
方法将是一场噩梦,因为错误消息是发生错误时的重要信息来源。程序员应不遗余力地尝试获取完整的错误消息。在我的文章PHP error reporting中,我确实说明了如何使程序员和用户友好的错误报告成为可能。简而言之,您不应该当场发现错误,而应该在单个位置报告错误和异常,然后可以根据当前服务器的角色轻松进行配置。
尽管,正如另一个答案中所建议的那样,您可以在index.php文件中使用全局try-catch块充当此类全局错误处理程序,但我更喜欢专用的错误处理程序脚本,如本文所述以上。这将使您的代码井井有条,并减少index.php的膨胀。
此外,您的“在插入查询中返回真实结果”的想法与您使用异常的意图相矛盾。当使用异常时,没有必要验证立即函数的结果。万一发生错误,它只会冒泡到错误处理程序或catch块,因此,它将永远不会达到条件。一个简单的例子:
function test() {
throw new Exception("Test");
return false;
}
$result = test();
if ($result === false) {
echo "false";
}
此示例中的代码执行永远不会达到条件,因此使您的函数在错误时返回false毫无用处。反过来,这使得在成功时返回真实是多余的。只是返回有意义的结果,但不要将其用作标志:只需编写代码而没有任何条件,就好像一切都很好。请记住,您在其他地方有错误处理代码,如果发生错误,这些代码将被神奇地调用。
正如我在另一篇文章How to connect properly using mysqli中所解释的那样,如果出现连接错误,则有少许机会显示连接凭据。为了避免发生这种情况,但要使程序员了解情况,我们必须抛出一个全新异常,但是要保留错误信息-因此堆栈跟踪将从throw行开始,因此不包含任何敏感信息。
此外,连接代码缺少必要的部分-设置正确的字符集。尽管在MySQL 8中默认设置了正确的字符集,但最好将其明确化。
另外,使mysqli语句成为类变量是一个严重错误,将导致race condition errors。您的类应保留的唯一状态是与连接有关的状态,但不能将单个类变量用于语句。
因此,让我们根据上一篇文章中的代码重写您的构造函数:
public function __construct()
{
mysqli_report(MYSQLI_REPORT_INDEX | MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$this->dbcon = mysqli_init();
$this->dbcon->real_connect('127.0.0.1', '', '', '', 3306, '', MYSQLI_CLIENT_COMPRESS);
$this->dbcon->set_charset('utf8mb4');
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
}
坦率地说,该功能很奇怪。准备好的语句和转义之间是奇怪的混合。让我们根据我的mysqli helper function实际使用mysqli准备好的语句进行重写
public function dbquery($sql, $data = [], $types = "")
{
$this->dbcon->ping(); // not sure if it's necessary
$stmt = $this->dbcon->prepare($sql);
if ($data) {
$types = $types ?: str_repeat("s", count($data));
$stmt->bind_param($types, ...$data);
}
$stmt->execute();
return $stmt->get_result();
}
现在此功能可以满足您对安全SQL查询的需求
所以最后我们可以重写您的index.php
<?php
require_once('connection.php');
$DBX = new DBConnect();
$sql = 'INSERT INTO `xathx_key` (`license`, `client`, `server`, `uniquex`) VALUES (?, ?, ?, ?)';
$DBX->dbquery($sql, ['1', '3', '5', '7']);
就像您在上面学到的那样,不需要“成功执行查询时的标志”。就像总能成功一样。如果出现错误,它将毫无条件地显示(如果在索引中包含错误处理程序脚本,则将正确处理实时站点上的内容)。
答案 1 :(得分:1)
您有问题
$this->result = $this->paramquery->get_result();
因为mysqli_stmt::get_result
returns a resultset for successful SELECT queries, or FALSE for other DML queries or on failure.
Other DML-queries
是INSERT,UPDATE,DELETE。这就是示例中的内容。
要解决您的问题,您可以通过向$mysqli->errno
添加一些额外的检查来修改类:
$this->result = $this->paramquery->get_result();
if ($this->result) {
...
}
if ($this->paramquery->errno !== 0) { // we have some real error
exit('Database Query Failed');
}
// we have DML-query (INSERT, UPDATE, DELETE)
// and we can return number of affected rows (if it's necessary)
return $this->paramquery->affected_rows;
P.S。我同意this comment,并且我认为您的课程仅应用于教育目的,因为它存在多个严重缺陷。
答案 2 :(得分:0)
在您的DBConnect
类中,您有try catch块。但是您的catch块只是使用exit
语句来终止请求。您的班级不应这样做。
假设您将其部署在生产环境中,并且由于某种原因DB连接失败。在这种情况下,用户将只看到带有“数据库连接失败”消息的白屏,看起来根本不专业。
相反,您的类应该将此信息传递回index.php
,后者调用该类的方法,并让index.php
处理错误消息或异常。
因此,我将对您的代码进行以下更改:
DBConnect
类应引发Exception而不是完全终止程序的执行。以下是__contruct()
的外观。 public function __construct() {
try {
$this->dbcon = mysqli_init();
mysqli_real_connect($this->dbcon, '127.0.0.1', '', '', '', 3306, '', MYSQLI_CLIENT_COMPRESS);
$this->paramquery = $this->dbcon->stmt_init();
} catch (mysqli_sql_exception $e) {
//exit('Database Connection Failed'); Commented this out.
//Throw the Exception Here. This will then be passed to the calling code.
throw $e;
}
}
您需要相应地更改其他方法。
index.php
文件中,您应该查找上述异常。因此,您应该将代码移到“尝试捕获”块中以捕获该异常。require_once('connection.php');
try {
$DBX = new DBConnect();
$DBX->dbquery('INSERT INTO `xathx_key` (`license`, `client`, `server`, `uniquex`) VALUES (?, ?, ?, ?)', array('ssss', '1', '3', '5', '7'));
} catch (Exception $e) {
$message = 'Caught exception: ', $e->getMessage() . "\n";
//Display this Message to User in an appropriate way.
//Write to Error Log
}
//var_dump($DBX);
//unset($DBX)
因此,在数据库连接失败以及插入查询失败时,这将捕获异常。您可以将异常写入日志,以便以后检查它们,并可以根据引起的异常向用户显示任何适当的错误消息。
您可以在PHP Manual
中阅读有关异常的更多信息。答案 3 :(得分:-1)
我认为您在成功插入记录或查询执行后正在寻找return true;
或return 1;
。
$this->db->update('table_name',$Array_to_insert);
$last_id = $this->db->insert_id();
if($this->db->affected_rows() == 1){
return TRUE;
}
else {
return FALSE;
}