浅谈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 相关文章推荐
使用php4加速网络传输
Oct 09 PHP
使PHP自定义函数返回多个值
Nov 26 PHP
php feof用来识别文件末尾字符的方法
Aug 01 PHP
PHP开发的一些注意点总结
Oct 12 PHP
php清空(删除)指定目录下的文件,不删除目录文件夹的实现代码
Sep 04 PHP
php出现web系统多域名登录失败的解决方法
Sep 30 PHP
浅析php原型模式
Nov 25 PHP
php 中奖概率算法实现代码
Jan 25 PHP
php数组指针操作详解
Feb 14 PHP
由php中字符offset特征造成的绕过漏洞详解
Jul 07 PHP
laravel框架数据库配置及操作数据库示例
Oct 10 PHP
解决在laravel中leftjoin带条件查询没有返回右表为NULL的问题
Oct 15 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 中的类
2006/10/09 PHP
PHP解析html类库simple_html_dom的转码bug
2014/05/22 PHP
PHP实现的文件上传类与用法详解
2017/07/05 PHP
php获取微信共享收货地址的方法
2017/12/21 PHP
浅谈PHP中的Trait使用方法
2019/03/22 PHP
IE下js调试工具Companion.JS
2010/10/15 Javascript
浅谈javascript的原型继承
2012/07/25 Javascript
jquery中push()的用法(数组添加元素)
2014/11/25 Javascript
js对字符的验证方法汇总
2015/02/04 Javascript
JavaScript中的继承之类继承
2016/05/01 Javascript
js删除数组元素、清空数组的简单方法(必看)
2016/07/27 Javascript
JS实现图片局部放大或缩小的方法
2016/08/20 Javascript
使用JS在浏览器中判断当前网络连接状态的几种方法
2017/05/05 Javascript
基于vue-simplemde实现图片拖拽、粘贴功能
2018/04/12 Javascript
JS实现的倒计时恢复按钮点击功能【可用于协议阅读倒计时】
2018/04/19 Javascript
vue实现条件判断动态绑定样式的方法
2018/09/29 Javascript
layui实现图片虚拟路径上传,预览和删除的例子
2019/09/25 Javascript
js实现拖动缓动效果
2020/01/13 Javascript
使用js原生实现年份轮播选择效果实例
2021/01/12 Javascript
Python引用(import)文件夹下的py文件的方法
2014/08/26 Python
如何高效使用Python字典的方法详解
2017/08/31 Python
Python实现统计英文文章词频的方法分析
2019/01/28 Python
Python2和Python3的共存和切换使用
2019/04/12 Python
TensorFLow 不同大小图片的TFrecords存取实例
2020/01/20 Python
Python flask框架端口失效解决方案
2020/06/04 Python
Python利用Pillow(PIL)库实现验证码图片的全过程
2020/10/04 Python
使用python-cv2实现Harr+Adaboost人脸识别的示例
2020/10/27 Python
英国奢侈品概念店:Base Blu
2019/05/16 全球购物
EM Cosmetics官网:由彩妆大神Michelle Phan创办的独立品牌
2020/04/27 全球购物
用JAVA实现一种排序,JAVA类实现序列化的方法(二种)
2014/04/23 面试题
教师竞聘上岗演讲稿
2014/09/03 职场文书
民主评议党员自我评价材料
2014/09/18 职场文书
十岁生日答谢词
2015/01/05 职场文书
雾霾停课通知
2015/04/24 职场文书
MySQL 自定义变量的概念及特点
2021/05/13 MySQL
MySQL数据库必备之条件查询语句
2021/10/15 MySQL