PHP单例设计PDO

PHP Singleton design PDO

多年来,我一直在我的网站上使用 PDO 的单例设计 PHP5。 我现在正在迁移到 PHP7,我在我的 Apache 日志文件中看到错误:

PHP Notice:  Accessing static property Db::$PDOInstances as non static in /var/www/class/class_db_test.php on line 12
PHP Notice:  Accessing static property Db::$PDOInstances as non static in /var/www/class/class_db_test.php on line 13
PHP Notice:  Accessing static property Db::$PDOInstances as non static in /var/www/class/class_db_test.php on line 32

这是我的单例 class 用于数据库访问:

<?php
require_once(dirname(__FILE__) . "/../setting.php");

class Db {
    private static $debug = 1;
    private static $debugPath = "/tmp/sql_debug.log";
    private static $PDOInstances = array("db1"=>"","db2"=>"");

    private function __construct($db_type){
        switch($db_type) {
            case "db1":
                $this->PDOInstances[$db_type] = new PDO('mysql:dbname='.DB_NAME.';host='.DB_HOST,DB_USER ,DB_PWD,array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
                $this->PDOInstances[$db_type]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
            break;
            case "db2":
                $this->PDOInstances[$db_type] = new PDO('mysql:dbname='.DB2_NAME.';host='.DB2_HOST,DB2_USER ,DB2_PWD,array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
                $this->PDOInstances[$db_type]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
            break;
        }
    }
    public static function getInstance($db_type) {
        if(self::$debug == 1){self::sqlDebug("connection","");}

        if(self::$PDOInstances[$db_type] == null){
            self::$PDOInstances[$db_type] = new self($db_type);
        }
        return self::$PDOInstances[$db_type];
    }

    public function query($db_type,$query){
        if(self::$debug == 1){self::sqlDebug("read request",$query);}
        return $this->PDOInstances[$db_type]->query($query);
    }

    public function exec($db_type,$query){
        if(self::$debug == 1){self::sqlDebug("request",$query);}  
        return $this->PDOInstances[$db_type]->exec($query);
    }

    public function lastInsertId($db_type){
        return $this->PDOInstances[$db_type]->lastInsertId();
    }

    public function quote($db_type,$string){
        if(self::$debug == 1){self::sqlDebug("quote",$string);} 
        return $this->PDOInstances[$db_type]->quote($string);
    }

    public static function sqlDebug($_mode,$_query){
        $today = date("m.d.y-H:m:s"); 
        if($_mode == "connection"){
            $line = $today." Connection: '".$_query."'\r\n";
        }
        else if($_mode == "quote"){
            $line = $today." Quote : '".$_query."'\r\n";
        }
        else if($_mode == "request"){
            $line = $today." Request : '".$_query."'\r\n";
        }
        else if($_mode == "read request"){
            $line = $today." Read request : '".$_query."'\r\n";
        }

        $file_debug = fopen(self::$debugPath, "a+");
        fwrite($file_debug, $line);
        fflush($file_debug);
        fclose($file_debug);
    }

}

?>

还有我的测试代码:

<?php
error_reporting(E_ALL); 

require_once(dirname(__FILE__) . "/setting.php");
require_once(dirname(__FILE__) . "/class/class_db_test.php");

$con =  Db::getInstance("db1");
$res = $con->query("db1","SELECT userId from user WHERE userName='test'");

if($res->rowCount() == 1){
    $line = $res->fetchAll();
    $res->closeCursor();
    echo $line[0]['userId'];
}
else{
    echo "0";
}

?>

当我在第 12、13 和 32 行将 $this 更改为 self:: 时,出现以下错误:

PHP Warning:  Missing argument 2 for Db::query(), called in /var/www/class/class_db_test.php on line 32 and defined in /var/www/class/class_db_test.php on line 30
PHP Notice:  Undefined variable: query in /var/www/class/class_db_test.php on line 31
PHP Notice:  Undefined index: SELECT userId from user WHERE userName='test' in /var/www/class/class_db_test.php on line 32
PHP Fatal error:  Uncaught Error: Call to a member function query() on null in /var/www/class/class_db_test.php:32\nStack trace:\n#0 /var/www/class/class_db_test.php(32): Db->query('SELECT userId f...')\n#1 /var/www/test.php(8): Db->query('db1', 'SELECT userId f...')\n#2 {main}\n  thrown in /var/www/class/class_db_test.php on line 32

关于如何调整我的代码以使其在 PHP7 上运行,您有什么想法吗?

谢谢

我能看到的主要内容之一是您以两种不同的方式使用 PDOInstances。它用于 Db class 的实例列表(在 getInstance() 静态方法中设置)。但它似乎也是您尝试为数据库存储 PDO 实例的地方(在构造函数中)。

如果您更改用于存储数据库的变量(Db class),那么这至少意味着您拥有两条数据...

private $PDOInstances = array("db1"=>"","db2"=>"");  // Remove static
private static $DbInstances = array("db1"=>"","db2"=>"");


public static function getInstance($db_type) {
    if(self::$debug == 1){self::sqlDebug("connection","");}

    if(self::$DbInstances[$db_type] == null){
        self::$DbInstances[$db_type] = new self($db_type);
    }
    return self::$DbInstances[$db_type];
}

我也不确定你为什么打电话

$con =  Db::getInstance("db1");

然后您需要将数据库类型传递给所有其他方法...

$res = $con->query("db1","SELECT userId from user WHERE userName='test'");

当然这应该由使用数据库类型调用的连接暗示。

如果在构造函数中你刚刚做了(设置 PDOInstances 没有数组部分)

$this->PDOInstances = new PDO('mysql:dbname='.DB_NAME.';host='.DB_HOST,DB_USER ,DB_PWD,array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));

那么数据库的所有访问都是通过这个完成的...

public function query($query){
    if(self::$debug == 1){self::sqlDebug("read request",$query);}
    return $this->PDOInstances->query($query);
}

考虑到 Nigel Ren 的回答,这里是修改后的 Db class 文件:

<?php
require_once(dirname(__FILE__) . "/../setting.php");

class Db {
    private static $debug = 1;
    private static $debugPath = "/tmp/sql_debug.log";
    private $PDOInstances = "";  // Remove static
    private static $DbInstances = array("db1"=>"","db2"=>"");

    private function __construct($db_type){
        switch($db_type) {
            case "db1":
                $this->PDOInstances = new PDO('mysql:dbname='.DB_NAME.';host='.DB_HOST,DB_USER ,DB_PWD,array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
                $this->PDOInstances->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
            break;
            case "db2":
                $this->PDOInstances = new PDO('mysql:dbname='.DB2_NAME.';host='.DB2_HOST,DB2_USER ,DB2_PWD,array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
                $this->PDOInstances->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
            break;
        }
    }
    public static function getInstance($db_type) {
        if(self::$debug == 1){self::sqlDebug("connection","");}

        if(self::$DbInstances[$db_type] == null){
            self::$DbInstances[$db_type] = new self($db_type);
        }
        return self::$DbInstances[$db_type];
    }

    public function query($query){
        if(self::$debug == 1){self::sqlDebug("read request",$query);}
        return $this->PDOInstances->query($query);
    }

    public function exec($query){
        if(self::$debug == 1){self::sqlDebug("request",$query);}  
        return $this->PDOInstances->exec($query);
    }

    public function lastInsertId(){
        return $this->PDOInstances->lastInsertId();
    }

    public function quote($string){
        if(self::$debug == 1){self::sqlDebug("quote",$string);} 
        return $this->PDOInstances->quote($string);
    }

    public static function sqlDebug($_mode,$_query){
        $today = date("m.d.y-H:m:s"); 
        if($_mode == "connection"){
            $line = $today." Connection: '".$_query."'\r\n";
        }
        else if($_mode == "quote"){
            $line = $today." Quote : '".$_query."'\r\n";
        }
        else if($_mode == "request"){
            $line = $today." Request : '".$_query."'\r\n";
        }
        else if($_mode == "read request"){
            $line = $today." Read request : '".$_query."'\r\n";
        }

        $file_debug = fopen(self::$debugPath, "a+");
        fwrite($file_debug, $line);
        fflush($file_debug);
        fclose($file_debug);
    }

}

?>

和测试文件:

<?php
error_reporting(E_ALL); 

require_once(dirname(__FILE__) . "/setting.php");
require_once(dirname(__FILE__) . "/class/class_db_test.php");

$con =  Db::getInstance("db1");
$res = $con->query("SELECT userId from user WHERE userName='test'");

if($res->rowCount() == 1){
    $line = $res->fetchAll();
    $res->closeCursor();
    echo $line[0]['userId'];
}
else{
    echo "0";
}

?>

一切正常,没有 PHP Notice/Error。

谢谢!