PHP PDO 的单例替代方案

2024-03-15

这是我用来连接到我的课程MySQL数据库。 正如你所看到的,我正在使用Singleton Pattern但几乎每个帖子都说这是一个非常糟糕的模式。创建数据库连接类的最佳方法是什么?有更好的模式吗?

class DB extends PDO {

    function __construct() {
        try {
            parent::__construct('mysql:host=' . 'localhost' . ';dbname=' . 'kida', 'root', 'root', array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
            parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    }

    public static function get_instance() {
        static $instance = false;
        if(!$instance) $instance = new self;
        return $instance; //returns pdo object.
     }
}

使用单例模式(或反模式)被认为是不好的做法,因为它使测试代码变得非常困难,并且依赖关系非常复杂,直到项目在某个时候变得难以管理。每个 php 进程只能拥有一个固定的对象实例。在为代码编写自动化单元测试时,您需要能够将要测试的代码使用的对象替换为以可预测的方式运行的测试替身。当您要测试的代码使用单例时,您无法将其替换为测试替身。

(据我所知)组织对象(例如数据库对象和使用数据库的其他对象)之间的交互的最佳方法是反转依赖关系的方向。这意味着您的代码不会从外部源请求所需的对象(在大多数情况下是全局对象,例如代码中的静态“get_instance”方法),而是从外部获取其依赖对象(它需要的对象)在它需要它之前。通常您会使用依赖注入管理器/容器,例如这个来自 symfony 项目 http://symfony.com/doc/current/components/dependency_injection/introduction.html#basic-usage来组合你的对象。

使用数据库对象的对象将在构造时注入它。它可以通过 setter 方法或构造函数注入。在大多数情况下(不是全部),最好在构造函数中注入依赖项(您的数据库对象),因为这样使用数据库对象的对象将永远不会处于无效状态。

Example:

interface DatabaseInterface
{
    function query($statement, array $parameters = array());
}

interface UserLoaderInterface
{
    public function loadUser($userId);
}

class DB extends PDO implements DatabaseInterface
{
    function __construct(
        $dsn = 'mysql:host=localhost;dbname=kida',
        $username = 'root',
        $password = 'root',
    ) {
        try {
            parent::__construct($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
            parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class SomeFileBasedDB implements DatabaseInterface
{
    function __construct($filepath)
    {
        # ...
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class UserLoader implements UserLoaderInterface
{
    protected $db;

    public function __construct(DatabaseInterface $db)
    {
        $this->db = $db;
    }

    public function loadUser($userId)
    {
        $row = $this->db->query("SELECT name, email FROM users WHERE id=?", [$userId]);

        $user = new User();
        $user->setName($row[0]);
        $user->setEmail($row[1]);

        return $user;
    }
}

# the following would be replaced by whatever DI software you use,
# but a simple array can show the concept.


# load this from a config file
$parameters = array();
$parameters['dsn'] = "mysql:host=my_db_server.com;dbname=kida_production";
$parameters['db_user'] = "mydbuser";
$parameters['db_pass'] = "mydbpassword";
$parameters['file_db_path'] = "/some/path/to/file.db";


# this will be set up in a seperate file to define how the objects are composed
# (in symfony, these are called 'services' and this would be defined in a 'services.xml' file)
$container = array();
$container['db'] = new DB($parameters['dsn'], $parameters['db_user'], $parameters['db_pass']);
$container['fileDb'] = new SomeFileBasedDB($parameters['file_db_path']);

# the same class (UserLoader) can now load it's users from different sources without having to know about it.
$container['userLoader'] = new UserLoader($container['db']);
# or: $container['userLoader'] = new UserLoader($container['fileDb']);

# you can easily change the behaviour of your objects by wrapping them into proxy objects.
# (In symfony this is called 'decorator-pattern')
$container['userLoader'] = new SomeUserLoaderProxy($container['userLoader'], $container['db']);

# here you can choose which user-loader is used by the user-controller
$container['userController'] = new UserController($container['fileUserLoader'], $container['viewRenderer']);

请注意不同的类如何彼此不了解。它们之间没有直接的依赖关系。这是通过在构造函数中不需要实际的类,而是需要提供所需方法的接口来完成的。

这样您就可以随时为您的类编写替换项,然后将它们替换到依赖注入容器中。您不必检查整个代码库,因为替换只需要实现所有其他类使用的相同接口。您知道一切都会继续工作,因为使用旧类的每个组件只知道接口并且只调用接口已知的方法。

P.S.:请原谅我不断引用 symfony 项目,这正是我最习惯的。其他项目如 Drupal、Propel 或 Zend 可能也有类似的概念。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PHP PDO 的单例替代方案 的相关文章

  • MySQL JSON 存储与两个表

    与使用单独的元表相比 使用 JSON 在表中存储数据有什么好处吗 这是原始架构 Users Table UserId Username Etc 5 John Avatar Table Id UserId ImageName ImageTyp
  • 对自定义 symfony 约束进行单元测试

    这应该非常简单 但今天下午它让我发疯 对自定义 symfony 验证器进行单元测试的正确方法是什么 我能找到的所有文章都与我的做法完全相同 class Foo extends Constraint public string message
  • 在另一列中查找重复值时,如何将列数据存储为逗号分隔值? [复制]

    这个问题在这里已经有答案了 如果任何公司的产品都相同 我必须组合或内爆子数组值 预期输出应该类似于 0 gt array company gt 1 6 product gt 5 我的数组是 array 0 gt array company
  • printf() 字符串中的名称 PHP 说明符

    PHP 中有没有一种方法可以像 Python 一样命名我的说明符 我想要这个 PHP 版本 foo array name gt 24 printf name d foo 我在 google 或 php 手册中找不到任何相关内容 好问题 通过
  • Javascript 闭包与 PHP 闭包,有什么区别?

    JS 中的闭包和 PHP 中的闭包有什么区别 它们的工作方式几乎相同吗 在 PHP 中编写闭包时有什么需要注意的注意事项吗 一个区别是两者如何处理存储执行匿名函数的上下文 JavaScript var a 1 var f function
  • 使用 Mail_Mime 发送附件到 GMail,收到“noname”附件

    我有一个非常简单的网站表单 可以包含附件 它使用 gmail 的 smtp 发送到 gmail 地址 一切工作都很好 除了文件以 noname 形式到达 没有文件名或扩展名 如果您下载附件并使用正确的文件名重命名它 则该文件可以正常打开 我
  • 选择早于的时间戳

    我如何从数据库中选择超过 12 小时的项目 我使用时间戳列来存储时间 但我认为我不需要年 月 日 只需要小时 我有类似的东西 但它不起作用 没有错误 只是从表中返回所有数据 sql SELECT FROM Y WHERE X and tim
  • 如何杀死Mysql“show processlist”中的所有进程?

    因为我在那里看到了很多进程 并且 时间 列显示了所有进程的大值 大规模屠杀操作节省时间 在 MySql 本身中执行此操作 运行这些命令 mysql gt select concat KILL id from information sche
  • 会话已关闭对象名称:“ISession”。在 NHibernate.Impl.AbstractSessionImpl.ErrorIfClosed() - 如何阻止会话过早关闭

    我在带有 MySQL 的 MVC C 应用程序中使用 NHibernate 我正在尝试让多个用户访问该会话 我一直在使用 InRequestScope 在我的会议上 但我仍然得到 System ObjectDisposeException
  • 将 __DIR__ 常量与字符串连接作为数组值,该数组值是 PHP 中的类成员

    谁能告诉我为什么这不起作用 这只是我在其他地方尝试做的事情的一个粗略的例子 stuff array key gt DIR value 但是 这会产生错误 PHP Parse error syntax error unexpected exp
  • Laravel 5.2 如何更新迁移而不丢失数据

    我使用的是 laravel 5 2 我通常会根据项目要求更新数据库 所以我希望在不丢失数据库记录的情况下执行此操作 我的意思不是如何为我的数据库播种 我的意思是当我的数据库处于活动状态并且我想更新它时抛出 laravel 迁移 我正要扔La
  • 如何在不使用 PHP 原生函数的情况下将二进制转换为十进制?

    我的代码是这样的
  • 为通用字符选择表排序规则

    我正在开发一个需要存储通用字符的后端 我选择了utf8mb4用于此目的的表编码 我还必须选择表格排序规则 最直接的选择是选择utf8mb4 general ci表整理 除了一般的排序规则之外 还有大约20种其他排序规则可供选择 更具体的排序
  • 修复 PHP 中格式错误的 HTML?

    我正在根据用户提供的片段构建一个大型 HTML 文档 这些用户有以各种方式格式错误的烦人习惯 浏览器足够强大且宽容 但我希望能够验证并 理想情况下 修复任何格式错误的 HTML 如果可能的话 例如 td b Title b td 可以合理地
  • 将 foreach 与 SplFixedArray 结合使用

    看来我无法通过引用迭代 SplFixedArray 中的值 spl new SplFixedArray 10 foreach spl as value value string var dump spl Outputs Fatal erro
  • Mysql 连接到服务器:用户 root@localhost 的访问被拒绝

    edit9 是否有可能我只是缺少文件夹的一些权限 我真的非常非常感谢更多的建议 edit3 由于这篇文章没有得到足够的回复 而且这绝对是至关重要的 我尽快完成这件事 我重建了我的帖子以显示我认为到目前为止我已经扣除的内容 注意 通过许多不同
  • 更新或插入 MySQL Python

    如果记录已存在 我需要更新一行 如果不存在 我需要创建一个新记录 我理解 ON DUPLICATE KEY 将使用 MYSQLdb 完成此操作 但是我无法使其正常工作 我的代码如下 cursor database cursor cursor
  • 在 while 循环内查询可以吗?

    我在一个数据库中有两个表 我正在查询第一个表限制 10 然后循环结果 在 while 循环内 我使用第一个查询中的数据作为参数再次执行另一个查询 以下是该脚本的示例
  • PHP UTF-8 配置

    我正在使用 PHP 5 3 5 配置 Apache 2 2 17 服务器 我的目标是创建一个默认为内容类型的干净配置UTF 8 php ini default charset UTF 8 default mimetype applicati
  • Google Analytics PHP(发送信息)

    大意 我正在开发一个项目 我需要使用 Google Analytics 服务器端 我不需要检索信息 但我需要发送信息 我最终可以发送 js 脚本客户端 但在这种情况下它不是一个选项 以下大多数链接都非常旧 2012年 检索 不是我需要的 我

随机推荐

  • 包括本地与远程 javascript 库

    我正在使用 jsSHA 1 3 1在这里下载 https github com Caligatio jsSHA downloads并用于我的本地主机上的学习项目 它给出的结果与我通过引用遥控器获得的副本略有不同 如下所示 现在 远程复制对我
  • 实例初始值设定项和*this*关键字[重复]

    这个问题在这里已经有答案了 尝试编译这段代码 public class Main public static void main String args new Main System out println x Error here in
  • Internet Explorer 中的淘汰赛验证错误?

    在我的剃刀观点中 使用knockout http knockoutjs com据说敲除验证 https github com ericmbarnard Knockout Validation我添加以下行 以实际开始使用 ko 验证 Scri
  • 并行运行过程 - Oracle PL/SQL

    我正在尝试使用 dbms scheduler 并行运行存储过程 Oracle PL SQL 但出现类似未知作业的错误 我也尝试过 dbms job 这里出现错误 必须声明标识符 dbms jobs 有人可以帮我吗 以下是我尝试过的两种方法
  • 如何制作 text-decoration: 下划线和 2px 填充?

    我喜欢听话的前端开发人员必须创建带有 2 像素填充的下划线 而不是默认的 1 像素 是否存在简单的解决方案 PS 是的 伙计们 我知道 div 具有黑色背景颜色和 1px Npx 和位置 相对 但它太慢了 对于交叉浏览 最好使用text u
  • 替换git中的master分支并防止快进

    我在 github 上有一个库 大约一年前 我完全重写了该应用程序 但不向后兼容 它位于一个名为 结构化 的分支上 现在大多数安装该库的人都在使用该分支 我想将其设为主分支并将当前主分支移至 旧版 我不希望使用旧主机的人能够快进到新主机 因
  • 如何使用 Seed.rb 有选择地填充开发和/或生产数据库?

    我正在使用 seeds rb 来填充我的开发和生产数据库 我通常用虚拟数据填充第一个 然后用我的应用程序运行所需的实际最小数据 例如第一个用户等 填充后者 如何在 Seed rb 中指定每个数据的环境 鉴于我知道 group 是一个 Gem
  • 何时应使用链表的现实世界示例有哪些?

    另一位程序员提到 在他的职业生涯中 他们还没有在任何专业软件中找到使用链表数据结构的用例 我一时想不出什么好的例子 他主要是 C 和 Java 开发人员 谁能举一些例子来说明这是解决特定现实世界问题的正确数据结构 Related 链表的实际
  • 询问 Python 值“是否可散列”

    我感兴趣的是采用任意字典并将其复制到新字典中 并一路改变它 我想做的一项改变是交换键和值 不幸的是 有些价值观本身就是命令 但是 这会生成 不可散列类型 dict 错误 我真的不介意只是将值字符串化并为其提供密钥 但是 我希望能够做这样的事
  • 是否可以在 Chrome 开发者工具元素面板中选择多个元素?

    是否可以在 Chrome 开发者工具元素面板中一次选择多个元素 对于删除多个同级元素或快速重新排序非常有用 我期望Command Click or Shift Click这样做但没有成功 答案是否定的 在 Chrome 开发工具中一次无法选
  • Python kivy - 如何减少TextInput的高度

    我正在使用 kivy 为应用程序制作一个非常简单的 GUI 没什么复杂的 布局非常简单 尽管如此 我在 TextInputs 方面遇到了困难 它们总是以全高显示 我无法设法使它们调整到 合理的 文本高度 如高度 我正在使用 kv 文件样式
  • 使用 QQuickImageProvider 的正确方法是什么?

    我需要动态选择 qpixmaps 以显示在 QML 图像项中 该 qpixmaps 应该从源 qpixmap 中裁剪出来 我将从 QML 文件中设置它 我希望它们能够根据 QML 的第一个需求被 C 代码裁剪并缓存以供将来使用 对于动态图像
  • 使用 Javascript 重新定位

    我试图根据特定条件在页面上重新定位 div if somecondition document getElementById Div1 setAttribute style position absolute left 297px top
  • Android Studio 上的 Flutter 崩溃

    Android studio崩溃并且无法运行项目 flutter升级到2 0后 flutter doctor 没有问题 dart 修复不起作用 建议在这里 Flutter项目升级到2 0 0后编译错误 https stackoverflow
  • 在网格视图上突出显示所选项目

    我试图突出显示 gridview 上的选定项目 使用适配器动态填充 但它不起作用 我做了研究 我什至尝试准确复制其他人的选择器 甚至他们将其放在 gridview 上的方式 但我无法让它工作 它只是没有做任何事情 每个项目的背景都是白色的
  • websocket消息会丢失吗?

    我目前正在开发一个 Java WebSocket 客户端应用程序 我必须确保客户端收到来自服务器的每条消息 由于连接中断 我是否可能丢失一些消息 一旦从服务器发送消息 WebSocket 基于 TCP 所以这种情况不应该发生 对吗 这有可能
  • 长双精度文字的 C++ 后缀是什么?

    在 C 和 C 中 不带后缀的浮点文字默认为double 而后缀f意味着一个float 但是获得 a 的后缀是什么long double 在不知道的情况下 我会定义说 const long double x 3 14159265358979
  • 什么是 CDI 豆?

    我有点困惑 我们将 CDI bean 称为我们注入它们的 bean Inject注释或我们使用的bean Inject他们里面 CDI豆是 CDI 可以实例化 管理和注入的类 自动满足其他对象的依赖关系 几乎任何 Java类可以由CDI管理
  • JSF/Primefaces AJAX 请求真的是异步的吗?

    我是 JSF 新手 所以我不知道我面临的行为是否正常 我有这个代码
  • PHP PDO 的单例替代方案

    这是我用来连接到我的课程MySQL数据库 正如你所看到的 我正在使用Singleton Pattern但几乎每个帖子都说这是一个非常糟糕的模式 创建数据库连接类的最佳方法是什么 有更好的模式吗 class DB extends PDO fu