浅谈thinkphp5 instance 的简单实现


Posted in PHP onJuly 30, 2017

最近学习 ThinkPHP5,第一次看到 TestClass::instance() 就能创建 TestClass 实例的方法。感到很好奇,翻阅 ThinkPHP 的源代码,大体理解了 它的 设计思想,非常的先进。

老规矩,直接上代码:

<?php
class TestClass {
 
 public static function instance() {
  return new self();
 }
 
 public $data = [];
 
 public function __set($name, $val) {
  return $this->data[$name] = $val;
 }
 
 public function __get($name) {
  return $this->data[$name];
 }
}
 
$app1 = TestClass::instance();
$app1->key = 'Application 1';
echo $app1->key . '<br />';
?>

为了方便调用,也模仿 ThinkPHP 写了一个助手函数

<?php
function app() {
 return TestClass::instance();
}
 
$app2 = app();
$app2->key = 'Application 2';
echo $app2->key . '<br />';
?>

这样就简单的实现了 instance。

不过这种方法还有一个小问题,试想以下,调用100次,就需要创建100个实例,想想都觉得可怕。

给 Test 类 增加一个 静态属性,将创建的实例保存到这里。下次如果需要调用,则直接调用这个实例。

<?php
class TestClass {
 
 public static $instance; //用于缓存实例
 
 public $data = [];
 
 public static function instance() {
  //如果不存在实例,则返回实例
  if (empty(self::$instance)) {
   self::$instance = new self();
  }
  return self::$instance;
 }
 
 public function __set($name, $val) {
  return $this->data[$name] = $val;
 }
 
 public function __get($name) {
  return $this->data[$name];
 }
 
}
 
function app($option = []) {
 return TestClass::instance($option);
}
 
header('content-type:text/plain');
 
$result = [];
$app1 = app();
$app1->key = "Application 1"; //修改 key 为 Application 1
$result['app1'] = [
 'app1' => $app1->key, //实例中 key 为 Application 1
];
 
// 创建 app2,因为 instance 已经存在实例,直接返回 缓存的实例
$app2 = app();
$result['app2'] = [
 'setp1' => [
  'app1' => $app1->key, // Application 1
  'app2' => $app2->key, //因为直接调用的实例的缓存,所以 key 也是 Application 1
 ],
];
 
// 无论 app1,app2 都对在内存中 对应的同一个实例,无论通过谁修改,都能改变值
$app1->key = "Application 2";
$result['app2']['setp2'] = [
 'app1' => $app1->key, // Application 2
 'app2' => $app2->key, // Application 2
];
print_r($result);
?>

通过上边的实验,可以看到 无论调用多少次,都会使用同一个实例。这样就解决了效率低的问题。

到现在基本就满足大多数情况了,唯一的小缺陷,就是 可能 实例的 初始参数不同,这样没法灵活调用(常见的比如同一个程序调用两个数据库)。在 上边的 例子中稍作改造,以传入的参数为key,将不通的 实例缓存到数组中 就可以解决。

<?php
class TestClass {
 
 public static $instance = []; //用于缓存实例数组
 public $data = [];
 
 public function __construct($opt = []) {
  $this->data = $opt;
 }
 
 public static function instance($option = []) {
  // 根据传入的参数 通过 serialize 转换为字符串,md5 后 作为数组的 key
  $instance_id = md5(serialize($option));
  //如果 不存在实例,则创建
  if (empty(self::$instance[$instance_id])) {
   self::$instance[$instance_id] = new self($option);
  }
  return self::$instance[$instance_id];
 }
 
 public function __set($name, $val) {
  return $this->data[$name] = $val;
 }
 
 public function __get($name) {
  return $this->data[$name];
 }
 
}
 
function app($option = []) {
 return TestClass::instance($option);
}
 
header('content-type:text/plain');
 
$result = [];
//传入 初始数据
$app1 = app(['key' => '123']);
$result['init'] = $app1->key; // 使用 传入的数据,即:123
$app1->key = "app1";
$result['app'] = $app1->key; // 现在值改为了 自定义的 app1了
print_r($result);
 
$result = [];
// 创建 app2,注意 初始参数不一样
$app2 = app();
// 因为初始参数不一样,所以还是创建新的实例
$app2->key = "app2";
$result['app1'] = $app1->key; // app1
$result['app2'] = $app2->key; // app2
print_r($result);
 
$result = [];
// 创建 app3,传入的参数 和 app1 一样,所以会直接返回 和app1相同 的 实例
$app3 = app(['key' => '123']);
$result['log'] = [
 'app1' => $app1->key, // app1
 'app2' => $app2->key, // app2
 'app3' => $app3->key, // app1
];
 
// 设置 app3 的key,会自动修改 app1 的值,因为他们两个是同一个实例
$app3->key = 'app3';
$result['app3_set'] = [
 'app1' => $app1->key, // app3
 'app2' => $app2->key, // app2
 'app3' => $app3->key, // app3
];
 
// 同理,设置 app1 的key,app3 的 key 也会修改
$app1->key = 'app1';
$result['app1_set'] = [
 'app1' => $app1->key, // app1
 'app2' => $app2->key, // app2
 'app3' => $app3->key, // app1
];
print_r($result);
?>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
多php服务器实现多session并发运行
Oct 09 PHP
php 静态变量的初始化
Nov 15 PHP
php中计算中文字符串长度、截取中文字符串的函数代码
Aug 09 PHP
PHP中执行MYSQL事务解决数据写入不完整等情况
Jan 07 PHP
php图片缩放实现方法
Feb 20 PHP
php中current、next与reset函数用法实例
Nov 17 PHP
thinkphp如何获取客户端IP
Nov 03 PHP
PHP命名空间namespace的定义方法详解
Mar 29 PHP
ThinkPHP框架实现数据增删改
May 07 PHP
laravel-admin解决表单select联动时,编辑默认没选上的问题
Sep 30 PHP
Laravel框架实现即点即改功能的方法分析
Oct 31 PHP
TP5框架页面跳转样式操作示例
Apr 05 PHP
PHP用PDO如何封装简单易用的DB类详解
Jul 30 #PHP
详解PHP防止直接访问.php 文件的实现方法
Jul 28 #PHP
php简单实现单态设计模式的方法分析
Jul 28 #PHP
[原创]PHP实现SQL语句格式化功能的方法
Jul 28 #PHP
使用php自动备份数据库表的实现方法
Jul 28 #PHP
PHP自定义函数判断是否为Get、Post及Ajax提交的方法
Jul 27 #PHP
PHP 7安装使用体验之性能大提升,兼容性强,扩展支持不够(升级PHP要谨慎)
Jul 27 #PHP
You might like
PHP的单引号和双引号 字符串效率
2009/05/27 PHP
PHP代码网站如何防范SQL注入漏洞攻击建议分享
2012/03/01 PHP
smarty内置函数capture用法分析
2015/01/22 PHP
php对象工厂类完整示例
2018/08/09 PHP
PHP+redis实现的购物车单例类示例
2019/02/02 PHP
Laravel使用Queue队列的技巧汇总
2019/09/02 PHP
laravel Model 执行事务的实现
2019/10/10 PHP
整理Javascript数组学习笔记
2015/11/29 Javascript
点评js异步加载的4种方式
2015/12/22 Javascript
详解获取jq ul第一个li定位的四种解决方案
2016/11/23 Javascript
基于vue-simplemde实现图片拖拽、粘贴功能
2018/04/12 Javascript
vue模块拖拽实现示例代码
2019/03/09 Javascript
Vue实现微信支付功能遇到的坑
2019/06/05 Javascript
基于Vue中使用节流Lodash throttle详解
2019/10/30 Javascript
JavaScript中break、continue和return的用法区别实例分析
2020/03/02 Javascript
JavaScript中ES6规范中let和const的用法和区别
2020/08/06 Javascript
在arcgis使用python脚本进行字段计算时是如何解决中文问题的
2015/10/18 Python
Python如何快速实现分布式任务
2017/07/06 Python
Python环境搭建之OpenCV的步骤方法
2017/10/20 Python
对python3 urllib包与http包的使用详解
2018/05/10 Python
Python随机生成身份证号码及校验功能
2018/12/04 Python
Python实现定时自动关闭的tkinter窗口方法
2019/02/16 Python
Django基础三之视图函数的使用方法
2019/07/18 Python
Python协程操作之gevent(yield阻塞,greenlet),协程实现多任务(有规律的交替协作执行)用法详解
2019/10/14 Python
Python SSL证书验证问题解决方案
2020/01/13 Python
python GUI库图形界面开发之PyQt5 UI主线程与耗时线程分离详细方法实例
2020/02/26 Python
python实现拼接图片
2020/03/23 Python
HTML5几个设计和修改的页面范例分享
2015/09/29 HTML / CSS
彪马日本官网:PUMA日本
2019/01/31 全球购物
Linux Interview Questions For software testers
2013/05/17 面试题
软件测试工程师面试问题精选
2016/10/28 面试题
小学班主任寄语大全
2014/04/04 职场文书
品质口号大全
2014/06/17 职场文书
高中教师先进事迹材料
2014/08/22 职场文书
PyTorch 实现L2正则化以及Dropout的操作
2021/05/27 Python
Python创建SQL数据库流程逐步讲解
2022/09/23 Python