【问题标题】:PDO Use MySQL's NOW()PDO 使用 MySQL 的 NOW()
【发布时间】:2013-12-20 12:01:21
【问题描述】:

我将如何在 PDO 准备语句中使用 MySQL 的 NOW(),或者我将如何使用它来解决问题,同时牢记 Apache 服务器和数据库服务器可能有轻微的当前时间不匹配(几秒),或者在在极少数情况下可能是时区不同?

我的代码中有以下函数:

try {
    $dbh->insert("users", array(
        "email" => $email,
        "password" => $password,
        "salt" => $salt,
        "ingame" => $ingame,
        "kiosk" => $kiosk
    ));
} catch (PDOException $ex) {
    error($ex);
}

调用者:

/**
 * Inserts data into a table. Data must be given in key-value pairs.
 * 
 * Example: $dbh->insert("table", array(
 *                                  "data1" => $data1,
 *                                  "data2" => $data2
 *                                  );
 * 
 * @param type $table   The table to insert to
 * @param type $keyvaluepairs   The key-value pairs.
 * @return type The statement that this query produced.
 */
public function insert($table, $keyvaluepairs) {
    $sql = "INSERT INTO `{$table}` (";
    $values_sql = ") VALUES(";
    $values = array();
    foreach ($keyvaluepairs as $key => $value) {
        $sql .= "`${key}`, ";
        $values_sql .= "?, ";
        $values[] = $value;
    }
    $query = substr($sql, 0, -2).substr($values_sql, 0, -2).")";
    return $this->query($query, $values);
}

调用者:

//TODO update documentation to show it also handles associative arrays with bindvalue
/**
 * Can be called to create a query. Use either unnamed or named placeholders for the prepared statements.
 * 
 * Example: $dbh->query("INSERT INTO table (data1, data2) VALUES(?, ?)", array($data1, $data2));
 * 
 * @param type $query   The input query, including unnamed or named placeholders
 * @param type $values  The input values. If it's not an array, then it will be an one-element array
 * @return type The statement constructed by this query
 */
public function query($query, $values = array()) {
    if (!is_array($values)) {
        $values = array($values);
    }
    $statement = $this->dbh->prepare($query);
    $statement->setFetchMode(PDO::FETCH_OBJ);
    $i = 1;
    if (is_assoc($values)) {
        foreach ($values as $key => $value) {
            $statement->bindValue($key, $value);
        }
    }
    else {
        foreach ($values as $value) {
            $statement->bindValue($i++, $value);
        }
    }
    $statement->execute();
    return $statement;
}

我有这个功能的地方:

function is_assoc($array) {
    return (bool)count(array_filter(array_keys($array), 'is_string'));
}

所以这里的交易是我不能对插入使用自定义 MySQL 查询,因为为了方便起见我已经封装了这些查询,但我仍然希望能够插入 NOW() em> 利用TIMESTAMP / CURRENT_TIMESTAMP()

我希望您理解这个问题需要一个解释性的答案,因为我已经阅读了“正常”的答案并表明它们不能满足我的需求。

更新:我已将const SQL_NOW = 1; 添加到我的DBH 课程中,但是现在我想将insert 修改为如下内容:

public function insert($table, $keyvaluepairs) {
    $sql = "INSERT INTO `{$table}` (";
    $values_sql = ") VALUES(";
    $values = array();
    foreach ($keyvaluepairs as $key => $value) {
        if ($value == SELF::SQL_NOW) {
            $sql .= "NOW(), ";
        }
        else {
            $sql .= "`${key}`, ";
            $values_sql .= "?, ";
            $values[] = $value;
        }
    }
    $query = substr($sql, 0, -2).substr($values_sql, 0, -2).")";
    return $this->query($query, $values);
}

这可能是一个合适的解决方案,但是我不能将1 用作SQL_NOW 值,因为如果我想插入一个整数1,它会失败。如果我采用这个解决方案,那么SQL_NOW 会有什么价值?甚至可以给它没有价值吗?

【问题讨论】:

  • @Downvoter:需要解释一下吗?

标签: php mysql sql pdo


【解决方案1】:

很好的问题!

这是一个完美的例子,它清楚地说明了为什么所有这些众多的insert(), update() 和所有其他旨在替代 SQL 的东西在设计上是错误的

NOW() 不是您将面临的唯一问题。只是因为SQL 并不是一门乍看之下那么愚蠢的语言。它是故意发明的。几十年来,它的可靠性得到了证明。意味着在学习 PHP 的同时编写整个 SQL 作为练习并不容易。

因此,您能做的最好的事情就是保持 SQL 不变。

您真正需要的是一两个辅助函数。使重复性任务自动化。就这些。而 SQL 必须保持原样。这将让您充分利用它的所有功能,包括使用函数、带参数的函数(!)、查询修饰符(例如“INSERT IGNORE”)或扩展语法(例如 JOINS)。

如果您使用的是 PDO,它不是那么简单但可行。

但是,唯一合适的解决方案是在 SET 语句中使用特殊类型的占位符

$data = array(
        "email"    => $email,
        "password" => $password,
        "salt"     => $salt,
        "ingame"   => $ingame,
        "kiosk"    => $kiosk,
);
$dbh->query("INSERT INTO ?n SET reg = NOW(), ?u","users", $data);

只需一行就可以解决所有这些混乱和许多、许多、许多其他问题。
只需一个 query() 方法即可运行您想要的任何查询,甚至是 REPLACE INTO

更新。

看看你在做什么!

你正在计划你的课程来简化事情。但现在你让它变得越来越复杂。最后,您将拥有一个笨重的巨人,其语法不一致,即使它的创建者和其他人也几乎无法理解众多异常。哪些仍然不允许您运行一些查询。

请在为时已晚之前重新考虑您的设计。

【讨论】:

  • 我可能比较固执,但我认为only insert()我不打算为其他功能编写函数对于琐碎的插入来说更容易,NOW()是要插入的平凡值。我确实同意不应该重写所有其他功能。我还在这个问题中添加了我自己的“解决方案”(有一个错误),如果您也能回答这个问题,我将不胜感激。
【解决方案2】:

如果您向它传递一个包含您需要的所有列=>SQL 函数值的附加参数,您可以编写一个包装器来执行此操作。

我的看起来像这样:

function pInsertFunc($action, $table, $values, $sqlfunctions)
{

    global $pdb;

    // There's no way to pass an SQL function like "NOW()" as a PDO parameter,
    // so this function builds the query string with those functions.  $values
    // and $sqlfunctions should be key => value arrays, with column names
    // as keys.  The $values values will be passed in as parameters, and the
    // $sqlfunction values will be made part of the query string.

    $value_columns = array_keys($values);
    $sqlfunc_columns = array_keys($sqlfunctions);
    $columns = array_merge($value_columns, $sqlfunc_columns);

    // Only $values become ':paramname' PDO parameters.
    $value_parameters = array_map(function($col) {return (':' . $col);}, $value_columns);
    // SQL functions go straight in as strings.
    $sqlfunc_parameters = array_values($sqlfunctions);
    $parameters = array_merge($value_parameters, $sqlfunc_parameters);

    $column_list = join(', ', $columns);
    $parameter_list = join(', ', $parameters);

    $query = "$action $table ($column_list) VALUES ($parameter_list)";

    $stmt = $pdb->prepare($query);
    $stmt->execute($values);

}

像这样使用它:

$values = array(
    'ID' => NULL,
    'name' => $username,
    'address' => $address,
);

$sqlfuncs = array(
    'date' => 'NOW()',
);

pInsertFunc("INSERT INTO", "addresses", $values, $sqlfuncs);

得到的查询字符串如下所示:

INSERT INTO addresses (ID, name, address, date) VALUES (:ID, :name, :address, NOW())

【讨论】:

  • 另外一天,我需要使用用户提供的数据调用一个函数,例如 INET_ATON(:ip)。你的这种做法是一条无路可走的路。
  • 这是一个方便的辅助函数。它可以自动执行一些重复性任务。就这样。我觉得它很有用。它并不妨碍我在需要时使用 SQL 的全部功能。
  • 更不用说它非常容易被sql注入。
  • 如果您允许用户在 $values 中定义键,那么它只会容易发生 SQL 注入,您可以看到我在这里没有这样做。 $values 中的所有值都将安全地传递到数据库,没有 SQL 注入的风险。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 2011-09-21
  • 2011-04-22
  • 2014-10-28
相关资源
最近更新 更多