【问题标题】:PHP PDO close connection set to null but return statementPHP PDO关闭连接设置为null但返回语句
【发布时间】:2019-06-23 22:52:48
【问题描述】:

我创建了一个连接到数据库的类。然后,所有其他类都可以使用此类中的 connect 函数来打开与 DB 的连接。在函数结束时,我返回结果。那么返回结果后如何关闭连接呢?

<?php

class DbhPdo {
  private $servername;
  private $username;
  private $pwd;
  private $dbname;

  protected function connect() {
    $this->servername = "localhost";
    $this->username = "someUser";
    $this->pwd = "somePswd";
    $this->dbname = "someDB";
    $this->charset = "utf8mb4";

    try{
      $dsn = "mysql:host=" . $this->servername . ";dbname=" . $this->dbname . ";charset=" . $this->charset;
      $pdo = new PDO($dsn, $this->username, $this->pwd);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      return $pdo;
    } catch(PDOException $e) {
      echo "Message: " . $e->getMessage();
    }
  }
}

如果我在返回后设置以下内容,则永远不会调用它来关闭连接:

$pdo = null;

或者 PDO 是否会自动关闭此连接,因为它在执行命令之后 - return $pdo?

或者我是否必须关闭扩展连接的类中的连接?

以下是上述类的扩展类,但我也在这个类中返回结果,所以我也不能在这里将stmt设置为null:

<?php

require_once('dbh.pdo.inc.php');

class LoginPdo extends DbhPdo {
  public $name;
  public $pass1;
  public $hashed;
  public $salted;


  protected function getSomething($name) {
    try{
      $stmt = $this->connect()->query("select something from table where name ='" . $name . "'");
      return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }catch (PDOException $e){
      echo 'Message: ' . $e->getMessage();
    }
  }
}

留下了开始这个的原始类:

  try{
    $this->result = $this->getSomething($this->name);
    echo json_encode($this->result);
    $this->result = null;
  }catch (PDOException $e){
    echo 'Message: ' . $e->getMessage();
  }

设置 $this->result = null 是否会关闭连接?如果是这样,那我就不明白了。有人可以向我解释吗?还是在设置 $pdo = null 是我需要关闭连接的地方时我是否理解正确?

如果是这样,那么在return $pdo执行后如何将其设置为null?

提前致谢

更新: 以防万一其他人希望能够验证连接是否已关闭,我采取了以下步骤来确认@Don'tPanic cmets 以跟踪常规日志文件:

mysql -u root -p
show variables like '%log%';
set global general_log=ON;

tail -f /usr/local/mysql/data/<your log file's name>.log

然后显示连接,因为我使用 PostMan 发布以下查询被立即打开和关闭:

190130 11:00:17  2581 Query show variables like '%log%'
190130 11:02:14  2582 Connect   root@localhost on <table name>
    2582 Query  select * from something where name ='whatever'
    2582 Quit

这是通过遵循@Don'tPanic 回答在 DbhPdo 类中添加一个新函数来完成的:

protected function disconnect() {
  $this->pdo = null;
}

然后我添加了 $this->pdo = null; echo json_encode($this->result);

之后

感谢所有的cmets

【问题讨论】:

  • 您不会将连接存储在变量中,因此很难将其设置为 null。如果将连接设置为类的属性 ($this-&gt;pdo),则可以添加一个方法将该属性设置为 null 以关闭连接。
  • 其实你现在的方式,如果你用其他方法也用connect()这样的话,我想你会创建多个连接。
  • $this-&gt;charset = "utf8mb4"; 在哪里定义为属性?参考DbhPdo类。
  • @Jaquarh,感谢您的回复,我不敢相信我在使用它几个月后没有收到错误,但我添加了 private $charset;与其他变量。
  • @Don'tPanic,你是说我需要添加私有 $pdo,但是我应该在哪里设置 $this->pdo = null?据我了解,$this->pdo 将连接返回到 $stmt = $this->connect()->query()。如果我将其设置为 null,那么 $stmt 将如何执行。在您的第二条评论中,我相信您是正确的,这就是为什么我想知道是否有办法从连接函数中关闭连接?或者在我回显 json_encode() 之后,我是否必须进入每个原始调用以连接并关闭 $this->pdo = null?

标签: php pdo database-connection


【解决方案1】:

将结果设置为 null 不会将连接设置为 null。

可能确实没有必要显式关闭连接,但如果您希望能够做到这一点,您需要有一些可以设置为 null 的东西。您必须对连接有一些引用,并且您当前的方法不会将该引用存储在任何地方。将连接定义为类的属性将为您解决这个问题。

我修改了你的连接函数来展示一个例子。

基本上,您不只是返回一个连接,而是检查现有连接并返回它,或者如果尚未建立一个新连接,则建立一个新连接,但重要的部分是您设置属性 $this-&gt;pdo 而不是使用$pdo 变量,仅存在于 connect 函数范围内。

// ...
private $pdo;

protected function connect() {
    // I suggest setting these in the constructor rather than hard coding them here
    // $this->servername = "localhost";
    // $this->username = "someUser";
    // $this->pwd = "somePswd";
    // $this->dbname = "someDB";
    // $this->charset = "utf8mb4";

    if ($this->pdo) {
        return $this->pdo;
    else {
        try{
            $dsn = "mysql:host=" . $this->servername . ";dbname=" . $this->dbname . ";charset=" . $this->charset;
            $this->pdo = new PDO($dsn, $this->username, $this->pwd);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $this->pdo;
        } catch(PDOException $e) {
            echo "Message: " . $e->getMessage();
        }
    }
}

设置$this-&gt;pdo 而不是仅仅在连接函数中使用一个局部变量会给你一些你可以设置为空来断开连接的东西。

protected function disconnect() {
    $this->pdo = null;
}

您可以在执行请求所需的所有查询后调用disconnect()。如果在此之前断开连接,则必须重新连接才能运行其他查询,这是不必要的,并且会损害应用程序的性能。

【讨论】:

  • @dont-panic,感谢您的回复。我会试一试,让你知道我发现了什么。我假设在将断开连接添加到 DbhPdo 类之后,我可以在 return $stmt->fetchAll(PDO::FETCH_ASSOC); 之前调用 $this-disconnect() - 我的理解正确吗?
  • 您应该在使用完连接后调用 disconnect。如果您可能执行多个查询,您应该重用相同的连接而不是断开/重新连接。但是不要在获取之前断开连接,之后断开连接。如果关闭连接,则无法获取结果。
  • 只要记住将$this-&gt;pdo 作为属性添加到您的DbhPdo 类中,确保它是private,因为您有使用实例的getter/setter 方法。这是一个快速修复的答案并且工作正常,但我只是不认为它解释了关于 PDOPrepareStatement 是否通过引用保存 PDO 实例的 OP 真正问题,并且在您的新答案情况下,会调用 @987654332 @fetchAll 之前的@,仍然有效。
  • @Jaquarh 感谢您的反馈。我进行了编辑以尝试更明确地说明这些要点。
  • @Don'tPanic,还有一件事。如何检查/确认连接是否实际关闭。我可以在 dbh.pdo.inc.php 文件上做类似尾巴的事情吗?
【解决方案2】:

您的 PDO API 实例从 $this-&gt;connect() 返回。这是您使用的唯一实例。如果您想保存状态以供以后使用或关闭它,您可以使用:

$stmt = ($con =& $this->connect())->query(...);
$con  = null; # Or use the instance else where
return $stmt->fetchAll(PDO::FETCH_ASSOC);

如果PDOPrepareStatement 通过引用使用PDO 实例,您可以使用clone 复制实例而不是写入实例。

$stmt = ($con =& clone $this->connect())->query(...);

您的$stmt 变量是PDOPrepareStatement,因为PDO::query 不返回PDO 实例,因此您已经将其设置为null。

注意点:如果你想构建模型,你应该考虑设计一个单例方法。你可以看看example I made for Laravel

trait Singleton
{
    private static $instance;

    public static function getInstance()
    {
        return self::$instance ?? (self::$instance = new self());
    }

    //
}

然后你就可以创建你的超级模型了:

class Model
{
    use Singleton;

    protected $pdo;

    public function __construct()
    {
        // instance your PDO
    }

    public function select()
    {
        // super method all models can use
    }
}

然后创建你的模型!

class UserModel extends Model
{
    public function find($id)
    {
        return $this->select(...)->fetch();
    }
}

你可以这样使用它:

UserModel::getInstance()
    ->find(1);

【讨论】:

  • 感谢您的回复。我一直不清楚单例、模型和 Super 的概念——我是否有文章的链接可以清楚地说明这个概念?另外,你是说我可以在 return $stmt->fetchAll(PDO::FETCH_ASSOC); 之前设置 $this-connect = null不影响 $stmt?
  • 您永远不会捕获 PDO 实例。 DbhPdo 类中的 connect 方法仅返回实例。对$this-&gt;connect()-&gt;query() 的调用永远不会保存 PDO 实例的状态,它会使用它但永远不会存储在内存中 - 因此执行($con =&amp; $this-&gt;connect())-&gt;query() 然后使用$con = null 不会产生影响只有除非 query() 返回通过引用使用 PDO 实例。在这种情况下,您需要clone。或者,您可以捕获变量中的行,关闭连接,最后返回行。
  • 如果你想扩大你在单例中的范围,你可以参考this question 在 PHP 7+ 中使用traitSuper 只是指父类,而 model 是您在 ORM concept 中使用的东西。
  • 谢谢,但是服务器是 PHP 5.3,我的开发服务器是 5.6。所有这些都适用于那些版本的 PHP 吗?
  • 我建议您更新您的 PHP 版本。 PHP 7+ 贬低了很多东西,并引入了很多新的安全修复和编程特性。 PHP 7.2 将加密扩展更改为 LibSodium,openssl_* 现在更安全。在这个时代,您不应该运行低于 PHP 7 的服务器 - 只需更新即可。
猜你喜欢
  • 1970-01-01
  • 2022-01-02
  • 2015-10-04
  • 1970-01-01
  • 2018-01-08
  • 1970-01-01
  • 2012-05-03
  • 1970-01-01
  • 2016-05-25
相关资源
最近更新 更多