duanlangwen9597 2013-12-05 16:06
浏览 25
已采纳

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

So honestly, this is the first time I am working with PDO and Error Exceptions. I have gone thru manuals as well as different Q/As resolved here in past and came up with code that I am pretty satisfied with. But I really really need your opinion on this. I have built few functions that I normally use often in my projects.

Please note that right now I am doing a test project just intended to learn these 2 new things.

First of all, I am not a fan of OOP and have always preferred procedural programming type.

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;
}

the above function is intended to fetch a row from $_table table with $_id (not necessarily an integer value).

  1. Please note that $_id may (sometimes) be the only thing (for this function) that is fetched from $_REQUEST. By simply preparing the statement, am I totally secure from any SQL injection threat?

  2. I couldn't find an alternative to mysql_num_rows() (I indeed found few PDO methods to use fetchColumn while using COUNT() in the query. But I didn't prefer it that way, so I want to know if I did it right?

also before people ask, let me explain that in above function I designed it so it returns the row directly whenever I am looking for a single one (it will always be the case when I use "id" as $_key because its PRIMARY auto_increment in database), while in rare cases I will also need multiple results :) this seems to be working fine, just need your opinions.

example of use:

_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

...sometimes during procedural programming, I wouldn't like those nasty "uncaught exception" errors, instead I will simply prefer a bool(false). So I did it like this:

function __database_row($_table, $_id = 0, $_key = "id") {
    try {
        return _database_row($_table, $_id, $_key);
    } catch (Exception $e) {
        return false;
    }
}

(don't miss the use of another leading "_"). This also seems to be working perfectly fine, so what's your opinion here?

IMPORTANT:

  1. what is the use of "PDOStatement::closeCursor" exactly? I did read the manual but I am quite confused as I can call my functions as many times as I want and still get the desired/expected results but never "closed the cursor"

now... ENOUGH WITH SELECTS AND FETCHINGS :) lets talk about INSERTS

so I made this function to add multiple products in a single script execution and quickly.

function _add_product($_name, $_price = 0) {
    global $Database;

    if(is_object($Database)) {
        $Query  =   $Database->prepare("INSERT INTO products (name, price) VALUES (:name, :price)");
        $Query->execute(Array(":name" => $_name, ":price" => $_price));

        if($Query->rowCount()   >=  1) {    
            return $Database->lastInsertId();
        } else {
            throw new Exception("Database Query Failure: ".$Query->errorInfo()[2]);
        }
    }

    return false;
}

This also seems to be working perfectly fine, but can I really rely on the method I used to get the ID of latest insert?

Thank you all!

  • 写回答

2条回答 默认 最新

  • doudi8525 2013-12-05 16:57
    关注

    There is a lot going on here, so I will try to answer specific questions and address some issues.

    I am not a fan of OOP

    Note that just because code has objects doesn't mean that it is object oriented. You can use PDO in a purely procedural style, and the presence of -> does not make it OOP. I wouldn't be scared of using PDO for this reason. If it makes you feel any better, you could use the procedural style mysqli instead, but I personally prefer PDO.


    By simply preparing the statement, am I totally secure from any SQL injection threat?

    No.

    Consider $pdo->prepare("SELECT * FROM t1 WHERE col1 = $_POST[rightFromUser]"). This is a prepared statement, but it is still vulnerable to injection. Injection vulnerability has more to do with the queries themselves. If the statement is properly parameterized (e.g. you were using ? instead of $_POST), you would know longer be vulnerable. Your query:

    SELECT * FROM {$_table} WHERE {$_key} = :id
    

    actually is vulnerable because it has variables in it that can be injected. Although the query is vulnerable, it doesn't necessarily mean that the code is. Perhaps you have a whitelist on the table and column names and they are checked before the function is called. However the query is not portable by itself. I would suggest avoiding variables in queries at all -- even for table/column names. It's just a suggestion, though.


    I couldn't find an alternative to mysql_num_rows()

    There isn't one. Looking at the count of fetched results, using SELECT COUNT or looking at the table stats (for some engines) are surefire way to get the column count for SELECT statements. Note that PDOStatement::rowCount does work for SELECT with MySQL. However, it is not guaranteed to work with any database in particular according to the documentation. I will say that I've never had a problem using it to get the selected row count with MySQL.

    There are similar comments regarding PDO::lastInsertId. I've never had a problem with that and MySQL either.


    let me explain that in above function I designed it so it returns the row directly whenever I am looking for a single one

    I would advise against this because you have to know about this functionality when using the function. It can be convenient at times, but I think it would be easier to handle the result of the function transparently. That is to say, you should not have to inspect the return value to discover its type and figure out how to handle it.


    I wouldn't like those nasty "uncaught exception" errors

    Exception swallowing is bad. You should allow exceptions to propagate and appropriately handle them.

    Generally exceptions should not occur unless something catastrophic happens (MySQL error, unable to connect to the database, etc.) These errors should be very rare in production unless something legitimately happens to the server. You can display an error page to users, but at least make sure the exceptions are logged. During development, you probably want the exceptions to be as loud as possible so you can figure out exactly what to debug.

    I also think that names should be reasonably descriptive, so two functions named __database_row and _database_row are really confusing.


    IMPORTANT: what is the use of "PDOStatement::closeCursor" exactly?

    I doubt you will have to use this, so don't worry too much about it. Essentially it allows you to fetch from separate prepared statements in parallel. For example:

    $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();
    

    I could be wrong, but I don't think you need to do this for MySQL (i.e. you could call execute on both statements without calling closeCursor.


    can I really rely on the method I used to get the ID of latest insert?

    PDO's documentation on this (above) seems to be more forgiving about it than it is about rowCount and SELECT. I would use it with confidence for MySQL, but you can always just SELECT LAST_INSERT_ID().


    Am I doing it right?

    This is a difficult question to answer because there are so many possible definitions of right. Apparently your code is working and you are using PDO, so in a way you are. I do have some criticisms:

    global $Database;
    

    This creates a reliance on the declaration of a global $Database variable earlier in the script. Instead you should pass the database as an argument to the function. If you are an OOP fan, you could also make the database a property of the class that had this function as a method. In general you should avoid global state since it makes code harder to reuse and harder to test.

    the above function is intended to fetch a row from $_table table with $_id

    Rather than create a generic function for querying like this, it is better to design your application in a way that will allow you to run queries that serve specific purposes. I don't really see why you would want to select all columns for a table for a given ID. This is not as useful as it seems. Instead, you probably want to get specific columns from these tables, perhaps joined with other tables, to serve specific functions.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器