我正在尝试将 PDO 和 OOP 结合起来,但无法正常工作

Posted

技术标签:

【中文标题】我正在尝试将 PDO 和 OOP 结合起来,但无法正常工作【英文标题】:I'm trying to combine PDO and OOP, and can't get it to work 【发布时间】:2012-11-19 20:47:35 【问题描述】:

所以我基本上有两个文件。最后我会得到更多,但我想创建一个名为DB 的类,它将使用 PDO 数据库操作,然后我将扩展这个类以使我的所有函数都用于处理数据库。所以DB 类,然后将扩展为dbADD 类,它将具有针对不同数据库表的所有添加功能。

这叫config.php

<?php

DEFINE ('DBHOST', 'localhost');
DEFINE ('DBUSER', 'REMOVED');
DEFINE ('DBPSW', 'REMOVED');
DEFINE ('DBNAME', 'REMOVED');

class DB 
    public $db;
    private static $instance;   

    public function __constructor()
        $config ['db'] = array(
        'host'      =>  DBHOST,
        'username'  =>  DBUSER,
        'password'  =>  DBPSW,
        'dbname'    =>  DBNAME,
        );

        $this->db = new PDO('mysql:host ='  . $config['db']['host'] . ';dbname='  .   $config['db']['dbname'],$config['db']['username'],$config['db']['password']) ;
    

    public static function getInstance()
    
        if (!isset(self::$instance))
        
            $object = __CLASS__;
            self::$instance = new $object;
        
        return self::$instance;
    

    public function GetArticles ($search)
        $sql = "SELECT `FirstColumn`, `SrcColumn`, `article` FROM `test_table`  WHERE `FirstColumn` = 23";  

        //$dbs = new DB();
        $dbs = DB::getInstance();
        $query = $dbs->db->prepare($sql);
        //$query->bindValue(':search', $search, PDO::PARAM_INT);
        $query->execute();

        while ($row = $query->fetch(PDO::FETCH_OBJ)) 
            // = $row['article'],'</br>';
            $return = $row['article'];
           
        return $return;
    

?>

这个文件是我的测试文件,它并不重要,只是一个测试场。致电test.php

<?php
require_once('app_core/config.php');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Untitled Document</title>

    <link rel="stylesheet" href="/style/style.css" type="text/css" media="screen" />
</head>

<body>
    <?php
    $db = new DB();
    //echo $db->db;
    //echo $db->GetTestDB();
    //$test = $db->TestThis();
    //print_r($test);
    echo $db->GetArticles('23');
    ?>
</body>
</html>

如果可能的话,我还有另外两个担忧:第一个问题是安全问题——这是否是一种好习惯?另一个问题是如何隐藏带有此密码数据的文件,以便我可以使用它们但没有人可以读取它们?

【问题讨论】:

你认为的 OOP 不是 OOP。 OOP 不仅仅是把一些类放在一起。 【参考方案1】:

建议:

    如果可能,不要使用内联 SQL,因为如果您更改数据库的方案,假设您将列从 foo 重命名为 bar,您将必须找到所有使用 foo 的内联 SQL并将其更改为 bar。

    你的想法很好,对不同的动作类型有一个通用的行为,但是没有实现它的意义,因为这个好想法之前已经实现了。例如,您可以尝试 Flourish,它是一种 ORM,可以为您提供很多帮助。

    如果可以将密码存储在数据库中,请不要将密码存储在单独的文件中,但不要忘记加密密码并使用盐来改进加密。

【讨论】:

只想说第一点:我经常听到“改变数据库方案”的说法,但要成为魔鬼的拥护者,真的很难做到 ctrl+f 和 replace所有这些文字都有另一个名字?我只是看不到为那一件事使用 ORM 的理由。 这可能真的很难,因为它可以存在于不同的文件和不同的语言中。如果你的 foo 列被替换为 bar 并且你已经使用了 foo 列 10000 次,例如,'foo' 词出现在语义不同的字符串中并且在这些字符串中仍然有效,那么你的 Ctrl+F 方法是低效的。跨度> 也许吧,但为了争论,如果您提前计划 ORM,如果您需要替换 foo,您也可以计划使 ctrl+f 方法有效;) ORM 和替换计划有很大的不同。 ORM 是动态的并自动支持数据库方案更改,而如果您使用替换计划,则必须特别注意代码中的所有表和列名称,以简化将来的名称更改。有时不使用 ORM 是合理的,但是当您使用具有许多用例的大型数据库开发大型系统时,没有 ORM 可能会变得非常困难。 ORM 是编码恶魔的小工具,可以引诱你产生额外的依赖。【参考方案2】:

好的,这里有很多事情要做,所以我将尝试一次解决一个问题,以使这个类以面向对象的方式正常运行(而不是一组不完全-相关方法)。

首先,你的构造函数:

//  Make these private, will find out why in a moment...
private $db;
// __construct, not __constructor!!
private function __construct() 
    // This whole array doesn't serve any purpose because the constants
    // are defined and available in all scopes
    // while this array is local to the __construct().  
    // Just get rid of it and use the 
    // constants directly in the PDO connection
    //$config ['db'] = array(
    //    'host'          =>      DBHOST,
    //    'username'      =>      DBUSER,
    //    'password'      =>      DBPSW,
    //    'dbname'        =>      DBNAME,
    //);

    // Use some error checking when establishing your connection
    try 
      // Some extra bad whitespace removed around =
      $this->db = new PDO('mysql:host=' . DBHOST . ';dbname=' . DBNAME, DBUSER, DBPSW); 
     catch (PDOException $e) 
      echo 'Connection failed: ' . $e->getMessage();
    

接下来是你的单例访问器 getInstance()。

// No code changes necessary....
public static function getInstance()

    if (!isset(self::$instance))
    
        $object = __CLASS__;
        self::$instance = new $object;
    
    return self::$instance;

由于您已经定义了一个以单例方式访问该类的方法,因此将 $db 属性和 __construct() 设为 private。在任何时候你都不会调用$DB_class-instance = new DB() 来实例化它,或者调用$DB_class_instance-&gt;db 来直接访问连接。相反,您将调用DB::getInstance() 来访问单例实例,并调用GetArticles() 等方法来执行查询。

现在进入您的查询方法:

public function GetArticles ($search)
    // Ok a SQL string, no problem...
    $sql = "SELECT `FirstColumn`, `SrcColumn`, `article` FROM `test_table`  WHERE `FirstColumn` = :search";

    // There's no need for this.  You already defined $db as 
    // a class property, so you should be using $this->db
    // $dbs = DB::getInstance();

    $query = $this->db->prepare($sql);
    // bind the $search input parameter...
    $query->bindParam(':search', $search);

    // Test for success
    if ($query->execute()) 

      $row = $query->fetch(PDO::FETCH_OBJ) 
      // I suppose you know what you want here. If you're only expecting 
      // one article, there's no real need for the while loop.
      // You can just fetch() once.
      $return = $row->article;

      // OR.....

      // However, if you are expecting *multiple* rows, you should be accumulating them
      // into an array like this:
      $return = array();
      while ($row = $query->fetch(PDO::FETCH_OBJ)) 
         // Append to an array
         $return[] = $row->article;
         // OR to get multiple columns returned as an object...
         $return[] = $row;
      
      return $return;
   
   else 
     // Query failed, return false or something
     return FALSE;
   

最后是你的控制器代码:

// The constructor is private, so you can't do this
// $db = new DB();
// Instead you need to use getInstance()
$db = DB::getInstance();
// Returns an array, so print_r()
print_r($db->GetArticles('23'));

由于我们创建了类的$db 属性private,因此无法在类外访问它。因此,您需要为您计划运行的任何其他查询定义类似于GetArticles() 的查询方法。如果您认为有时需要构建非类方法的即席查询,那么您可以将其更改为

public $db

然后,您可以在类之外执行以下操作,而不必构建类方法来执行此操作。但是,您确实仍然需要致电getInstance()

$dbs = DB::getInstance();
// Run a query via the PDO connection $dbs->db
$result = $dbs->db->query('SELECT * FROM sometable');

风格小问题:

这实际上不会导致问题,因为标识符不区分大小写,但在风格上它很奇怪。 define() 是函数调用,通常使用小写:

define('DBHOST', 'localhost');
define('DBUSER', 'REMOVED');
define('DBPSW', 'REMOVED');
define('DBNAME', 'REMOVED');

关于您的文件安全

只要您的网络服务器配置正确,其他人就无法读取这些文件。如果 Web 服务器将 .php 文件发送到 PHP 解释器而不是将其内容转储到浏览器,那么这些文件是安全的。如果您在共享主机上,并且该主机没有将您的文件与其他租户正确隔离,那就是他们的问题,唯一好的解决方案是获得更好的主机。

但是,将敏感文件存储在 Web 服务器的文档根目录之上是明智的。然后,即使是配置错误的 Web 服务器也不会意外地将其内容转储到客户端。 PHP 只能通过include 访问它们。

【讨论】:

我会删除它:D 对不起,我不知道,答案在哪里详细说明。 非常感谢,它成功了,我非常感谢。我是这个网站的新手,但我非常喜欢它。 @MrNewbProgrammer 乐于助人。为了将来参考,很多时候,一个范围如此之大的问题不会得到很好的回应,并且可能会因为过于广泛而立即被社区关闭。最好一次解决一个小问题,但这里的顺序安排得很好,我可以解决它们以将其构建成工作秩序。 大声笑——真的,否决者? 2 天后出现并在不发表评论的情况下投反对票? 什么?我不明白你的意思?【参考方案3】:

由于 PDO 已经是一个对象,也许你根本不需要为它创建一个单例类。

相反,创建一个将 PDO 对象传递给的通用模型类。

<?php

class Model 
    private $pdo;

    __construct(PDO $pdo) 
        $this->pdo = $pdo
    

    // generic model methods go here

然后您可以对它进行子类化并充实您创建的每个模型的功能。

用法类似于:

$PDO = new PDO("mysql:host=DBHOST;dbname=DBNAME", DBUSER, DBPSW);
$myModel = new MyModel($pdo);
$bob = $myModel->getByName('bob');
$articles = new Articles($pdo);
$recentArticles = $articles->getRecent(new Date());

关于安全性,本文提供了一些不错的通用提示,http://www.tuxradar.com/practicalphp/17/0/0。事实上,整个指南都非常有帮助。

【讨论】:

以上是关于我正在尝试将 PDO 和 OOP 结合起来,但无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

无法将“每个”中的元素与节点属性结合起来

PDO dblib 无法将 ip 地址作为“主机”参数连接

尝试使用 PDO 连接到错误的数据库时 PHP 不显示错误

制作 PDO mysql 静态连接类的最佳方法?

将 PDO 与 MS SQL 结合使用“活动结果不包含任何字段”

PDO 从表中提取一列到一维数组中