PHP单例模式是什么 php实现单例模式的方法


Posted in PHP onMay 14, 2016

一、什么是单例模式?
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;  
 
}

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

2、单例模式在PHP中的应用场合:
(1)、应用程序与数据库交互
  一个应用中会存在大量的数据库操作,比如过数据库句柄来连接数据库这一行为,使用单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源。
(2)、控制配置信息
 如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现.

三、如何实现单例模式?
1、普通的数据库访问例子:

<?php 
...... 
//初始化一个数据库句柄 
$db = new DB(...); 
 
//添加用户信息 
$db->addUserInfo(...); 
 
...... 
 
//在函数中访问数据库,查找用户信息 
function getUserInfo() 
{ 
  $db = new DB(...);//再次new 数据库类,和数据库建立连接 
  $db = query(....);//根据查询语句访问数据库 
} 
 
?>

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

<?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操作数据库类的一个封装,采用了单例模式:

<?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');
    } 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语句
   * @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 执行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;
  }
}
?>

调用方法:

<?php
require 'MyPDO.class.php';
$db = MyPDO::getInstance('localhost', 'root', '123456', 'test', 'utf8');
$db->query("select count(*) frome table");
$db->destruct();
?>

以上就是本文的全部内容,希望对大家学习php程序设计有所帮助。

PHP 相关文章推荐
使用PHP遍历文件夹与子目录的函数代码
Sep 26 PHP
第三章 php操作符与控制结构代码
Dec 30 PHP
解析Linux下Varnish缓存的配置优化
Jun 20 PHP
php网站被挂木马后的修复方法总结
Nov 06 PHP
分享一段PHP制作的中文拼音首字母工具类
Dec 11 PHP
试用php中oci8扩展
Jun 18 PHP
Thinkphp模板标签if和eq的区别和比较实例分析
Jul 01 PHP
php数组函数array_walk用法示例
May 26 PHP
php UNIX时间戳用法详解
Feb 16 PHP
PHP简单实现二维数组的矩阵转置操作示例
Nov 24 PHP
PHP 的Opcache加速的使用方法
Dec 29 PHP
PHP使用正则表达式实现过滤非法字符串功能示例
Jun 04 PHP
PHP pear安装配置教程
May 14 #PHP
php+html5+ajax实现上传图片的方法
May 14 #PHP
yii2使用ajax返回json的实现方法
May 14 #PHP
php文件上传类完整实例
May 14 #PHP
Smarty高级应用之缓存操作技巧分析
May 14 #PHP
php生成Android客户端扫描可登录的二维码
May 13 #PHP
php短信接口代码
May 13 #PHP
You might like
星际争霸兵种名称对照表
2020/03/04 星际争霸
无线电广播的开始
2002/01/30 无线电
咖啡历史、消费和行业趋势
2021/03/03 咖啡文化
PHP中的string类型使用说明
2010/07/27 PHP
php mysql_real_escape_string函数用法与实例教程
2013/09/30 PHP
学习YUI.Ext第七日-View&amp;JSONView Part Two-一个画室网站的案例
2007/03/10 Javascript
js每次Title显示不同的名言
2008/09/25 Javascript
js String对象中常用方法小结(字符串操作)
2012/01/27 Javascript
Javascript中的for in循环和hasOwnProperty结合使用
2013/06/05 Javascript
jquery.form.js用法之清空form的方法
2014/03/07 Javascript
JavaScript的设计模式经典之代理模式
2016/02/24 Javascript
JavaScript学习总结之正则的元字符和一些简单的应用
2017/06/30 Javascript
AngularJS实现的select二级联动下拉菜单功能示例
2017/10/25 Javascript
JavaScript函数节流和函数去抖知识点学习
2018/07/31 Javascript
解决Angular2 router.navigate刷新页面的问题
2018/08/31 Javascript
在Vue中实现随hash改变响应菜单高亮
2020/03/09 Javascript
python单链表实现代码实例
2013/11/21 Python
Python2.6版本中实现字典推导 PEP 274(Dict Comprehensions)
2015/04/28 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
Python 合并多个TXT文件并统计词频的实现
2019/08/23 Python
Pytorch 计算误判率,计算准确率,计算召回率的例子
2020/01/18 Python
python对数组进行排序,并输出排序后对应的索引值方式
2020/02/28 Python
Python语言编写智力问答小游戏功能
2020/10/13 Python
Python+logging输出到屏幕将log日志写入文件
2020/11/11 Python
用60行代码实现Python自动抢微信红包
2021/02/04 Python
泰国第一在线超市:Tops
2021/02/13 全球购物
无毒社区工作方案
2014/05/23 职场文书
排查整治工作方案
2014/06/09 职场文书
领导班子自我剖析材料
2014/08/16 职场文书
党员四风问题对照检查材料思想汇报
2014/09/16 职场文书
2016年母亲节寄语
2015/12/04 职场文书
解决redis sentinel 频繁主备切换的问题
2021/04/12 Redis
详解缓存穿透击穿雪崩解决方案
2021/05/28 Redis
Python编程编写完善的命令行工具
2021/09/15 Python
Python基础 括号()[]{}的详解
2021/11/07 Python
JavaScript阻止事件冒泡的方法
2021/12/06 Javascript