In a PHP5 class, when does a private constructor get called?
假设我正在编写一个php(>=5.0)类,该类应该是单例的。我读过的所有文档都说要将类构造函数设为私有的,这样类就不能直接实例化。
所以如果我有这样的东西:
1 2 3 4 5 6 7 8 9 10 11 12 | class SillyDB { private function __construct() { } public static function getConnection() { } } |
是否有任何情况下调用了u construct(),除了如果我正在执行
1 | new SillyDB() |
在类内部调用?
为什么我可以从内部实例化sillydb?
只有从包含私有构造函数的类的方法中调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class DBConnection { private static $Connection = null; public static function getConnection() { if(!isset(self::$Connection)) { self::$Connection = new DBConnection(); } return self::$Connection; } private function __construct() { } } $dbConnection = DBConnection::getConnection(); |
之所以能够/希望从内部实例化类,是因为可以检查以确保在任何给定时间只存在一个实例。毕竟,这是单身汉的全部观点。使用singleton进行数据库连接可确保应用程序一次不进行大量的DB连接。
编辑:按照@emanuele del grande的建议添加了美元
下面是一个非常简单的单例,它只生成日期/时间字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class TheDate { private static $DateInstance = null; private $dateVal; public static function getDateInstance() { if(!isset(self::$DateInstance)) { self::$DateInstance = new TheDate(); } return self::$DateInstance; } public static function getDateVal() { return self::$DateInstance->dateVal; } private function __construct() { $this->dateVal = strftime("%Y-%m-%d %H:%M:%S"); } } |
做这样的事情显然会让我一次又一次的约会:
1 2 3 4 5 | $date1 = TheDate::getDateInstance(); echo $date1->getDateVal() ."<br />"; $date2 = TheDate::getDateInstance(); echo $date2->getDateVal() ."<br />"; |
这样做不会产生任何错误:
1 2 3 4 5 6 7 | class NewDate extends TheDate { public function __construct() { } } |
好吧,我自己做了一些测试。
- 如果不在子类中声明自己的构造函数,尝试实例化它将引发致命错误,因为它试图调用超类构造函数。
- 在数据库连接的情况下,当您可能返回mysqli实例或
mysql_connect() 函数返回的资源(或其他链接到其他RDBMS)时,只要您将实例标记为私有,就不会有人对其进行子类化和篡改链接的威胁。 - 正如我之前提到的,如果有人真的想要覆盖你的行为并建立多个连接,他们也可以通过编写一个新的类来完成。
测试代码,马克,让我知道你发现了什么。
编辑:另外,在这种特定的情况下,我不知道为什么您需要考虑阻止一个人进行子类化。如果有人可以访问您的PHP代码来对其进行子类化,那么他们也可以访问您的代码来复制它,并将访问修饰符更改为他们(出于任何原因)认为合适的内容。
在这种情况下,singleton的实际用途是,通过使用它,可以确保对给定的HTTP请求始终使用相同的数据库连接。它实现了这一点。从理论的角度来看,其他的东西(使用
有点吹毛求疵:为了完整性,您可能也会声明该类是最终的,因为您不希望有人子类化该类并实现它自己的公共构造函数。
(如果编译器捕获了私有构造函数的重写,请原谅我,但我认为它没有捕获到。)