一、什么是單例模式?
1、含義
作為對象的創(chuàng)建模式,單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)全局地提供這個實例。它不會創(chuàng)建實例副本,而是會向單例類內(nèi)部存儲的實例返回一個引用。
2、單例模式的三個要點:
(1). 需要一個保存類的唯一實例的靜態(tài)成員變量:
private static $_instance;
(2). 構(gòu)造函數(shù)和克隆函數(shù)必須聲明為私有的,防止外部程序new類從而失去單例模式的意義:
- private function __construct(){
- $this->_db = pg_connect('xxxx');
- }
- private function __clone()
- {
- }//覆蓋__clone()方法,禁止克隆
(3). 必須提供一個訪問這個實例的公共的靜態(tài)方法(通常為getInstance方法),從而返回唯一實例的一個引用.
- public static function getInstance(){
- if(! (self::$_instance instanceof self) )
- {
- self::$_instance = new self();
- }
- return self::$_instance;
- }
多數(shù)人都是從單例模式的字面上的意思來理解它的用途, 認為這是對系統(tǒng)資源的節(jié)省, 可以避免重復(fù)實例化, 是一種"計劃生育". 而PHP每次執(zhí)行完頁面都是會從內(nèi)存中清理掉所有的資源. 因而PHP中的單例實際每次運行都是需要重新實例化的, 這樣就失去了單例重復(fù)實例化的意義了. 單單從這個方面來說, PHP的單例的確有點讓各位失望. 但是單例僅僅只有這個功能和應(yīng)用嗎? 答案是否定的,我們一起來看看。
php的應(yīng)用主要在于數(shù)據(jù)庫應(yīng)用, 所以一個應(yīng)用中會存在大量的數(shù)據(jù)庫操作, 在使用面向?qū)ο蟮姆绞介_發(fā)時(廢話), 如果使用單例模式, 則可以避免大量的new 操作消耗的資源。
如果系統(tǒng)中需要有一個類來全局控制某些配置信息, 那么使用單例模式可以很方便的實現(xiàn). 這個可以參看zend Framework的FrontController部分。
在一次頁面請求中, 便于進行調(diào)試, 因為所有的代碼(例如數(shù)據(jù)庫操作類db)都集中在一個類中, 我們可以在類中設(shè)置鉤子, 輸出日志,從而避免到處var_dump, echo。
1、PHP缺點:
PHP語言是一種解釋型的腳本語言,這種運行機制使得每個PHP頁面被解釋執(zhí)行后,所有的相關(guān)資源都會被回收。也就是說,PHP在語言級別上沒有辦法讓某個對象常駐內(nèi)存,這和asp.net、Java等編譯型是不同的,比如在Java中單例會一直存在于整個應(yīng)用程序的生命周期里,變量是跨頁面級的,真正可以做到這個實例在應(yīng)用程序生命周期中的唯一性。然而在PHP中,所有的變量無論是全局變量還是類的靜態(tài)成員,都是頁面級的,每次頁面被執(zhí)行時,都會重新建立新的對象,都會在頁面執(zhí)行完畢后被清空,這樣似乎PHP單例模式就沒有什么意義了,所以PHP單例模式我覺得只是針對單次頁面級請求時出現(xiàn)多個應(yīng)用場景并需要共享同一對象資源時是非常有意義的。
2、單例模式在PHP中的應(yīng)用場合:
(1)、應(yīng)用程序與數(shù)據(jù)庫交互
一個應(yīng)用中會存在大量的數(shù)據(jù)庫操作,比如過數(shù)據(jù)庫句柄來連接數(shù)據(jù)庫這一行為,使用單例模式可以避免大量的new操作,因為每一次new操作都會消耗內(nèi)存資源和系統(tǒng)資源。
(2)、控制配置信息
如果系統(tǒng)中需要有一個類來全局控制某些配置信息, 那么使用單例模式可以很方便的實現(xiàn).
三、如何實現(xiàn)單例模式?
1、普通的數(shù)據(jù)庫訪問例子:
- <?php
- ......
- //初始化一個數(shù)據(jù)庫句柄
- $db = new DB(...);
- //添加用戶信息
- $db->addUserInfo(...);
- ......
- //在函數(shù)中訪問數(shù)據(jù)庫,查找用戶信息
- function getUserInfo()
- {
- $db = new DB(...);//再次new 數(shù)據(jù)庫類,和數(shù)據(jù)庫建立連接
- $db = query(....);//根據(jù)查詢語句訪問數(shù)據(jù)庫
- }
- ?>
2、應(yīng)用單例模式對數(shù)據(jù)庫進行操作:
- <?php
- class DB
- {
- private $_db;
- private static $_instance;
- private function __construct(...)
- {
- $this->_db = pg_connect(...);//postgrsql
- }
- private function __clone() {}; //覆蓋__clone()方法,禁止克隆
- public static function getInstance()
- {
- if(! (self::$_instance instanceof self) ) {
- self::$_instance = new self();
- }
- return self::$_instance;
- }
- public function addUserInfo(...)
- {
- }
- public function getUserInfo(...)
- { //Vevb.com
- }
- }
- //test
- $db = DB::getInstance();
- $db->addUserInfo(...);
- $db->getUserInfo(...);
- ?>
下面的代碼是PDO操作數(shù)據(jù)庫類的一個封裝,采用了單例模式:
- <?php
- /**
- * MyPDO
- */
- class MyPDO
- {
- protected static $_instance = null;
- protected $dbName = '';
- protected $dsn;
- protected $dbh;
- /**
- * 構(gòu)造
- *
- * @return MyPDO
- */
- private function __construct($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset)
- {
- try {
- $this->dsn = 'mysql:host='.$dbHost.';dbname='.$dbName;
- $this->dbh = new PDO($this->dsn, $dbUser, $dbPasswd);
- $this->dbh->exec('SET character_set_connection='.$dbCharset.', character_set_results='.$dbCharset.', character_set_client=binary');
- } <a href="/"/tags.php/catch//"" target="/"_blank/"">catch</a> (PDOException $e) {
- $this->outputError($e->getMessage());
- }
- }
- /**
- * 防止克隆
- *
- */
- private function __clone() {}
- /**
- * Singleton instance
- *
- * @return Object
- */
- public static function getInstance($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset)
- {
- if (self::$_instance === null) {
- self::$_instance = new self($dbHost, $dbUser, $dbPasswd, $dbName, $dbCharset);
- }
- return self::$_instance;
- }
- /**
- * Query 查詢
- *
- * @param String $strSql SQL語句
- * @param String $queryMode 查詢方式(All or Row)
- * @param Boolean $debug
- * @return Array
- */
- public function query($strSql, $queryMode = 'All', $debug = false)
- {
- if ($debug === true) $this->debug($strSql);
- $recordset = $this->dbh->query($strSql);
- $this->getPDOError();
- if ($recordset) {
- $recordset->setFetchMode(PDO::FETCH_ASSOC);
- if ($queryMode == 'All') {
- $result = $recordset->fetchAll();
- } elseif ($queryMode == 'Row') {
- $result = $recordset->fetch();
- }
- } else {
- $result = null;
- }
- return $result;
- }
- /**
- * Update 更新
- *
- * @param String $table 表名
- * @param Array $arrayDataValue 字段與值
- * @param String $where 條件
- * @param Boolean $debug
- * @return Int
- */
- public function update($table, $arrayDataValue, $where = '', $debug = false)
- {
- $this->checkFields($table, $arrayDataValue);
- if ($where) {
- $strSql = '';
- <a href="/"/tags.php/foreach//"" target="/"_blank/"">foreach</a> ($arrayDataValue as $key => $value) {
- $strSql .= ", `$key`='$value'";
- }
- $strSql = <a href="/"/tags.php/substr//"" target="/"_blank/"">substr</a>($strSql, 1);
- $strSql = "UPDATE `$table` SET $strSql WHERE $where";
- } else {
- $strSql = "REPLACE INTO `$table` (`".implode('`,`', array_keys($arrayDataValue))."`) VALUES ('".implode("','", $arrayDataValue)."')";
- }
- if ($debug === true) $this->debug($strSql);
- $result = $this->dbh->exec($strSql);
- $this->getPDOError();
- return $result;
- }
- /**
- * Insert 插入
- *
- * @param String $table 表名
- * @param Array $arrayDataValue 字段與值
- * @param Boolean $debug
- * @return Int
- */
- public function insert($table, $arrayDataValue, $debug = false)
- {
- $this->checkFields($table, $arrayDataValue);
- $strSql = "INSERT INTO `$table` (`".implode('`,`', array_keys($arrayDataValue))."`) VALUES ('".implode("','", $arrayDataValue)."')";
- if ($debug === true) $this->debug($strSql);
- $result = $this->dbh->exec($strSql);
- $this->getPDOError();
- return $result;
- }
- /**
- * Replace 覆蓋方式插入
- *
- * @param String $table 表名
- * @param Array $arrayDataValue 字段與值
- * @param Boolean $debug
- * @return Int
- */
- public function replace($table, $arrayDataValue, $debug = false)
- {
- $this->checkFields($table, $arrayDataValue);
- $strSql = "REPLACE INTO `$table`(`".implode('`,`', array_keys($arrayDataValue))."`) VALUES ('".implode("','", $arrayDataValue)."')";
- if ($debug === true) $this->debug($strSql);
- $result = $this->dbh->exec($strSql);
- $this->getPDOError();
- return $result;
- }
- /**
- * Delete 刪除
- *
- * @param String $table 表名
- * @param String $where 條件
- * @param Boolean $debug
- * @return Int
- */
- public function delete($table, $where = '', $debug = false)
- {
- if ($where == '') {
- $this->outputError("'WHERE' is Null");
- } else {
- $strSql = "DELETE FROM `$table` WHERE $where";
- if ($debug === true) $this->debug($strSql);
- $result = $this->dbh->exec($strSql);
- $this->getPDOError();
- return $result;
- }
- }
- /**
- * execSql 執(zhí)行SQL語句
- *
- * @param String $strSql
- * @param Boolean $debug
- * @return Int
- */
- public function execSql($strSql, $debug = false)
- {
- if ($debug === true) $this->debug($strSql);
- $result = $this->dbh->exec($strSql);
- $this->getPDOError();
- return $result;
- }
- /**
- * 獲取字段最大值
- *
- * @param string $table 表名
- * @param string $field_name 字段名
- * @param string $where 條件
- */
- public function getMaxValue($table, $field_name, $where = '', $debug = false)
- {
- $strSql = "SELECT MAX(".$field_name.") AS MAX_VALUE FROM $table";
- if ($where != '') $strSql .= " WHERE $where";
- if ($debug === true) $this->debug($strSql);
- $arrTemp = $this->query($strSql, 'Row');
- $maxValue = $arrTemp["MAX_VALUE"];
- if ($maxValue == "" || $maxValue == null) {
- $maxValue = 0;
- }
- return $maxValue;
- }
- /**
- * 獲取指定列的數(shù)量
- *
- * @param string $table
- * @param string $field_name
- * @param string $where
- * @param bool $debug
- * @return int
- */
- public function getCount($table, $field_name, $where = '', $debug = false)
- {
- $strSql = "SELECT COUNT($field_name) AS NUM FROM $table";
- if ($where != '') $strSql .= " WHERE $where";
- if ($debug === true) $this->debug($strSql);
- $arrTemp = $this->query($strSql, 'Row');
- return $arrTemp['NUM'];
- }
- /**
- * 獲取表引擎
- *
- * @param String $dbName 庫名
- * @param String $tableName 表名
- * @param Boolean $debug
- * @return String
- */
- public function getTableEngine($dbName, $tableName)
- {
- $strSql = "SHOW TABLE STATUS FROM $dbName WHERE Name='".$tableName."'";
- $arrayTableInfo = $this->query($strSql);
- $this->getPDOError();
- return $arrayTableInfo[0]['Engine'];
- }
- /**
- * beginTransaction 事務(wù)開始
- */
- private function beginTransaction()
- {
- $this->dbh->beginTransaction();
- }
- /**
- * commit 事務(wù)提交
- */
- private function commit()
- {
- $this->dbh->commit();
- }
- /**
- * rollback 事務(wù)回滾
- */
- private function rollback()
- {
- $this->dbh->rollback();
- }
- /**
- * transaction 通過事務(wù)處理多條SQL語句
- * 調(diào)用前需通過getTableEngine判斷表引擎是否支持事務(wù)
- *
- * @param array $arraySql
- * @return Boolean
- */
- public function execTransaction($arraySql)
- {
- $retval = 1;
- $this->beginTransaction();
- foreach ($arraySql as $strSql) {
- if ($this->execSql($strSql) == 0) $retval = 0;
- }
- if ($retval == 0) {
- $this->rollback();
- return false;
- } else {
- $this->commit();
- return true;
- }
- }
- /**
- * checkFields 檢查指定字段是否在指定數(shù)據(jù)表中存在
- *
- * @param String $table
- * @param array $arrayField
- */
- private function checkFields($table, $arrayFields)
- {
- $fields = $this->getFields($table);
- foreach ($arrayFields as $key => $value) {
- if (!in_array($key, $fields)) {
- $this->outputError("Unknown column `$key` in field list.");
- }
- }
- }
- /**
- * getFields 獲取指定數(shù)據(jù)表中的全部字段名
- *
- * @param String $table 表名
- * @return array
- */
- private function getFields($table)
- {
- $fields = array();
- $recordset = $this->dbh->query("SHOW COLUMNS FROM $table");
- $this->getPDOError();
- $recordset->setFetchMode(PDO::FETCH_ASSOC);
- $result = $recordset->fetchAll();
- foreach ($result as $rows) {
- $fields[] = $rows['Field'];
- }
- return $fields;
- }
- /**
- * getPDOError 捕獲PDO錯誤信息
- */
- private function getPDOError()
- {
- if ($this->dbh->errorCode() != '00000') {
- $arrayError = $this->dbh->errorInfo();
- $this->outputError($arrayError[2]);
- }
- }
- /**
- * debug
- *
- * @param mixed $debuginfo
- */
- private function debug($debuginfo)
- {
- var_dump($debuginfo);
- exit();
- }
- /**
- * 輸出錯誤信息
- *
- * @param String $strErrMsg
- */
- private function outputError($strErrMsg)
- {
- throw new Exception('MySQL Error: '.$strErrMsg);
- }
- /**
- * destruct 關(guān)閉數(shù)據(jù)庫連接
- */
- public function destruct()
- {
- $this->dbh = null;
- }
- }
- ?>
調(diào)用方法:
- <?php
- require 'MyPDO.class.php';
- $db = MyPDO::getInstance('localhost', 'root', '123456', 'test', 'utf8');
- $db->query("<a href="/"/tags.php/select//"" target="/"_blank/"">select</a> count(*) frome table");
- $db->destruct();
- ?>
新聞熱點
疑難解答