一、什么是单例模式?

1、含义

作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。

2、单例模式的三个要点:

(1). 需要一个保存类的唯一实例的静态成员变量:
private static $_instance;

(2). 构造函数和克隆函数必须声明为私有的,防止外部程序new类从而失去单例模式的意义:
private function __construct()   
{   
    $this->_db = pg_connect('xxxx');  
}   
private function __clone()  
{  
}//覆盖__clone()方法,禁止克隆 
 
   
(3). 必须提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一实例的一个引用 
public static function getInstance()    
{    
    if(! (self::$_instance instanceof self) )   
    {    
        self::$_instance = new self();    
    }  
    return self::$_instance;    
  
}

二、为什么要使用单例模式?

多数人都是从单例模式的字面上的意思来理解它的用途, 认为这是对系统资源的节省, 可以避免重复实例化, 是一种"计划生育".   而PHP每次执行完页面都是会从内存中清理掉所有的资源. 因而PHP中的单例实际每次运行都是需要重新实例化的, 这样就失去了单例重复实例化的意义了. 单单从这个方面来说, PHP的单例的确有点让各位失望. 但是单例仅仅只有这个功能和应用吗? 答案是否定的,我们一起来看看。

php的应用主要在于数据库应用, 所以一个应用中会存在大量的数据库操作, 在使用面向对象的方式开发时(废话), 如果使用单例模式, 则可以避免大量的new 操作消耗的资源。
如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现. 这个可以参看zend Framework的FrontController部分。
在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志,从而避免到处var_dump, echo。

1、PHP缺点:

PHP语言是一种解释型的脚本语言,这种运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。也就是说,PHP在语言级别上没有办法让某个对象常驻内存,这和asp.net、Java等编译型是不同的,比如在Java中单例会一直存在于整个应用程序的生命周期里,变量是跨页面级的,真正可以做到这个实例在应用程序生命周期中的唯一性。然而在PHP中,所有的变量无论是全局变量还是类的静态成员,都是页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。

2、单例模式在PHP中的应用场合:

(1)、应用程序与数据库交互

一个应用中会存在大量的数据库操作,比如过数据库句柄来连接数据库这一行为,使用单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源。

(2)、控制配置信息

如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现.

三、如何实现单例模式?

1、普通的数据库访问例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php  
......  
//初始化一个数据库句柄  
$db new DB(...);  
   
//添加用户信息  
$db->addUserInfo(...);  
   
......  
   
//在函数中访问数据库,查找用户信息  
function getUserInfo()  
{  
    $db new DB(...);//再次new 数据库类,和数据库建立连接  
    $db = query(....);//根据查询语句访问数据库  
}  
   
?>

2、应用单例模式对数据库进行操作:

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
26
27
28
29
30
31
32
33
34
35
36
<?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(...)  
    {   
    }  
   
}  
   
//test    
$db = DB::getInstance();    
$db->addUserInfo(...);    
$db->getUserInfo(...);   
   
?>

下面的代码是PDO操作数据库类的一个封装,采用了单例模式:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
<?php
/**
 * MyPDO
 */
class MyPDO
{
    protected static $_instance = null;
    protected $dbName '';
    protected $dsn;
    protected $dbh;
     
    /**
     * 构造
     
     * @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 执行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;
    }
     
    /**
     * 获取指定列的数量
     
     * @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 事务开始
     */
    private function beginTransaction()
    {
        $this->dbh->beginTransaction();
    }
     
    /**
     * commit 事务提交
     */
    private function commit()
    {
        $this->dbh->commit();
    }
     
    /**
     * rollback 事务回滚
     */
    private function rollback()
    {
        $this->dbh->rollback();
    }
     
    /**
     * transaction 通过事务处理多条SQL语句
     * 调用前需通过getTableEngine判断表引擎是否支持事务
     *
     * @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 检查指定字段是否在指定数据表中存在
     *
     * @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 获取指定数据表中的全部字段名
     *
     * @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 关闭数据库连接
     */
    public function destruct()
    {
        $this->dbh = null;
    }
}
?>

调用方法:

1
2
3
4
5
6
<?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();
?>

PHP 单例模式优点意义及如何实现的更多相关文章

  1. PHP用单例模式实现一个数据库类

    使用单例模式的出发点: 1.php的应用主要在于数据库应用, 所以一个应用中会存在大量的数据库操作, 使用单例模式, 则可以避免大量的new 操作消耗的资源. 2.如果系统中需要有一个类来全局控制某些 ...

  2. PHP 单例模式

    一.什么是单例模式? 1.含义 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例.它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用. 2. ...

  3. PHP设计模式之:单例模式

        前 些日子开始着真正的去了解下设计模式,开始么,简单地从单例模式开始,当然网上看了一些资料,单例模式比较好理解,看看介绍,然后看看代码基本也就能够理 解了,设计模式这些的花点心思基本的是能够理 ...

  4. Android中的单例模式

    定义: 单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 使用场景: 确保某一个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一 ...

  5. php单例模式是怎么实现的呢

    <?php /** * 设计模式之单例模式 * $_instance必须声明为静态的私有变量 * 构造函数和析构函数必须声明为私有,防止外部程序new * 类从而失去单例模式的意义 * getI ...

  6. PHP之单例模式的实现

    单例模式: 单例模式又称职责模式:简单的说,一个对象(在学习设计模式之前,需要比较了解面向对象思想)只负责一个特定的任务: 单例类: 1.构造函数需要标记为private(访问控制:防止外部代码使用n ...

  7. php设计模式之单例模式

    单例模式顾名思义,就是只有一个实例.作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的要点有三个: 一是某个类只能有一个实例: 二是它必须自行 ...

  8. PHP设计模式之单例模式(数据库访问)

    1.什么是单例模式? 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例.它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用. 2.单例模式的 ...

  9. PHP 单例模式解析和实战

    一.什么是单例模式? 1.含义 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例.它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用. 2. ...

随机推荐

  1. Java并发之ReentrantLock

    一.ReentrantLock简介 ReentrantLock字面意义上理解为可重入锁.那么怎么理解可重入这个概念呢?或者说和我们经常用的synchronized又什么区别呢? ReentrantLo ...

  2. 在C++98基础上学习C++11新特性

    自己一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性.近期因为工作的需要,需要掌握C++11的一些特性,所以查阅了一些C++11资料.因为自己有C++98的基础,所以从C++98过 ...

  3. python_日历

    >>> import datetime>>> Today=datetime.date.today()>>> Todaydatetime.date( ...

  4. Spring Boot实战笔记(九)-- Spring高级话题(组合注解与元注解)

    一.组合注解与元注解 从Spring 2开始,为了响应JDK 1.5推出的注解功能,Spring开始大量加入注解来替代xml配置.Spring的注解主要用来配置注入Bean,切面相关配置(@Trans ...

  5. 一种轻量级的微信小程序日志监控的方法

    今天一个活动要写个H5,明天一个功能要用小程序,天天都在写bug.用户反馈小程序用起来有问题还特么还不知道到底出了啥bug,反馈多了,老板要扣工资了!看来挖了太多坑不填也不行,程序异常还是要主动追踪, ...

  6. JS中清空字符串前后空格

    在JS 操作的时候 长期会有对字符串的操作, 但是在JS 中不像在C#中 有字符串的方法 Trim() 来对字符串进行处理 .  所以自己写一个对字符串前后清楚空格的方法还是有必要的 以免以后忘记了 ...

  7. Debian虚拟机安装VirtualBox增强功能

    作者:荒原之梦 原文链接:http://zhaokaifeng.com/?p=573 本文中使用的Debian是安装在VirtualBox中的虚拟机,具体参数如下: Debian版本:Linux de ...

  8. css3D的魅力

    前言: 最近玩了玩用css来构建3D效果,写了几个demo,所以博客总结一下.  在阅读这篇博客之前,请先自行了解一下css 3D的属性,例如:transform-style,transform-or ...

  9. filddler一个抓包修改的工具 貌似很强大2017.12.07

    filddler直接百度下载  还没弄懂怎么用  有待研究!

  10. DOM元素的Attribute(特性)和Property(属性) 【转载】

    1.介绍: 上篇js便签笔记http://www.cnblogs.com/wangfupeng1988/p/3626300.html最后提到了dom元素的Attribute和Property,本文简单 ...