【问题标题】:Saving my PDO connection as a global variable [duplicate]将我的 PDO 连接保存为全局变量 [重复]
【发布时间】:2013-10-12 18:26:00
【问题描述】:

在询问有关 PDO 查询的其他问题时,有人告诉我,将我的 PDO 连接对象保存为全局对象以在调用数据库查询的各种函数中使用它通常是不好的做法。

以下是我通常如何使用我的 PDO 对象:

function somefunction(){
    global $pdo;

    $statement = $pdo->prepare("some query");
    $statement->execute();
}

我读过的论点更多的是关于代码维护和调试,很难追踪谁修改了 PDO 对象以及它在代码中的位置。其他人只是简单地拒绝使用全局变量来存储 PDO 对象,但无法真正解释为什么全局变量是一种不好的方法。

但是,对于只有一个数据库的中小型项目,使用全局变量真的有缺点吗?我通常有我的连接脚本和我的函数脚本,其中函数脚本将 require_once() 连接脚本,我的 PDO 对象是在其中创建的。这样,我的连接总是建立起来的,对 PDO 对象的所有修改都在我的连接脚本中完成。

使用这种方法有什么根本缺陷吗?

【问题讨论】:

  • 考虑你正在建造一座新房子。您会期望电工在墙上运行所有布线,这样您就可以在任何您想要的地方拥有漂亮的插座。但现在想象一下,电工告诉你:“我不会在墙上铺设所有电线,而是使用几米长的延长线。我的意思是,这有什么问题?你仍然有电。”

标签: php design-patterns coding-style


【解决方案1】:

使用这种方法有什么根本缺陷吗?

您首先要了解的是,$pdo存储 逻辑的一部分。这意味着,它只能在执行抽象数据访问的类中使用,无论是 SQL 表还是集合。

让我们看看你的代码,

function somefunction(){
    global $pdo;

    $statement = $pdo->prepare("some query");
    $statement->execute();
}

如果您将来想从 MySQL 切换到 Mongo/MSSQL/PgSQL,该怎么办? 那么你将不得不重写很多代码。

对于每个数据库供应商,您必须创建一个具有不同变量的单独文件。就这样

function somefunction(){
    global $mongo;
    return $mongo->fetch(...);
}

通过使用全局状态,最终会导致大量代码重复,因为您无法传递参数,因此无法在运行时更改函数的行为。

现在让我们看看这个,

function somefunction($pdo){
    $statement = $pdo->prepare("some query");
    $statement->execute();
}

这里,$pdo 作为参数传递,因此没有全局状态。但是问题依然存在,你最终违反了单一职责原则

如果你真的想要一些可维护、干净且可读性强的东西,你最好坚持使用DataMappers。这是一个例子,

$pdo = new PDO(...);

$mapper = new MySQL_DataMapper($pdo);
$stuff = $mapper->fetchUserById($_SESSION['id'])    

var_dump($stuff); // Array(...)

// The class itself, it should look like this
class MySQL_DataMapper
{
    private $table = 'some_table';

    private $pdo;

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

    public function fetchUserById($id)
    {
        $query = "SELECT * FROM `{$this->table}` WHERE `id` =:id";
        $stmt = $this->pdo->prepare($query);

        $stmt->execute(array(
           ':id' => $id
        ));

        return $stmt->fetch();
    }
}

结论

  • 不管你的项目是小是大,你都应该避免使用所有形式的全局状态(全局变量、静态类、单例) - 为了代码的可维护性

  • 您必须记住,$pdo 不是您业务逻辑的一部分。它是存储逻辑的一部分。这意味着,在您开始使用业务逻辑(例如繁重的计算)之前,您应该真正抽象表访问(包括 CRUD 操作)

  • data access abstractioncomputation logic 连接在一起的桥梁通常称为Service

  • 您应该始终将 things 函数的需要作为参数传递

  • 您最好停止担心您的代码并开始考虑抽象层。

  • 最后,在你开始做任何事情之前,你首先要初始化bootstrap.php中的所有服务,然后根据用户的输入($_POST$_GET)开始查询存储。

就像,

public function indexAction()
{
    $id = $_POST['id']; // That could be $this->request->getPost('id')
    $result = $this->dataMapper->fetchById($id);

    return print_r($result, true);
}

【讨论】:

  • 我不确定这是不是真的。 PDO 的部分目的是对数据库连接进行信号化。在非全局情况下重新打开 PDO(Linus 禁止私有)可能会导致不必要的打开-非关闭连接,并可能导致一些严重的开销缺陷。像“避免所有形式的全局状态”这样的笼统声明完全忽略了全局状态的存在是有目的的。托管 pdo 连接就是其中之一。此外,在单个位置维护 pdo 连接可以显着简化以后数据库架构更改时的更新。
  • @liljoshu 所有处理程序共享同一个数据库连接,每次传递时都不会建立连接。那么,你又是什么意思呢?
  • 使用相同 PDO 变量的所有处理程序都使用相同的连接。如果您不断地重新创建 PDO,那么您将建立多个连接。基本上,每次你执行“新 PDO”时,你都在创建一个新连接(如果你不这样做会很奇怪,考虑到每个 PDO 都包含自己的连接字符串。)但是,与 mysqli 不同,你不需要为每个查询创建一个新连接,只是每个 pdo。这意味着,为了保存连接,您希望每个数据库使用一个全局 PDO(或者如果您想要一些更改的连接变量)。
  • @liljoshu 如果需要多个数据库连接,则需要另一种解决方案,这与帖子中提到的不同。在编写真实世界的应用程序方面,众所周知且早已确立的事实是,全局状态使应用程序状态变得不可预测并且很难孤立地测试,因此建议不惜一切代价避免它。此外,还有一个技巧可以按需连接 PDO 连接,稍后可以将其注入服务定位器,请参见此处:pastebin.com/wdUJJWmy
  • 另外,曾经有人说过,“那些害怕重新发明轮子的人会尝试在喷气式飞机的起落架上装车轮。”
【解决方案2】:

对于一个很小的项目,使用 global 不会有太大的伤害。当项目规模开始增长时,事情就会开始变得糟糕。

如果您的 somefunction() 有 300 行使用 5 个全局变量,那么查看您的代码的人不会知道该函数使用外部变量,除非他们通过它寻找全局变量。

另外,不使用全局变量很容易......为什么要这样做

function somefunction( PDO $pdo ){
    $statement = $pdo->prepare("some query");
    $statement->execute();
}

编辑:

show_profile.php 是您的控制器。您的控制器收集视图的所有数据并加载视图。这是一个非常简化的版本。

require( 'functions.php' );
$db = new PDO();
$user = get_user( $db, $user_id );
require( 'view.php' );

【讨论】:

  • 在我看来,我的页面被用户调用,例如 show_profile.php 或 update_settings.php。这些页面调用我的脚本页面中连接到数据库的函数。在我看来,这将内容结构与连接和检索/更新数据库数据所需的计算逻辑分开。让我的页面在参数中调用带有 $pdo 的函数意味着它们必须以某种方式处理 PDO 对象,因此,这模糊了我的用户页面和我的数据库页面之间的界限。或者这是一种错误的看待方式?
  • @Prusprus - show_profile.php 是您的控制器。它应该创建一个数据库连接。加载必要的功能等,然后加载视图。查看我的编辑。
  • Galen 已经展示了一种避免函数中全局变量的简单方法。我已经在我的回答中展示了我使用的一种 OO 方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-28
  • 2019-03-26
  • 2014-12-13
  • 1970-01-01
相关资源
最近更新 更多