使用单例模式的出发点:
1、php的应用主要在于数据库应用, 所以一个应用中会存在大量的数据库操作, 使用单例模式, 则可以避免大量的new 操作消耗的资源。
2、如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现. 这个可以参看ZF的FrontController部分。
3、在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志,从而避免到处var_dump, echo。
创造单例注意:
1、一个雷只能有一个类对象(只能实例化一个对象)
2、它必须自己创建这个实例
3、它必须自行向整个系统提供这个实例
4、构造函数和克隆函数必须声明为私有的,这是为了防止外部程序 new 类从而失去单例模式的意义
5、 getInstance()方法必须声明为公有的,必须调用此方法以返回唯一实例的一个引用
6、拥有一个保存类的实例的静态成员变量
7、PHP的单例模式是相对而言的,因为PHP的解释运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收
8、拥有一个访问这个实例的公共的静态方法(常用getInstance()方法进行实例化单例类,通过instanceof操作符可以检测到类是否已经被实例化)
另外,需要创建__clone()方法防止对象被复制(克隆)
代码如下:
1 <?php 2 class Danli 3 { 4 5 //保存类实例的静态成员变量 6 private static $_instance; 7 8 //private标记的构造方法 9 private function __construct() 10 { 11 echo ‘This is a Constructed method;‘; 12 } 13 14 //创建__clone方法防止对象被复制克隆 15 public function __clone() 16 { 17 trigger_error(‘Clone is not allow!‘, E_USER_ERROR); 18 } 19 20 //单例方法,用于访问实例的公共的静态方法 21 public static function getInstance() 22 { 23 if (!(self::$_instance instanceof self)) { 24 self::$_instance = new self; 25 } 26 return self::$_instance; 27 } 28 29 public function test() 30 { 31 echo ‘调用方法成功‘; 32 } 33 34 } 35 36 /*用new实例化private标记构造函数的类会报错 37 $danli = new Danli(); 38 39 复制(克隆)对象将导致一个E_USER_ERROR 40 $danli_clone = clone $danli; 41 */ 42 43 44 //正确方法,用双冒号::操作符访问静态方法获取实例 45 $danli = Danli::getInstance(); 46 $danli->test(); 47 ?>
运用单例模式实现一个数据库类:
1 <?php 2 class DBHelper 3 { 4 private $link; 5 static private $_instance; 6 7 // 连接数据库 8 private function __construct($host, $username, $password) 9 { 10 $this->link = mysql_connect($host, $username, $password); 11 $this->query("SET NAMES ‘utf8‘", $this->link); 12 //echo mysql_errno($this->link) . ": " . mysql_error($link). "n"; 13 //var_dump($this->link); 14 return $this->link; 15 } 16 private function __clone() 17 { 18 } 19 public static function get_class_nmdb($host, $username, $password) 20 { 21 //$connector = new nmdb($host, $username, $password); 22 //return $connector; 23 24 if (FALSE == (self::$_instance instanceof self)) { 25 self::$_instance = new self($host, $username, $password); 26 } 27 return self::$_instance; 28 } 29 // 连接数据表 30 public function select_db($database) 31 { 32 $this->result = mysql_select_db($database); 33 return $this->result; 34 } 35 // 执行SQL语句 36 public function query($query) 37 { 38 return $this->result = mysql_query($query, $this->link); 39 } 40 // 将结果集保存为数组 41 public function fetch_array($fetch_array) 42 { 43 return $this->result = mysql_fetch_array($fetch_array, MYSQL_ASSOC); 44 } 45 // 获得记录数目 46 public function num_rows($query) 47 { 48 return $this->result = mysql_num_rows($query); 49 } 50 // 关闭数据库连接 51 public function close() 52 { 53 return $this->result = mysql_close($this->link); 54 } 55 } 56 $connector = DBHelper::get_class_nmdb($host, $username, $password); 57 $connector -> select_db($database); 58 ?>
也可以参考这个类实现:
1 <?php 2 /* 3 * mysql 单例 4 */ 5 class mysql{ 6 private $host =‘localhost‘; //数据库主机 7 private $user = ‘root‘; //数据库用户名 8 private $pwd = ‘‘; //数据库用户名密码 9 private $database = ‘imoro_imoro‘; //数据库名 10 private $charset = ‘utf8‘; //数据库编码,GBK,UTF8,gb2312 11 private $link; //数据库连接标识; 12 private $rows; //查询获取的多行数组 13 static $_instance; //存储对象 14 /** 15 * 构造函数 16 * 私有 17 */ 18 private function __construct($pconnect = false) { 19 if (!$pconnect) { 20 $this->link = @ mysql_connect($this->host, $this->user, $this->pwd) or $this->err(); 21 } else { 22 $this->link = @ mysql_pconnect($this->host, $this->user, $this->pwd) or $this->err(); 23 } 24 mysql_select_db($this->database) or $this->err(); 25 $this->query("SET NAMES ‘{$this->charset}‘", $this->link); 26 return $this->link; 27 } 28 /** 29 * 防止被克隆 30 * 31 */ 32 private function __clone(){} 33 public static function getInstance($pconnect = false){ 34 if(FALSE == (self::$_instance instanceof self)){ 35 self::$_instance = new self($pconnect); 36 } 37 return self::$_instance; 38 } 39 /** 40 * 查询 41 */ 42 public function query($sql, $link = ‘‘) { 43 $this->result = mysql_query($sql, $this->link) or $this->err($sql); 44 return $this->result; 45 } 46 /** 47 * 单行记录 48 */ 49 public function getRow($sql, $type = MYSQL_ASSOC) { 50 $result = $this->query($sql); 51 return @ mysql_fetch_array($result, $type); 52 } 53 /** 54 * 多行记录 55 */ 56 public function getRows($sql, $type = MYSQL_ASSOC) { 57 $result = $this->query($sql); 58 while ($row = @ mysql_fetch_array($result, $type)) { 59 $this->rows[] = $row; 60 } 61 return $this->rows; 62 } 63 /** 64 * 错误信息输出 65 */ 66 protected function err($sql = null) { 67 //这里输出错误信息 68 echo ‘error‘; 69 exit(); 70 } 71 } 72 //用例 73 $db = mysql::getInstance(); 74 $db2 = mysql::getInstance(); 75 $data = $db->getRows(‘select * from blog‘); 76 //print_r($data); 77 //判断两个对象是否相等 78 if($db === $db2){ 79 echo ‘true‘; 80 } 81 ?>
PHP单例模式的缺点
众所周知,PHP语言是一种解释型的脚本语言,这种运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。也就是说,PHP在语言级别上没有办法让某个对象常驻内存,这和asp.net、Java等编译型是不同的,比如在Java中单例会一直存在于整个应用程序的生命周期里,变量是跨页面级
的,真正可以做到这个实例在应用程序生命周期中的唯一性。然而在PHP中,所有的变量无论是全局变量还是类的静态成员,都是页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只是针对单次页面
级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。