我正在尝试将PDO和OOP结合起来,并且无法使其工作

时间:2012-12-02 01:02:27

标签: php mysql security oop pdo

所以我基本上有两个文件。最后我会有更多,但我想创建一个名为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>

如果有可能,我还有另外两个问题:第一个问题是证券问题 - 这是一个好的做法吗?另一个问题是我如何用这个密码数据隐藏文件,所以我可以使用它们,但没有人可以阅读它们?

3 个答案:

答案 0 :(得分:3)

好的,你在这里有很多事情要做,所以我会尝试一次解决一个问题,使这个类行为正确,并以面向对象的方式(而不是一个非完整的集合)相关方法)。

首先,你的构造函数:

//  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->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服务器配置正确,其他任何人都无法读取这些文件。如果Web服务器将.php文件发送到PHP解释器而不是将其内容转储到浏览器,则文件是安全的。如果您在共享主机上并且主机没有正确地将您的文件与其他租户隔离,那么这就是他们的问题,唯一的好办法就是获得更好的主机。

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

答案 1 :(得分:2)

由于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。事实上,整个指南非常有用。

答案 2 :(得分:1)

建议:

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

  2. 你的想法很好,对于不同的动作类型有一般的行为,但没有实现它的意义,因为之前实现了这个好主意。例如,你可以试试Flourish,这是一个ORM,可以帮助你很多。

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