2013-12-20 25 views
2

我將如何使用MySQL的NOW()在PDO準備語句,或者我會怎麼解決辦法使用它,同時牢記可能Apache服務器和數據庫服務器可能有兩種稍微當前時間不匹配(幾秒鐘),或者在極少數情況下可能與時區分開?PDO使用MySQL的NOW()

我在我的代碼如下功能:

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查詢的刀片,因爲我已經封裝那些easyness的緣故,但我仍然希望能夠插入NOW()利用的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會有什麼價值?是否有可能給它沒有價值

+0

@Downvoter:護理解釋? – skiwi

回答

2

優秀的問題!

這是一個很好的例子,清楚地顯示了爲什麼所有這些衆多insert(), update()和所有其他的東西,無意取代SQL,都是錯誤的設計

NOW()是不是你將面臨的唯一問題。僅僅因爲SQL並不像第一眼那樣愚蠢的語言。它是故意發明的。幾十年來它的可靠性已經得到證明。意味着在學習PHP的同時將整個SQL寫成練習並不那麼容易。

所以,你能做的最好的事情是保持SQL原樣。

你真的需要的是一個輔助函數或兩個。自動執行重複任務。就這些。雖然SQL必須保持原樣。這將讓你使用它的所有電源,包括使用功能,功能與參數(!),查詢修飾語,如「INSERT忽略」或擴展語法類似的聯接。

如果您使用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

更新。

看看你在做什麼!

你正在策劃你的課簡化的東西。但目前你正在變得越來越複雜。最後,你將會擁有一個巨大的巨人,即使是創造者和其他人也不能理解衆多例外語法的不一致。而哪個仍然不讓你運行一些查詢。

請在您太遲之前重新考慮您的設計。

+0

我可能會固執,但我認爲使用** only **'insert()'我不打算爲其他功能編寫函數對於簡單的插入操作更容易,而NOW()是一個很小的值插入。我同意,但其他功能不應該被重寫。我也爲這個問題添加了我自己的'解決方案'(帶有一個bug),如果你能回答這個問題,我會很感激。 – skiwi

+0

繼續,繼續您的想法。祝你好運。 –

-1

你可以編寫一個包裝器,如果你傳遞一個包含你所需要的所有column => SQLfunction值的附加參數,就可以這樣做。

礦看起來像這樣:

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()) 
+0

另一天,我將需要用用戶提供的數據調用函數,如INET_ATON(:ip)。你的這種做法是無路可走的。 –

+0

這是一個方便的幫手功能。它可以自動執行一些重複任務。就這樣。我覺得它很有用。它並不妨礙我在需要時使用SQL的全部功能。 –

+0

更不用說它非常容易sql注入。 –