2012-03-15 244 views
0

在爲我的新網站創建了一些功能之後,我很快意識到所有使用永久編程的包含文件都會失去控制,因此我決定學習並將當前編寫的函數轉換爲OOP類並將其從mysql轉換爲mysqli的。到目前爲止還沒有太過於糟糕的轉換,但我已經達到了需要多個查詢的功能,其中一個是在我更新之前檢查數據的SELECT。我迄今爲止所寫的內容,使用準備好的語句,像魅力一樣起作用,但它只做SELECT。我被卡住的地方是更新數據庫的時間。這是我到目前爲止有:mysqli(多)_query - 如何做和執行循環(S)?

public function ban() { 
    $connection = Database::getConnection(); 
    $user_id = $_POST['user']; 
    $feedback = ''; 
    $query = "SELECT banned, user_name 
      FROM users 
      WHERE user_id = ?"; 
    $stmt = $connection -> prepare($query); 

    // Check for a multi-user selection on POST 
    if (count($user_id) > 1) { 
    foreach ($user_id as $value) { 
     $stmt -> bind_param('i', $value); 
     if (!$result = $stmt -> execute()) { 
      $feedback .= "Did not execute."; 
     } else { 
      $stmt -> bind_result($banned, $user); 
      $stmt -> fetch(); 
      if ($banned == 1) { 
       $feedback .= $user . " is already banned.<br />"; 
      } else { 
       // This is where I need the code to update the database 
       // with the users who aren't already banned. 
      } 
     } 
    } 
    $stmt -> close(); 
    return $feedback; 
    } else { 
    // This is where the UPDATE will be for a single person ban 
    // if only one was selected on POST 
    } 
} 

我能創建/執行第二預處理語句的UPDATE注射,運行在一節的另一循環內我需要的代碼或者這將是最好避免,或甚至可以工作嗎?我確信mysqli_multi_query可能是最好的方法,不得不重新編寫函數,因爲我發現(在寫了這麼多的函數之後),你不能使用準備好的語句和multi_query注入。重寫不是什麼大不了的事,但是使用multi_query的幫助很少,而且很少。 PHP網站有很多文檔,但說實話,這很讓人困惑。

更新查詢會是這個樣子:

$explain = mysql_real_escape_string($_POST['banExplain']); 
UPDATE users 
SET banned = '1', ban_reason = '$explain' 
WHERE user_id = '$value'" 

任何幫助,這將不勝感激。希望我解釋清楚我需要做什麼。如果沒有,請告訴我。謝謝!

回答

1

該代碼示例仍然非常程序化。

您應該有一個方法來檢查用戶是否是user_banned,以及禁用用戶的方法。

user_banned方法應該帶一個user_id並返回一個布爾值。

ban_user方法應該有一個原因和user_id。

應該有另一個功能或方法做循環。

將user_id作爲數組強制轉換,您可以執行一個循環。

使用異常來處理錯誤。

<?php 
    //the model 
    ... 

    public function update_users (array $user_ids) 
    { 
     $result = array();   

     foreach ($user_ids as $user_id) { 
      if (!$this->user_banned($user_id)) { 
       $this->ban_user($user_id, $reason); 

      } else { 
       $result[$user_id] = "already banned"; 
      } 

     return $result; 
    } 
    ... 

    //the controller 

    //prevent XSS, cast as integers or use filter_var or something 
    $user_ids = sanitize((array) $_POST['user']); 
    try { 
     $result = $obj->update_users($user_ids); 
    } catch (Exception $e) { 
    ... 
+0

您的權利,代碼仍然非常程序化。發佈這個問題後,我想出了一個解決方案,雖然不是更多的OOP風格,只是一個更長的版本(請參閱我的答案回覆。)但它的工作原理。現在我會接受你的建議,看看我是否可以把它分解成更小,更多的OOP風格的功能。 – 2012-03-16 00:48:42

0

當我重新閱讀我的問題時,我想到了使用第二個準備好的語句進行更新。首先,我試圖在最初的foreach循環中包含第二條語句,但是這個錯誤出現了,大概是因爲您一次只能打開一個以上的語句。接下來我決定將每次迭代時的值(被檢查的user_id)存儲到一個單獨的數組中,然後在關閉最初的foreach循環和準備好的語句之後,我使用UPDATE的新準備語句運行第二個循環,並且瞧!下面是代碼,因爲它現在看起來:

public function ban() { 
    $connection = Database::getConnection(); 
    $user_id = $_POST['user']; 
    $reason = $_POST['banExplain']; 
    $update = array(); 
    $feedback = ''; 
    $query = "SELECT banned, user_name 
      FROM users 
      WHERE user_id = ?"; 
    $query2 = "UPDATE users 
      SET banned = '1', ban_reason = ? 
      WHERE user_id = ?"; 
    $stmt = $connection -> prepare($query); 
    $stmt2 = $connection -> prepare($query2); 

    if (count($user_id) > 1) { 
    foreach ($user_id as $value) { 
     $stmt -> bind_param('i', $value);    
     if (!$result = $stmt -> execute()) { 
      $feedback .= "Did not execute search.<br />"; 
     } else { 
      $stmt -> bind_result($banned, $user); 
      $stmt -> fetch(); 
      if ($banned == 1) { 
       $feedback .= $user . " is already banned.<br />"; 
      } else { 
       $update["$user"] = $value; // Populate an array to pass to update loop 
      } 
     } 
    } 
    $stmt -> close(); 

    // Run update query - $key from $update var is the user name 
    foreach ($update as $key => $value) { 
     $stmt2 -> bind_param('si', $reason, $value); 
     if (!$result2 = $stmt2 -> execute()){ 
      $feedback .="Did not execute update.<br />"; 
     } else { 
      $feedback .= $key . " is banned.<br />"; 
     } 
    } 
    $stmt2 -> close(); 
    return $feedback; 
    } else { 

    // Executes for single selection requests 
    $stmt -> bind_param('i', $user_id); 
    if (!$result = $stmt -> execute()) { 
     $feedback .= "Did not execute search.<br />"; 
    } else { 
     $stmt -> bind_result($banned, $user); 
     $stmt -> fetch(); 
     if ($banned == 1) { 
      $feedback .= $user . " is already banned.<br />"; 
     } else { 
      $update["$user"] = $user_id; 
     } 
    } 
    $stmt -> close(); 
    // Runs loop simply for the user name in the $key var 
    foreach ($update as $key => $value) { 
     $stmt2 -> bind_param('si', $reason, $value); 
     if (!$result2 = $stmt2 -> execute()){ 
      $feedback .="Did not execute update.<br />"; 
     } else { 
      $feedback .= $key . " is banned.<br />"; 
     } 
    } 
    $stmt2 -> close(); 
    return $feedback; 
    } 

}

相當長的什麼似乎是這樣一個簡單的任務,我想的是,mysqli::multi_query很可能還是去了解的最佳方式這個(在使用mysqli::real_escape添加所需的安全性之後),但除了這個函數的龐大程度之外,它可以工作,並且在保持服務器請求成本最小的情況下是安全的(從我所知道的)。

我現在要採取user934258的建議,並嘗試將其分解爲更小,更容易消化和維護類功能。如果/當我完成時,我會在這裏跟進以顯示結果,希望能幫助他人擺脫同樣的困境。如果其他人有一些好的解決方案,請讓我知道。我總是試圖提高我的編碼

謝謝大家,希望這至少可以幫助其他人在相同的情況。


好吧,thanks to user934258我回去重新檢查了這個/這些方法的編碼。通過聽取他的建議,我能夠從禁止方法中刪減大約25行代碼。但是,只需要添加幾行代碼,並且通過/注入額外的參數,我就能夠使代碼多用途併入unban方法,從而移除大約77行不需要的重複代碼(unban是除了它在DB中檢查了Zero而不是One之外,並且顯示了稍微不同的響應。)對於將來可能會發現此主題的人,這裏是我的代碼的最終產品。

方法從腳本調用:

public function ban($arg) {    // 0 = Unban 1 = Ban 
    $this -> user_id = $_POST['user'];  // initially set user id as global variable 
    $user_id = $this -> user_id; 
    $this -> banReason = mysqli_real_escape_string($_POST['banExplain']); 

    if (!$this -> check_ban($user_id, $arg)) { 
    $this -> feedback .= Admin::CONNFAIL; 
    return $this -> feedback;   // Returned connection failure on select 
    } elseif (!$this -> ban_user($arg)) { 
    $this -> feedback .= Admin::UPFAIL; 
    return $this -> feedback;   // Returned connection failure on update 
    } else { 
    return $this -> feedback; 
    } 
} 

方法來檢查DB禁令值(S):

private function check_ban($user_id, $arg) { 
    $connection = Database::getConnection(); 
    $query = "SELECT banned, user_name 
      FROM users 
      WHERE user_id = ?"; 
    $stmt = $connection -> prepare($query); 

    foreach ($user_id as $value) { 
    $stmt -> bind_param('i', $value); 
    if (!$result = $stmt -> execute()) { 
     return FALSE; 
    } else { 
     $stmt -> bind_result($banned, $user); 
     $stmt -> fetch(); 
     if ($arg == 1 && $banned == 1) { 
      $this -> feedback .= $user . " is already banned.<br />"; 
     } elseif ($arg == 0 && $banned == 0) { 
      $this -> feedback .= $user . " is not currently banned.<br />"; 
     } else { 
      $this -> update["$user"] = $value; // Populate array to be un/banned 
     } 
    } 
    } 
    $stmt -> close(); 
    return TRUE; 
} 

最後的方法來更新數據庫與聯合國/禁令:

private function ban_user($arg) { 
    $connection = Database::getConnection(); 
    $update = $this -> update; 
    $reason = $this -> banReason; 
    $query = "UPDATE users 
      SET banned = ?, ban_reason = ? 
      WHERE user_id = ?"; 
    $stmt = $connection -> prepare($query); 

    foreach ($update as $key => $value) { 
    $stmt -> bind_param('isi', $arg, $reason, $value); 
    if (!$result = $stmt -> execute()) { 
     return FALSE; 
    } elseif ($arg == 0) { 
     $this -> feedback .= $key . " is unbanned.<br />"; 
    } else { 
     $this -> feedback .= $key . " is banned.<br />"; 
    } 
    } 
    $stmt -> close(); 
    return TRUE; 
} 

請注意,這些方法中很少發生錯誤檢查/過濾/消毒。我有幾個原因:在腳本中檢查變量的值,我是唯一被授予這種權限的人,並且像$user_id這樣的變量值被預填充到複選框和下拉列表中,從數據庫的價值,所以錯誤$user_id的機會是渺茫的。

正如我在其他文章中提到的,如果任何人都可以提供更好的解決方案,我已經完成了,請讓我知道。

感謝這樣一個偉大的社區!