这是我创建的用于查询的类:
<?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);
}
}
?>
Run Code Online (Sandbox Code Playgroud)
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)
?>
Run Code Online (Sandbox Code Playgroud)
我正在尝试在此实例中执行INSERT查询。当查询成功执行时,我想获取成功结果或标志。但是在对象的var_dump中,我得到了一些不相关的数据,如果使用echo,则会收到一个错误,指出该对象无法转换为字符串。我只想为查询执行失败,损坏或问题得到0,为完成,成功,确定状态得到1。我什么时候在代码中出错?
编辑:你们能告诉我这个简单脚本有什么问题吗?该脚本的主要目标是连接到mysql服务器,并尽可能安全地执行所有可能的查询。
完整的项目来源:https : //github.com/FSMySQL/PHP-FSMySQL
该脚本的主要目标是连接到 mysql 服务器并尽可能快地、尽可能安全地执行所有可能的查询。
目标很好,但实施可以从许多改进中受益。
免责声明:会有很多指向我自己网站的链接,因为我帮助人们使用 PHP 20 多年,并且痴迷于撰写有关最常见问题的文章。
首先,你需要改变错误报告的概念。您的exit()方法对程序员来说将是一场噩梦,因为错误消息是出现问题时的重要信息来源。程序员应该不遗余力地尝试获取完整的错误消息。在我的文章PHP 错误报告中,我确实解释了如何使错误报告对程序员和用户友好。简而言之,你不应该当场捕获错误,而是有一个专用的地方来报告错误和异常,然后可以根据当前服务器的角色轻松配置它。
虽然,正如另一个答案中所建议的,您可以在 index.php 文件中使用全局 try-catch 块来充当这样的全局错误处理程序,但我更喜欢专用的错误处理程序脚本,如上面的文章所述。它会让你的代码更有条理,让 index.php 不那么臃肿。
此外,您认为“在插入查询中返回真实结果”的想法与您使用异常的意图相矛盾。当使用异常时,没有必要验证立即函数的结果。如果出现错误,它只会冒泡到错误处理程序或 catch 块,因此,它永远不会达到条件。一个简单的例子:
function test() {
throw new Exception("Test");
return false;
}
$result = test();
if ($result === false) {
echo "false";
}
Run Code Online (Sandbox Code Playgroud)
此示例中的代码执行永远不会达到条件,因此使您的函数在出错时返回 false 无用。反过来,这使得在成功时返回 true 是多余的。只需返回一个有意义的结果,但不要将其用作标志:只需在没有任何条件的情况下编写代码,就好像一切都很好。请记住,您在其他地方拥有错误处理代码,它们会在发生错误时神奇地调用。
正如我在另一篇文章如何使用 mysqli 正确连接中所解释的那样,在连接错误的情况下,有一点机会显示连接凭据。为了避免任何可能性但让程序员知道我们必须抛出一个全新的异常,但保留错误信息 - 因此堆栈跟踪将从抛出行开始,因此不包含敏感信息。
此外,连接代码缺少一个必不可少的部分 -设置正确的字符集。尽管在 MySQL 8 中默认设置了正确的字符集,但最好使其明确。
此外,将 mysqli 语句作为类变量是一个严重的错误,会导致竞争条件错误。您的类应该保留的唯一状态是与连接相关的状态,但不应将单个类变量用于语句。
因此,让我们根据上面文章中的代码重写您的构造函数:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
坦率地说,这个功能很奇怪。这是准备好的语句和转义之间的奇怪组合。让我们根据我的mysqli helper 函数重写它,该函数实际上使用了 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();
}
Run Code Online (Sandbox Code Playgroud)
现在这个函数满足了你对安全 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']);
Run Code Online (Sandbox Code Playgroud)
正如您在上面学到的那样,不需要“成功执行查询时的标志”。就好像总是成功一样。如果出现错误,它将在没有任何条件的情况下出现(如果您在索引中包含错误处理程序脚本,则会正确处理实时站点上的错误)。
在你的DBConnect班级中,你有 try catch 块。但是您的 catch 块只是使用exit语句终止请求。你的班级不应该这样做。
想象一下,您将其部署在生产环境中,并且由于某种原因数据库连接失败。在这种情况下,用户只会看到一个带有“数据库连接失败”消息的白屏,这看起来一点也不专业。
相反,您的类应该将此信息传递回调用index.php该类的方法的函数,并让其index.php处理错误消息或异常。
所以我会对您的代码进行以下更改:
DBConnect类应该抛出异常而不是完全终止程序的执行。下面是应该的样子__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;
}
}
Run Code Online (Sandbox Code Playgroud)
您将需要相应地更改其他方法。
index.php文件中,您应该寻找捕获上述异常。因此,您应该将代码移至 Try Catch 块中以捕获该异常。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)
Run Code Online (Sandbox Code Playgroud)
因此,这将在数据库连接失败以及插入查询失败时捕获异常。您可以将异常写入日志,以便稍后检查它们,并可以根据引起的异常向用户显示任何适当的错误消息。
您可以阅读PHP 手册中有关异常的更多信息
| 归档时间: |
|
| 查看次数: |
334 次 |
| 最近记录: |