PDO ......我做得对吗?

时间:2013-12-05 16:06:30

标签: php mysql pdo

老实说,这是我第一次使用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的行(不一定是整数值)。

  1. 请注意,$_id可能(有时)是从$_REQUEST获取的唯一(对于此函数)。通过简单地准备语句,我是否完全不受任何SQL注入威胁?

  2. 我找不到mysql_num_rows()的替代方法(我确实发现在查询中使用COUNT()时使用fetchColumn的PDO方法很少。但我不喜欢这样,所以我想要知道我做得对吗?

  3. 在人们问之前,让我解释一下,在上面的函数中我设计了它,所以当我在寻找一个时,它会直接返回行(当我使用“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
    

    (不要错过使用另一个领先的“_”)。这似乎也很好,所以你的意见在这里?

    重要:

    1. 究竟是什么用“PDOStatement :: closeCursor”?我确实阅读了手册,但我很困惑,因为我可以根据需要多次调用我的函数,但仍然可以获得所需/期望的结果但从未“关闭光标”
    2. 现在......通过function __database_row($_table, $_id = 0, $_key = "id") { try { return _database_row($_table, $_id, $_key); } catch (Exception $e) { return false; } } SELECTS :)来讨论FETCHINGS

      所以我使这个功能在一个脚本执行中快速添加多个产品。

      INSERTS

      这似乎也很好用,但我真的可以依靠我用来获取最新插入ID的方法吗?

      谢谢大家!

2 个答案:

答案 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关于此(上文)的文档似乎比rowCountSELECT更宽容。我会自信地使用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