2016-03-30 55 views
0

Iam試圖在PHP中創建基於OOP的論壇,並且目前正在致力於製作Database類。特別是Iam堅持爲Datatable類(使用PDO btw)製作「通用」插入類函數。在製作數據庫插入類功能時遇到困難

class DB 
      { 
       private $dbconn; 

       public function __construct(){ 

       } 

       protected function connect($dbname, $dbhost='127.0.0.1', $dbuser='root', $dbpass=''){ 

        try{ 
        $this->dbconn = new PDO("mysql:host=$dbhost;dbname=$dbname;", $dbuser, $dbpass, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'")); 
        } 

        catch(PDOException $e){ 
         echo 'Connection failed: '.$e->getMessage()."<br />"; 
        } 
       } 

       protected function disconnect(){ 
        $this->dbconn = null; 
       } 

       public function insert($dbname,){ 
        $this->connect($dbname); 

        try{ 
         # prepare 
         $sql = "INSERT INTO pdodemotable (firstname, lastname, age, reg_date) 
          VALUES (?, ?, ?, now())"; 
         $stmt = $dbconn->prepare($sql); 
         # the data we want to insert 
         $data = array($firstname, $lastname, $age); 
         # execute width array-parameter 
         $stmt->execute($data); 

         echo "New record created successfully"; 
        } 
        catch(PDOException $e){ 
         echo $sql . "<br>" . $e->getMessage(); 
        } 
       } 
      } 

插入功能就像你看到的未完成。我不知道如何讓插入函數適應任何數量的參數,任何數量的數據庫列和任何表。現在函數中的代碼取自我使用過程編程的其他項目之一。它第一次使用數據庫的OOP。

我是OOP和PDO的新手。必須有某種方法或功能可以幫助我彌補失蹤。我現在看到的唯一解決方案是使用一個ridicoulus數量的字符串處理,如果語句...它不能是最好的解決方案...必須有一個更簡單的方法...

回答

1

首先通知 - 你不需要$dbname參數插入方法,相反,它應該是一個構造函數的參數:

class DB { 

    private $dbconn; 

    public function __construct($dbname, $dbhost='127.0.0.1', $dbuser='root', $dbpass='') { 
     // also don't catch the error here, let it propagate, you will clearly see 
     // what happend from the original exception message 
     $this->dbconn = new PDO("mysql:host=$dbhost;dbname=$dbname;", $dbuser, $dbpass, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'")); 
    } 

    ... 

} 

至於insert方法 - 首先嚐試想象它將如何使用。 例如,它可以是這樣的:

$db = new DB('mydb'); 
$db->insert('mytable', array('firstname'=>'Pete', 'lastname'=>'Smith')); 

然後,可以通過表名和數據(鍵/值)進入方法:

public function insert($table, $data) { 
    // again, no need to try/catch here, let the exceptions 
    // do their job 
    // handle errors only in the case you are going to fix them 
    // and not just to ingnore them and 'echo', this can lead to much worse problems 
    // see the explanation below regarding the `backtick` method 
    $table = $this->backtick($table); 
    $fields = array(); 
    $placeholders = array(); 
    $values = array(); 
    foreach($data as $key=>$value) { 
     $fields[] = $this->backtick($key); 
     // you can also process some special values like 'now()' here 
     $placeholders[] = '?'; 
    } 
    $fields = implode($fields, ','); // firstname, lastname 
    $placeholders = implode($placeholders, ','); // ?, ? 
    $sql = "INSERT INTO $table ($fields) values ($placeholders)"; 
    $stmt = $this->dbconn->prepare($sql); 
    $stmt->execute(array_values($data)); 
} 


public function update($table, $id, $data) { 
    $table = $this->backtick($table); 
    $fields = array(); 
    foreach($data as $key=>$value) { 
     $fields[] = $this->backtick($key) . " = ?"; 
    } 
    $fields = implode($fields, ','); // firstname=?, lastname=? 
    $sql = "UPDATE $table SET $fields where id=?"; 
    $stmt = $this->dbconn->prepare($sql); 
    $data['id'] = $id; 
    $stmt->execute(array_values($data)); 
    if ($stmt->execute(array_values($data)) === false) { 
     print 'Error: ' . json_encode($stmt->errorInfo()). PHP_EOL; 
    } 
    while ($row = $stmt->fetchAll()) { 
     print json_encode($row) . PHP_EOL; 
    } 
} 

private function backtick($key) { 
    return "`".str_replace("`","``",$key)."`"; 
} 

另一種方法是創建單獨的對象這將代表一個表格行(ActiveRecord模式)。 它採用這樣的對象可能看起來像這樣的代碼:

$person = new Person($db); 
$person->firstName = 'Pete'; 
$person->lastName = 'Smith'; 
$person->save(); // insert or update the table row 

更新上可能存在SQL注入漏洞

我還添加了updatebacktick方法來說明可能的SQL注入。 沒有backtick,有可能update將像這樣的東西被稱爲:

$db->updateUnsafe('users', 2, array(
    "name=(SELECT'bad guy')WHERE`id`=1#"=>'', 
    'name'=>'user2', 'password'=>'text')); 

這將導致SQL語句是這樣的:

UPDATE users SET name=(SELECT'bad guy')WHERE`id`=1# = ?,name = ?,password = ? where id=? 

因此,而不是更新的用戶數據與id 2,我們它將更改ID爲1的用戶的名稱。 由於backtick方法,上述語句將失敗,Unknown column 'name=(SELECT'bad guy')WHERE ID =2#' in 'field list'Here is我測試的完整代碼。

無論如何,這可能不會保護您免受任何可能的SQL注入,所以最好不要將用戶輸入用於已知參數,如表名和字段名稱。

而不是做一些像$db->insert('mytable', $_POST),做$db->insert('mytable', array('first'=>$_POST['first']))

+1

[此代碼本質上容易受到SQL注入的攻擊。](https://phpdelusions.net/pdo/lame_update) –

+0

@YourCommonSense如果數據中的$ keys是來自用戶,那麼它很脆弱,就像我們執行'$ db-> insert('mytable',$ _POST)'並將整個$ _POST'數組作爲數據傳遞,對吧?但是,如果我們執行'$ db-> insert('mytable',array('first'=> $ _ POST ['first']))'',那麼它應該可以。這裏唯一能做的就是引用?我們可以爲表名和鍵添加$ this-> dbconn-> quote()來使其安全嗎? –

+0

不,我們不能使用字符串引用標識符,它會導致語法錯誤。至少您必須格式化標識符,但最好將其列入白名單。 –

-1

嘗試通過參數有一個數組,然後,在方法插入內,做一個foreach。

喜歡的東西:

$data['first_name'] = 'your name'; 
... 
$data['twentieth_name'] = 'twentieth name'; 

foreach($data as $key => $value) 
    $final_array[':'.$key] = $value; 

$stmt->execute($final_array);