php - PDO MySQL `prepared statements` 使用或不使用占位符

标签 php mysql oop pdo

我正在上 Web 开发类(class)。他们教我们使用 PDO 连接到 MySQL 数据库,然后在类中创建一些方法来访问 PDO 连接。

db_model.php

<?php
class DB {
    protected $db;
    function __construct() {  
        $this->db = new PDO("mysql:host=localhost;dbname=blog", "root", "");
    }  

    function executeQuery($query) {
        $statement = $this->db->prepare($query);    
        $statement->execute();
        return $statement;
    }
}

articles_model.php

<?php
require_once "db_model.php";

class ArticlesModel extends DB {
    function getAll() {
        $statement = $this->executeQuery("SELECT * FROM articles");
        return $statement->fetchAll(PDO::FETCH_ASSOC);
    }

    function getArticle($id) {
        $statement = $this->executeQuery("SELECT * FROM articles WHERE id = " . $id);
        return $statement->fetchAll(PDO::FETCH_ASSOC);
    }

    function insertArticle($article) {
        $this->executeQuery("INSERT into articles (title, body, image) values ('".$article["title"]."', '".$article["body"]."', '".$article["file"]."');");
        return $this->db->lastInsertId();
    }

    function updateArticle($article) {
        $statement = $this->executeQuery("UPDATE articles SET title ='".$article["title"]."',body = '".$article["body"]."' WHERE id =".$article["id"]);
        return $statement->rowCount();    
    }

    function deleteArticle($article) {
        $statement = $this->executeQuery("DELETE FROM articles WHERE id =".$article["id"]);
        return $statement->rowCount(); 
    }
}

我距离高级 PHP 程序员还很远,但据我所知,好的做法是使用带有占位符的真正准备好的语句,而不仅仅是在 SQL 语句中连接 PHP 变量,所以我提出了这个:

db_model.php

<?php

define('DB_NAME', 'blog');
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_CHAR', 'utf8');

class DB {
        protected $db;
        function __construct() {
                $opt  = array(
                        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
                        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                        PDO::ATTR_EMULATE_PREPARES   => TRUE
                );
                $dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
                $this->db = new PDO($dsn, DB_USER, DB_PASS, $opt);
        }

        function executeQuery($query) {
                $statement = $this->db->prepare($query);
                $statement->execute();
                return $statement;
        }
}

articles_model.php

<?php

require_once 'db_model.php';

class ArticlesModel extends DB {
        function getAll() {
                $statement = $this->executeQuery('SELECT * FROM articles');
                return $statement->fetchAll(PDO::FETCH_ASSOC);
        }

        function getArticle($id) {
                $statement = $this->executeQuery('SELECT * FROM articles WHERE id = :id');
                $statement->bindParam(':id', $id, PDO::PARAM_INT);
                return $statement->fetchAll(PDO::FETCH_ASSOC);
        }

        function insertArticle($article) {
                $statement = $this->executeQuery('INSERT into articles (title, body, image) values (:title, :body, :image)');
                $statement->bindParam(':title', $article['title'], PDO::PARAM_VAR);
                $statement->bindParam(':body', $article['body'], PDO::PARAM_VAR);
                $statement->bindParam(':image', $article['file'], PDO::PARAM_VAR);
                return $statement->lastInsertId();
        }

        function updateArticle($article) {
                $statement = $this->executeQuery('UPDATE articles SET title = :title, body = :body WHERE id = :id');
                $statement->bindParam(':title', $article['title'], PDO::PARAM_VAR);
                $statement->bindParam(':body', $article['body'], PDO::PARAM_VAR);
                $statement->bindParam(':id', $article['id'], PDO::PARAM_INT);
                return $statement->fetchColumn();
        }

        function deleteArticle($article) {
                $statement = $this->executeQuery('DELETE FROM articles WHERE id = :id');
                $statement->bindParam(':id', $article['id'], PDO::PARAM_INT);
                return $statement->fetchColumn();
        }
}

哪种方法使用起来更安全/更正确?我错了吗?顺便说一句,我本可以使用更简洁的代码,但希望我的代码尽可能接近老师的代码。 (例如,除了 execute(array()) 之外,不要在任何地方使用 bindparam() )

编辑:

我认为正确的是:

<?php

require_once 'db_model.php';

class ArticlesModel extends DB {
        function getAll() {
                $statement = $this->executeQuery('SELECT * FROM articles');
                return $statement->fetchAll(PDO::FETCH_ASSOC);
        }

        function getArticle($id) {
                $statement = $this->executeQuery('SELECT * FROM articles WHERE id = :id');
                $statement->bindParam(':id', $id, PDO::PARAM_INT);
                return $statement->fetchAll(PDO::FETCH_ASSOC);
        }

        function insertArticle($article) {
                $params = [
                        ':title' => $article['title'],
                        ':body' => $article['body'],
                        ':image' => $article['file']
                ];
                $statement = $this->executeQuery('INSERT into articles (title, body, image) values (:title, :body, :image)', $params);
                return $statement->lastInsertId();
        }

        function updateArticle($article) {
                $params = [
                        ':title' => $article['title'],
                        ':body' => $article['body'],
                        ':id' => $article['id']
                ];
                $statement = $this->executeQuery('UPDATE articles SET title = :title, body = :body WHERE id = :id', $params);
                return $statement->fetchColumn();
        }

        function deleteArticle($article) {
                $params = [':id' => $article['id']];
                $statement = $this->executeQuery('DELETE FROM articles WHERE id = :id', $params);
                return $statement->fetchColumn();
        }
}

最佳答案

虽然到目前为止使用准备好的语句更安全,但它们的真正好处在于将它们应用于 SQL 语句中的用户输入,从而保护您的代码免受 SQL 注入(inject)攻击。当您直接在 SQL 语句中键入值时,这些好处不会实现,因此准备好的语句并不是绝对必要的。不过,您仍然可以从重用查询中受益,这是准备好的语句的另一个好处。

话虽这么说,自始至终使用统一方法是良好的编程习惯,并且始终使用准备好的语句将有助于防止二阶 SQL 注入(inject)攻击。

关于php - PDO MySQL `prepared statements` 使用或不使用占位符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37235360/

相关文章:

php - AJAX 查询期间未设置 $_GET 值

MySQL 注释导致 Perl DBI 的绑定(bind)参数计数困惑

javascript - 将 json 从对象重新制作为数组?

c# - C#中继承对象的内存分配

oop - 一个存储库可容纳更多实体?

php - PHP从HTML中的select(来自数据库)选项中删除sqlite3中的数据

javascript - 如何根据值选项选择显示不同的输入框

php - 计算给定的开始日期和结束日期集的平均时间

更新前mysql触发错误

python - Python 中的类/OO 是一种好方法吗?