浅谈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 Smarty生成EXCEL文档的代码
Aug 23 PHP
PHP中删除变量时unset()和null的区别分析
Jan 27 PHP
解析PHP函数array_flip()在重复数组元素删除中的作用
Jun 27 PHP
php中的常用魔术方法总结
Aug 02 PHP
使用php检测用户当前使用的浏览器是否为IE浏览器
Dec 03 PHP
php网站地图生成类示例
Jan 13 PHP
php 购物车完整实现代码
Jun 05 PHP
php输出指定时间以前时间格式的方法
Mar 21 PHP
php生成酷炫的四个字符验证码
Apr 22 PHP
PHP正则表达式过滤html标签属性(DEMO)
May 04 PHP
CI(CodeIgniter)框架实现图片上传的方法
Mar 24 PHP
PHP laravel中的多对多关系实例详解
Jun 07 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转成EXE文件
2006/10/09 PHP
修改PHP的memory_limit限制的方法分享
2012/02/21 PHP
php实现点击可刷新验证码
2015/11/07 PHP
asp.net下利用js实现返回上一页的实现方法小集
2009/11/24 Javascript
jquery 图片 上一张 下一张 链接效果(续篇)
2010/04/20 Javascript
JS 类型转换常见方法小结
2010/05/31 Javascript
使用jquery与图片美化checkbox和radio控件的代码(打包下载)
2010/11/11 Javascript
javascript 构造函数强制调用经验总结
2012/12/02 Javascript
一个简单的实现下拉框多选的插件可移植性比较好
2014/05/05 Javascript
jquery图片切换插件
2015/03/16 Javascript
MVC+jQuery.Ajax异步实现增删改查和分页
2020/12/22 Javascript
Google 地图获取API Key详细教程
2016/08/06 Javascript
基于Bootstrap的Metronic框架实现条码和二维码的生成及打印处理操作
2016/08/29 Javascript
微信小程序开发一键登录 获取session_key和openid实例
2016/11/23 Javascript
vuejs通过filterBy、orderBy实现搜索筛选、降序排序数据
2020/10/26 Javascript
vue-cli项目如何使用vue-resource获取本地的json数据(模拟服务端返回数据)
2017/08/04 Javascript
详解vue-cli脚手架中webpack配置方法
2018/08/22 Javascript
es6函数中的作用域实例分析
2020/04/18 Javascript
Python爬虫框架Scrapy安装使用步骤
2014/04/01 Python
Ubuntu 16.04 LTS中源码安装Python 3.6.0的方法教程
2016/12/27 Python
Python使用修饰器执行函数的参数检查功能示例
2017/09/26 Python
numpy matrix和array的乘和加实例
2018/06/28 Python
如何更优雅地写python代码
2019/07/02 Python
python PyAutoGUI 模拟鼠标键盘操作和截屏功能
2019/08/04 Python
python使用pip安装模块出现ReadTimeoutError: HTTPSConnectionPool的解决方法
2019/10/04 Python
5行Python代码实现图像分割的步骤详解
2020/05/25 Python
python爬虫利用selenium实现自动翻页爬取某鱼数据的思路详解
2020/12/22 Python
浅析移动设备HTML5页面布局
2015/12/01 HTML / CSS
H&M美国官网:欧洲最大的服饰零售商
2016/09/07 全球购物
英国演唱会订票网站:Ticket Selection
2018/03/27 全球购物
Stührling手表官方网站:男女高品质时尚手表的领先零售商
2021/01/07 全球购物
拉拉队口号
2014/06/16 职场文书
给老婆道歉的话
2015/01/20 职场文书
公司老总年会致辞
2015/07/30 职场文书
学风建设主题班会
2015/08/17 职场文书
Win11怎么启动任务管理器?Win11启动任务管理器的几种方法
2021/11/23 数码科技