使用單例模式的出發點:
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單例模式我覺得只是針對單次頁面
級請求時出現多個應用場景並需要共享同一對象資源時是非常有意義的。