实现单例模式时出错


Error in Implementing singleton pattern

我正在尝试实现singleton模式,并得到以下错误

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

<?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);
?>

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

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

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);
?>

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

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

由于PDO的__construct()函数是公共的,因此无法使用专用__construct()函数对其进行扩展。

因此,"真正的"单例是不可能的
您必须设置public function __construct()

除了涉及PHP源代码的技术困难外,父构造函数和子构造函数之间需要不同的访问级别是完美的选择

事实上,它已经被归档为一个bug(#61970)。

在2017年3月做出的这一承诺中,如果我没有错的话,开发商表示它已经关闭:
http://git.php.net/?p=php-src.git;a=commitdiff;h=5324fb1f348f5bc979d9b5f13ac74177b73f9bf7

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

您应该

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

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

在Silex或Symfony中,您必须在类名前面加上"''"或使用"use''PDO;"。这意味着这是一个全球性的阶层。

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

所以$db=new Database()将返回Database类的对象。然后,您将不得不使用类方法访问您的PDO$pdo=$db->getInstance()这不是构建合适的singleton的正确方法。

如果您有兴趣阅读更多关于singletons的优点和缺点以及一些用例,请阅读PHP singleton类的最佳实践,您将找到有关此模式设计的更多信息。

/**
 *  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;
    }
}