PHP pdo实例作为私有静态属性

时间:2013-11-07 22:54:22

标签: php database pdo static

我正在尝试基于OOP设计我的网站,但我遇到了如何设计数据库连接的问题。目前,我正在抽象类Connector中创建一个私有静态PDO对象。显然,任何需要与数据库交互的东西都会扩展这个类。我一直在翻转如何确保脚本中只有一个连接或PDO对象,因为某些页面需要多个扩展Connector的类。许多人似乎为此目的推荐了Singleton模式,但我目前这样做的方式似乎完成了同样的事情。

这是我目前的代码。

abstract class Connector
{
    private static $dbh;

    public function __construct()
    {
        try
        {
            self::$dbh = new PDO(...);
            self::$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
        catch(PDOException $e)
        {
            die($e->getMessage());
        }
    }

    public function getDB()
    {
        return self::$dbh;
    }
}

然后任何子类都会像这样使用它。

class Subclass extends Connector
{
    public function interactWithDB()
    {
        $stmt = $this->getDB()->prepare(...);
        // etc...
    }
}

理论上,我认为子类的每个实例应始终访问同一个PDO实例。这段代码是否真的有意义,或者我是否以某种方式误解了静态属性?这是不好的设计/实践,或者Singleton有更多的优势吗?

如果某些事情不明确,请评论,谢谢!

修改

Connector类并不仅仅是为了保存PDO对象而存在。它的析构函数关闭连接(使其为null)并且它包含isValueTaken等函数,它检查数据库中是否已有值。它具有以下抽象函数

abstract function retrieveData();
abstract function setData();

例如,我有一个扩展Connector的User类。它定义了setData()来在数据库中注册用户。我不知道这是否会对回应产生影响。

2 个答案:

答案 0 :(得分:7)

  

显然,任何需要与数据库交互的东西都会扩展这个类。

从OOP的角度来看,这没有任何意义。当某个类扩展另一个暗示“是一个”关系的类时。如果你走这条路线,你将很难不违反OCP这是SOLID中的一个字母。

  

我一直在翻转如何确保脚本中只有一个连接或PDO对象,因为某些页面需要多个扩展连接器的类。

轻松!只需创建一个实例。

  

许多人似乎为此目的推荐了Singleton模式,但我目前这样做的方式似乎完成了同样的事情。

很多像这样的人对OOP原则一无所知。使用单身人士只会引入一种“幻想”global instance / state

  

这段代码是否真的有意义,或者我是否以某种方式误解了静态属性?

老实说,这更多是对OOP的误解。

  

设计/实践不好,或者Singleton有更多优势吗?

见上文。


你应该做什么(在OOP中)inject the database connection到需要它的类中。这使您的代码松散耦合,从而使您的代码更易于维护,可测试,可调试和灵活。

另外,我真的不明白为什么需要为pdo连接创建数据库类,因为PDO API本身已经是OOP。因此,除非你有一个真正的理由为PDO写一个适配器(可能就是这种情况,因为有一些),我只是放弃它。

我的€0.02

-

回复您的修改:

  

Connector类并不仅仅是为了保存PDO对象而存在。它的析构函数关闭连接(使其为null)。

通常根本不需要关闭连接。处理请求后,连接将自动关闭(除非我们讨论持久连接)。

  

它包含isValueTaken等函数,用于检查数据库中是否已有值。它具有以下抽象函数

这听起来像是另一堂课的工作。

  

例如,我有一个扩展Connector的User类。它定义了setData()来在数据库中注册用户。我不知道这是否会对回应产生影响。

不,我的观点仍然存在。用户无需从数据库继承。这听起来不奇怪吗?从数据库继承的用户(我不想见到那个人)。如果需要,您应该将数据库连接注入用户。

答案 1 :(得分:3)

前言

单身方法通常不受欢迎。你将是set upon by raptors


您实际上要问的是如何配置连接并使其全局可用。这通常称为全局状态。您可以使用容器类和静态方法来实现此目的。

这是一个例子

namespace Persistence\Connection\Config;

interface PDOConfig {
    public function getDSN();
    public function getUsername();
    public function getPassword();
    public function getDriverOptions();
}

class MySqlConfig implements PDOConfig {
    private $username;
    private $password;
    private $db;
    private $host = 'localhost';
    private $charset = 'utf8';

    public function __construct($username, $password, $db) {
        $this->username = $username;
        $this->password = $password;
        $this->db = $db;
    }

    // getters and setters, etc

    public function getDSN() {
        return sprintf('mysql:host=%s;dbname=%s;charset=%s',
            $this->host, $this->db, $this->charset);
    }

    public function getDriverOptions() {
        return [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ];
    }
}

namespace Persistence\Connection;

use Persistence\Connection\Config\PDOConfig;

class Registry {
    private static $connection;
    private static $config;

    public static function setConfig(PDOConfig $config) {
        self::$config = $config;
    }

    public static getConnection() {
        if (self::$connection === null) {
            if (self::$config === null) {
                throw new RuntimeException('No config set, cannot create connection');
            }
            $config = self::$config;
            self::$connection = new \PDO($config->getDSN(), $config->getUsername(),
                $config->getPassword(), $config->getDriverOptions());
        }
        return self::$connection;
    }
}

然后,在应用程序执行周期的某个时间点(早期)

use Persistence\Connection\Config\MySqlConfig;
use Persistence\Connection\Registry;

$config = new MySqlConfig('username', 'password', 'dbname');
Registry::setConfig($config);

然后,您可以使用

Registry::getConnection();

代码中的任何位置都可以检索PDO实例。