2016-03-30 56 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);