一、什么是單例模式?
1、含義
作為對(duì)象的創(chuàng)建模式,單例模式確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)全局地提供這個(gè)實(shí)例。它不會(huì)創(chuàng)建實(shí)例副本,而是會(huì)向單例類內(nèi)部存儲(chǔ)的實(shí)例返回一個(gè)引用。
2、單例模式的三個(gè)要點(diǎn):
(1). 需要一個(gè)保存類的唯一實(shí)例的靜態(tài)成員變量:
private static $_instance;
(2). 構(gòu)造函數(shù)和克隆函數(shù)必須聲明為私有的,防止外部程序new類從而失去單例模式的意義:
private function __construct()
{
$this->_db = pg_connect('xxxx');
}
private function __clone()
{
}//覆蓋__clone()方法,禁止克隆
(3). 必須提供一個(gè)訪問(wèn)這個(gè)實(shí)例的公共的靜態(tài)方法(通常為getinstance方法),從而返回唯一實(shí)例的一個(gè)引用
public static function getinstance()
{
if(! (self::$_instance instanceof self) )
{
self::$_instance = new self();
}
return self::$_instance;
}
二、為什么要使用單例模式?
1、php缺點(diǎn):
php語(yǔ)言是一種解釋型的腳本語(yǔ)言,這種運(yùn)行機(jī)制使得每個(gè)php頁(yè)面被解釋執(zhí)行后,所有的相關(guān)資源都會(huì)被回收。也就是說(shuō),php在語(yǔ)言級(jí)別上沒(méi)有辦法讓某個(gè)對(duì)象常駐內(nèi)存,這和asp.net、java等編譯型是不同的,比如在java中單例會(huì)一直存在于整個(gè)應(yīng)用程序的生命周期里,變量是跨頁(yè)面級(jí)的,真正可以做到這個(gè)實(shí)例在應(yīng)用程序生命周期中的唯一性。然而在php中,所有的變量無(wú)論是全局變量還是類的靜態(tài)成員,都是頁(yè)面級(jí)的,每次頁(yè)面被執(zhí)行時(shí),都會(huì)重新建立新的對(duì)象,都會(huì)在頁(yè)面執(zhí)行完畢后被清空,這樣似乎php單例模式就沒(méi)有什么意義了,所以php單例模式我覺(jué)得只是針對(duì)單次頁(yè)面級(jí)請(qǐng)求時(shí)出現(xiàn)多個(gè)應(yīng)用場(chǎng)景并需要共享同一對(duì)象資源時(shí)是非常有意義的。
2、單例模式在php中的應(yīng)用場(chǎng)合:
(1)、應(yīng)用程序與數(shù)據(jù)庫(kù)交互
一個(gè)應(yīng)用中會(huì)存在大量的數(shù)據(jù)庫(kù)操作,比如過(guò)數(shù)據(jù)庫(kù)句柄來(lái)連接數(shù)據(jù)庫(kù)這一行為,使用單例模式可以避免大量的new操作,因?yàn)槊恳淮蝞ew操作都會(huì)消耗內(nèi)存資源和系統(tǒng)資源。
(2)、控制配置信息
如果系統(tǒng)中需要有一個(gè)類來(lái)全局控制某些配置信息, 那么使用單例模式可以很方便的實(shí)現(xiàn).
三、如何實(shí)現(xiàn)單例模式?
1、普通的數(shù)據(jù)庫(kù)訪問(wèn)例子:
......
//初始化一個(gè)數(shù)據(jù)庫(kù)句柄
$db = new db(...);
//添加用戶信息
$db->adduserinfo(...);
......
//在函數(shù)中訪問(wèn)數(shù)據(jù)庫(kù),查找用戶信息
function getuserinfo()
{
$db = new db(...);//再次new 數(shù)據(jù)庫(kù)類,和數(shù)據(jù)庫(kù)建立連接
$db = query(....);//根據(jù)查詢語(yǔ)句訪問(wèn)數(shù)據(jù)庫(kù)
}
?>
2、應(yīng)用單例模式對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作:
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(...) { } } //test $db = db::getinstance(); $db->adduserinfo(...); $db->getuserinfo(...); ?>
下面的代碼是pdo操作數(shù)據(jù)庫(kù)類的一個(gè)封裝,采用了單例模式:
/** * 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'); } catch (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語(yǔ)句 * @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 = ''; foreach ($arraydatavalue as $key => $value) { $strsql .= ", `$key`='$value'"; } $strsql = substr($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語(yǔ)句 * * @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 庫(kù)名 * @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 通過(guò)事務(wù)處理多條sql語(yǔ)句 * 調(diào)用前需通過(guò)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錯(cuò)誤信息 */ 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(); } /** * 輸出錯(cuò)誤信息 * * @param string $strerrmsg */ private function outputerror($strerrmsg) { throw new exception('mysql error: '.$strerrmsg); } /** * destruct 關(guān)閉數(shù)據(jù)庫(kù)連接 */ public function destruct() { $this->dbh = null; } } ?>
調(diào)用方法:
require 'mypdo.class.php';
$db = mypdo::getinstance('localhost', 'root', '123456', 'test', 'utf8');
$db->query("select count(*) frome table");
$db->destruct();
?>
以上內(nèi)容就是關(guān)于php實(shí)現(xiàn)單例模式的方法。希望能幫助到大家。
- PHP中怎么使用ORM框架連接數(shù)據(jù)庫(kù)
- 如何使用PHP實(shí)現(xiàn)Redis數(shù)據(jù)庫(kù)主從復(fù)制
- PHP如何用Memcache緩存技術(shù)提高數(shù)據(jù)訪問(wèn)速度
- thinkphp怎么配置數(shù)據(jù)庫(kù)連接池
- 原生PHP和Laravel中的錯(cuò)誤處理方法是什么
- PHP中的Laravel、Yii、CodeIgniter框架有什么優(yōu)缺點(diǎn)
- PHP的instanceof詳解及使用方法介紹
- ThinkPHP5.0之底層運(yùn)行原理執(zhí)行流程分析
- php實(shí)現(xiàn)單例模式的方法