【问题标题】:Error in Implementing singleton pattern实现单例模式时出错
【发布时间】:2012-03-28 05:19:16
【问题描述】:

我正在尝试实现单例模式,但出现以下错误

致命错误:对 Database::__construct() 的访问级别必须是公共的 (如在 PDO 类中)在第 29 行的 /config/database.php 中

<?php

class Database extends PDO
{
    private static $instance;

    private function __construct()
    {

            return parent::__construct(
                    "mysql:host=localhost;dbname=live",
                    "root",
                    "root"
            );

    }

    public function getInstance() {
        if(self::$instance === null) {
            self::$instance = new Database();
        }
        return self::$instance;
    }

    public function __clone() 
    {
        die(__CLASS__ . ' class cant be instantiated. Please use the method called getInstance.');
    }
}


$mySingleton = Database::getInstance();

var_dump($mySingleton);

?>

【问题讨论】:

  • 这个问题似乎跑题了,因为错误信息解释了问题的根源。

标签: php design-patterns database-design


【解决方案1】:

通过将 __construct() 函数声明为像 private function __construct() 这样的私有函数,您实际上是在禁止 PHP 在创建对象时自动调用它。

相反,您应该始终将 __construct() 以及其他魔术方法声明为 public。

public function __construct() 
{
   // Now PHP can access me
}

就使您的 Database 类遵循单例模式而言,扩展不这样做的类(即 PDO)是没有意义的。相反,请执行以下操作:

<?php

class Database
{
    private static $instance;

    public function __construct()
    { 
        // Check to see if static PDO instance
        // has already been created, so we create only one (singleton)
        if(!self::$instance)
        {
            self::$instance = new PDO(
                "mysql:host=localhost;dbname=live",
                "root",
                "root"
            );
         }
    }

    public function getInstance() {
        if(self::$instance === null) {
            self::__construct();
        }
        return self::$instance;
    }

    public function __clone() 
    {
        die(__CLASS__ . ' class cant be instantiated. Please use the method called getInstance.');
    }
}


$mySingleton = Database::getInstance();

var_dump($mySingleton);

?>

【讨论】:

  • if(self::$instance === null) {self::__construct();}, self::__construct();这是错误的,因为构造不能是静态的。
  • 由于您不再扩展 PDO 类,您可以将构造函数设置为私有。还插入... "if(self::$instance === null)" 使用起来会更合适:"if(!(self::$_instance instanceof PDO))" 获取实例的唯一方法PDO 对象应该通过调用静态方法 getInstance() 来实现。在您的代码中,此方法不是静态的。它应该是“public static function getInstance(){...}”然后在你的代码中你只使用“Database::getInstance()”
【解决方案2】:

您不能更改覆盖方法的访问级别。

您可以只在Database 中拥有一个 PDO 实例,而不是扩展 PDO。 组合比继承更灵活。

【讨论】:

    【解决方案3】:

    由于 PDO 的 __construct() 函数是公共的,因此您不能使用私有的 __construct() 函数对其进行扩展。

    所以“真正的”单例是不可能的。
    你必须设置public function __construct()

    【讨论】:

      【解决方案4】:

      除了涉及 PHP 源代码的技术难题之外,完全合理需要在父构造函数和子构造函数之间具有不同的访问级别。

      事实上,它已作为一个错误提交 (#61970)。

      在 2017 年 3 月的这个提交中,如果我没记错的话,开发者说它已经关闭了:
      http://git.php.net/?p=php-src.git;a=commitdiff;h=5324fb1f348f5bc979d9b5f13ac74177b73f9bf7

      我使用的是 7 月发布的 PHP 7.0.24,但我仍然看到它没有被修复。

      【讨论】:

        【解决方案5】:

        你应该

        1) 通过将构造函数设置为私有来禁用它。

        2) 仅通过调用静态方法创建单个 PDO 对象。静态方法必须返回 PDO 对象的实例。

        在 Silex 或 Symfony 中,您必须在类名前加上“\”或使用“use \PDO;”。这仅意味着这是一个全局类。

        附言。如果您将 __constructor 设置为 public 并使用 return functino 请注意,它不会生成任何异常或警告,但您将返回一个类对象,而不是 return 语句的实际值。

        所以 $db = new Database() 将返回类数据库的对象。然后从那里你必须使用类方法访问你的 PDO。 $pdo = $db->getInstance() 这不是构建正确单例的正确方法。

        如果您有兴趣阅读更多关于单例的优缺点和一些用例的信息,请阅读此Best practice on PHP singleton classes,您将找到有关此模式设计的更多信息。

        /**
         *  Singleton pattern
         */
        class Database
        {
        
            /**
             *  holds the only PDO instance
             */
            private static $_instance;
        
            /**
             *  private __constructor not accesible
             */
            private function __construct()
            {
        
                self::$instance = new PDO(
                    "mysql:host=localhost;dbname=live",
                    "root",
                    "root"
                );
        
            }
        
            /**
             *  clone will not duplicate object
             */
            public function __clone() 
            {
        
                die(__CLASS__ . ' class cant be instantiated. Please use the method called getInstance.');
        
            }
        
            /**
             *  the only public function to access or create PDO object
             */
            public static function getInstance()
            {
        
                if(!self::$_instance instanceof PDO){
                    new self;
                }
        
                return self::$_instance;
        
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多