PHP pdo 实例作为私有静态属性
Posted
技术标签:
【中文标题】PHP pdo 实例作为私有静态属性【英文标题】:PHP pdo instance as private static property 【发布时间】:2013-11-19 19:59:26 【问题描述】:我正在尝试基于 OOP 设计我的网站,但我在如何设计数据库连接方面遇到了麻烦。目前,我正在一个抽象类 Connector 中创建一个私有静态 PDO 对象。显然,任何需要与数据库交互的东西都会扩展这个类。我一直在反复讨论如何确保脚本中只有一个连接或 PDO 对象,因为有些页面需要多个扩展连接器的类。许多人似乎为此目的推荐单例模式,但我目前的做法似乎完成了同样的事情。
这是我当前的代码。
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 有更多的优势?
如果有不清楚的地方请评论,谢谢!
编辑:
连接器类的存在并不是为了保存 PDO 对象。它的析构函数关闭连接(使其为空)并包含诸如 isValueTaken 之类的函数,用于检查数据库中是否已存在值。它具有以下抽象功能
abstract function retrieveData();
abstract function setData();
例如,我有一个扩展连接器的用户类。它定义了 setData() 在数据库中注册用户。我不知道这是否会对响应产生影响。
【问题讨论】:
“显然,任何需要与数据库交互的东西都会扩展这个类。” ---一点都不明显。你需要用勺子,你不需要成为勺子,对吗?提示:google 依赖注入和委托。 PS:pimple.sensiolabs.org 我建议改用this approach 之类的东西。 @zerkms pimple 实现了一个服务提供者,这是另一种反模式。并且服务提供者不是 DI容器。 @tereško:周围有任何“真正的”开源 php DI 容器可用吗? @tereško:我确实意识到了不同之处。我可以忍受 sf2 服务容器的“限制” 【参考方案1】:如果有人正在阅读本文,请注意,如果有人使用 Phil 的上述代码 sn-p,请记住在 getDriverOptions() 中的 PDO 前面使用黑色斜线,以便引用全局命名空间。它应该看起来像这样。
public function getDriverOptions()
return [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => false,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
];
【讨论】:
【参考方案2】:前言
单例方法通常不受欢迎。你将成为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 实例。
【讨论】:
“我知道有时单例是一个肮脏的词,但无论如何,你需要某种全局状态。你也可以使用注册表模式,但这完全取决于你。”呜?没有也没有。 @PeeHaa OP 询问如何配置全局状态。查看任何流行的框架(而不仅仅是 PHP),您会看到某种形式的静态连接池/注册表。否则,您需要实现服务定位器或 DI 容器,这是一项更大的任务 OP 正在询问 OOP。单例和注册表都与 OOP 无关。是的,你是对的,大多数框架也与 OOP 没有太大关系。 @Phil 带有“故意”的静态注册表是单例。 @tereško 我想说它不是一个单例,而是一个静态的、延迟加载的实例化器。然后我意识到,这实际上是一个单身人士:)【参考方案3】:显然,任何需要与数据库交互的东西都会扩展这个类。
从 OOP 的角度来看,这确实没有意义。当某个类扩展另一个类时,这意味着“是”关系。如果你走这条路,你将很难不违反OCP,这是SOLID中的字母之一。
我一直在反复讨论如何确保脚本中只有一个连接或 PDO 对象,因为有些页面需要多个扩展连接器的类。
简单!只需创建一个实例。
许多人似乎为此目的推荐单例模式,但我目前这样做的方式似乎完成了同样的事情。
许多这样的人对 OOP 原则一无所知。使用单例只会引入“花哨”global instance / state。
这段代码是否真的有意义,或者我是否以某种方式误解了静态属性?
说实话这更像是对OOP的误解。
这是糟糕的设计/实践,还是单例有更多优势?
见上文。
你应该做的(在 OOP 中)是 inject the database connection 进入需要它的类。这使您的代码松散耦合,从而使您的代码具有更好的可维护性、可测试性、可调试性和灵活性。
另外,我真的不明白为什么需要为 pdo 连接创建数据库类,因为 PDO API 本身已经是 OOP。因此,除非您有真正的理由为 PDO 编写适配器(可能是这种情况,因为有一些),否则我会放弃它。
我的 €0.02
--
响应您的编辑:
连接器类的存在并不是为了保存 PDO 对象。它的析构函数关闭连接(使其为空)。
通常根本不需要关闭连接。处理请求后,连接将自动关闭(除非我们谈论的是持久连接)。
它包含诸如 isValueTaken 之类的函数,用于检查一个值是否已经在数据库中。它具有以下抽象功能
这听起来像是另一个班级的工作。
例如,我有一个扩展连接器的用户类。它定义了 setData() 在数据库中注册用户。我不知道这是否会对响应产生影响。
不,我的观点仍然成立。用户无需从数据库继承。这听起来是不是很奇怪。从数据库继承的用户(我不想见到那个人)。如果需要,您应该将数据库连接注入到用户中。
【讨论】:
我编辑了我的问题以包含更多信息。如果您的答案仍然适用,那么保留抽象类是否有意义,但将 PDO 对象作为参数传递给任何需要它的函数,而不是将其作为属性保留? 除了在依赖注入需要时尝试将 PDO 实例保持在范围内之外,您是否有任何关于如何使其可用的提示? 对于我的几乎所有项目,我只需将其保持在“范围内”即可。有几次我用过DIC。当我说 DIC 时,我并不是说将整个容器传递给所有东西并将其变成服务定位器,而是一个 DIC,它可以为我连接我的东西。以上是关于PHP pdo 实例作为私有静态属性的主要内容,如果未能解决你的问题,请参考以下文章
Python:类属性,实例属性,私有属性与静态方法,类方法,实例方法