老实说,这是我第一次使用PDO和错误例外。我已经通过手册以及过去解决的不同Q / As,并提出了我非常满意的代码。但我真的需要你的意见。我已经构建了一些我通常在我的项目中经常使用的函数。
请注意,我现在正在做一个旨在学习这两件新事物的测试项目。
首先,我不是OOP的粉丝,并且总是喜欢程序编程类型。
function _database_row($_table, $_id = 0, $_key = "id") {
global $Database;
if(is_object($Database)) {
$Query = $Database->prepare("SELECT * FROM {$_table} WHERE {$_key} = :id");
if($Query->execute(Array(":id" => $_id))) {
$Query_Data = $Query->fetchAll(PDO::FETCH_ASSOC);
if(count($Query_Data) >= 1) {
if(count($Query_Data) == 1) {
return $Query_Data[0];
}
return $Query_Data;
}
} else {
throw new Exception("Database Query Failure: ".$Query->errorInfo()[2]);
}
}
return false;
}
上述函数用于从$_table
表中获取$_id
的行(不一定是整数值)。
请注意,$_id
可能(有时)是从$_REQUEST
获取的唯一(对于此函数)。通过简单地准备语句,我是否完全不受任何SQL注入威胁?
我找不到mysql_num_rows()的替代方法(我确实发现在查询中使用COUNT()时使用fetchColumn的PDO方法很少。但我不喜欢这样,所以我想要知道我做得对吗?
在人们问之前,让我解释一下,在上面的函数中我设计了它,所以当我在寻找一个时,它会直接返回行(当我使用“id”作为{{1}时,情况总会如此因为它在数据库中的PRIMARY auto_increment,而在极少数情况下我也需要多个结果:)这似乎工作正常,只需要你的意见。
使用示例:
$_key
...有时在程序编程期间,我不喜欢那些令人讨厌的“未捕获异常”错误,相反,我只是更喜欢bool(false)。所以我这样做了:
_database_row("user", 14); // fetch me the user having id # 14
_database_row("products", 2); // fetch me the user having id # 14
_database_row("products", "enabled", "status"); // fetch me all products with status enabled
(不要错过使用另一个领先的“_”)。这似乎也很好,所以你的意见在这里?
重要:
现在......通过function __database_row($_table, $_id = 0, $_key = "id") {
try {
return _database_row($_table, $_id, $_key);
} catch (Exception $e) {
return false;
}
}
和SELECTS
:)来讨论FETCHINGS
所以我使这个功能在一个脚本执行中快速添加多个产品。
INSERTS
这似乎也很好用,但我真的可以依靠我用来获取最新插入ID的方法吗?
谢谢大家!
答案 0 :(得分:2)
这里有很多内容,所以我会尝试回答具体问题并解决一些问题。
我不是OOP的粉丝
请注意,仅仅因为代码具有对象并不意味着它是面向对象的。您可以以纯粹的过程风格使用PDO,并且->
的存在不会使其成为OOP。因此我不会害怕使用PDO。如果它让你感觉更好,你可以使用程序样式mysqli
代替,但我个人更喜欢PDO。
通过简单地准备语句,我对任何SQL注入威胁都完全安全吗?
没有
考虑$pdo->prepare("SELECT * FROM t1 WHERE col1 = $_POST[rightFromUser]")
。这是一份准备好的声明,但它仍然容易受到注射。注入漏洞更多地与查询本身有关。如果语句已正确参数化(例如,您使用的是?
而不是$_POST
),那么您就会知道更长时间容易受到攻击。您的查询:
SELECT * FROM {$_table} WHERE {$_key} = :id
实际上很脆弱,因为它中有可以注入的变量。虽然查询易受攻击,但它并不一定意味着代码。也许您在表和列名称上有一个白名单,并在调用函数之前检查它们。但是,查询本身不可移植。我建议在查询中避免使用变量 - 甚至是表/列名。不过,这只是一个建议。
我无法找到
的替代品mysql_num_rows()
没有。查看获取结果的计数,使用SELECT COUNT
或查看表统计信息(对于某些引擎)可以获得SELECT
语句的列数。请注意,PDOStatement::rowCount
适用于SELECT
MySQL 。 但,不保证可以使用任何特定数据库according to the documentation。我会说使用它来获取MySQL的选定行数我从未遇到过问题。
关于PDO::lastInsertId
,有类似的评论。我和MySQL也没问题。
让我解释一下,在上面的函数中我设计了它,所以只要我在找一个单独的那个就直接返回行
我建议不要这样做,因为在使用该功能时您必须了解此功能。它有时很方便,但我认为透明地处理函数的结果会更容易。也就是说,您不必检查返回值以发现其类型并找出如何处理它。
我不喜欢那些令人讨厌的未被捕获的例外情况"错误
Exception swallowing很糟糕。您应该允许异常传播并适当地处理它们。
除非发生灾难性事件(MySQL错误,无法连接到数据库等),否则通常不会发生异常。除非服务器发生任何合理的事情,否则这些错误在生产中应该非常少见。您可以向用户显示错误页面,但至少要确保记录异常。在开发过程中,您可能希望异常尽可能大,这样您就可以确切地知道要调试的内容。
我还认为名称应该具有合理的描述性,因此名为__database_row
和_database_row
的两个函数确实令人困惑。
重要提示:" PDOStatement :: closeCursor"的用途是什么?到底是什么?
我怀疑你必须使用它,所以不要太担心它。从本质上讲,它允许您并行地从单独的预准备语句中获取。例如:
$stmt1 = $pdo->prepare($query1);
$stmt2 = $pdo->prepare($query2);
$stmt1->execute();
$stmt1->fetch();
// You may need to do this before $stmt2->execute()
$stmt1->closeCursor();
$stmt2->fetch();
我可能错了,但我认为你不需要为MySQL做这件事(即你可以在两个语句上调用execute
而不调用closeCursor
。
我真的可以依靠我用来获取最新插入ID的方法吗?
PDO关于此(上文)的文档似乎比rowCount
和SELECT
更宽容。我会自信地使用MySQL,但你总是可以SELECT LAST_INSERT_ID()
。
这是一个难以回答的问题,因为对的定义很多。显然你的代码正在运行,你正在使用PDO,所以在某种程度上你是。我确实有一些批评:
global $Database;
这会依赖于脚本中先前声明的全局$Database
变量。相反,您应该将数据库作为参数传递给函数。如果您是OOP粉丝,您还可以使数据库成为具有此函数作为方法的类的属性。一般来说,你应该避免全局状态,因为它使代码更难以重用并且更难以测试。
上述函数用于从$ _table表中使用$ _id
获取一行
不是像这样创建用于查询的通用函数,而是以允许您运行用于特定目的的查询的方式设计应用程序。我真的不明白为什么要为给定ID选择表的所有列。这并不像看起来那么有用。相反,您可能希望从这些表中获取特定列,可能与其他表连接,以提供特定功能。
答案 1 :(得分:0)
好吧,你的主要问题不是OOP,而是SQL。
说实话,SQL绝不是你正在接受的愚蠢的键值存储。因此,它使你的第一个功能,只能在太有限的SQL子集上使用,完全没用。
此外,你正在用几乎自然的SQL英语做一个乱码。比较这两句话
SELECT * FROM products WHERE status = ?
很容易理解 - 不是吗?
products! enabled! status!
HUH?
更不用说这个函数很容易注入SQL。所以,你只需要摆脱它。如果你想要一个单行,你可以做这样的事情
function db_row($sql, $data = array(), $mode = PDO::FETCH_ASSOC) {
global $Database;
$stmt = $Database->prepare($sql);
$stmt->execute($data);
return $stmt->fetch($mode);
}
以这种方式调用:
$row = db_row("SELECT * FROM products WHERE id = ?",[14]);
请注意,PDO足够智能,无需任何干预即可向您报告错误。您只需将其设置为异常模式。
说到第二个功能,可以查看。
function _add_product($_name, $_price = 0)
{
global $Database;
$sql = "INSERT INTO products (name, price) VALUES (?,?)";
$Database->prepare($sql)->execute(func_get_args());
return $Database->lastInsertId();
}
是你真正需要的。
我找不到mysql_num_rows()
的替代品
你实际上从来不需要使用mysql,也不需要使用PDO