说点闲话

距离上次写博客,已经有一年了。在动手写之前,总是带着深深的罪恶感。被它折磨许久,终于,还是,动手了。

值得庆祝的一件事:最近开始健身了。每天动感单车45分钟,游泳45分钟,真的是(生)爽(不)到(如)爆(死)。

好了,扯淡完毕,步入正题。

ActiveRecord被莫名写入?

准备知识

ActiveRecord的基本用法。如果不理解,可参考这里。

代码现场

/**

* @property integer $id

* @property string $name

* @property string $detail

* @property double $price

* @property integer $area

**/

class OcRoom extends ActivieRecord

{

...

}

$room = OcRoom::find() //先取出一个对象。

->select(['id']) //只取出'id'列

->where(['id'=>20])

->one();

$room->save(); //保存,会发现此行的其它字段都被写成默认值了。

总结问题

这个例子的问题在于:

我从数据库中取出了一行,也就是代码中的$room,但是只取出了id字段,而其他字段自然就是默认值。

当我$room->save()的时候,那些是默认值的字段也被保存到数据库里去了。what!?

也就是说,当你想节约资源,不取出所有字段的时候,一定要注意不能保存,否则,很多数据会被莫名修改为默认值。

解决方法

然而,我们有什么解决办法呢?提供几种思路:

自己时刻注意,避免未完全取出的ActiveRecord的保存。

修改或继承ActiveRecord, 使得,当此对象由find()新建,且字段没有完全取出,调用save()方法,抛出异常。

修改或继承ActiveRecord,使得,当此对象由find()新建,且字段没有完全取出,调用save()方法时,只保存取出过的字段,其他字段被忽略。

你的Transaction生效了吗?

代码现场

/**

* @property integer $id

* @property string $name

**/

class OcRoom extends ActiveRecord

{

public function rules()

{

return [['name','string','min'=>2,'max'=>10]];

}

...

}

class OcHouse extends ActiveRecord

{

public function rules()

{

return [['name','string','max'=>10]];

}

...

}

$a = new OcRoom();

$a->name = ''; //name为空字符串,不满足rules()条件。

$b = new OcHouse();

$b->name = '我的房间'; //name合法,可以保存。

$transaction = Yii::$app->db->beginTransaction();

try{

$a->save(); //name字段不合法,无法验证通过,在validate()阶段已经返回false,不会进行数据库存储的步骤,所以也不会抛出异常。

$b->save(); //name字段合法,可以正常保存。

$transaction->commit(); //提交后,发现$a保存失败,而$b保存成功。

}

catch (Exception $e)

{

Yii::error($e->getTraceAsString(),__METHOD__);

$transaction->rollBack();

}

问题总结

这段代码的问题在于:

大家知道$transaction的存在意义是保证整段数据库存储代码要么全成功,要么全失败。

显然,在这个例子中,transaction并没有达到我们想要的效果:$a因为validate()都没过,所以$transation->commit()的时候并不会报错。

解决方法

在$transation块内,所有的save()都要判断下返回值,如果为false,则直接抛出异常。

'Y-m-d'不被识别?

代码现场

OcRenterBill extends ActiveRecord

{

public function rules()

{

return [

['start_time','date','format'=>'Y-m-d'],

];

}

}

$a = new OcRenterBill();

$a = '2015-09-12';

$a->save(); //会报错,说格式不对

问题总结

如果一开始,Yii框架就报错,这个还不算坑。坑的是我在Mac上开发时,这个可以完全正常的工作,而发布到线上环境(Ubuntu)后,就弹出“属性start_time格式无效”的错误。而参考官方文档,发现这种格式是允许的官方文档。

啊啊啊。各种试错,最后发现如果改成php:Y-m-d,世界就清净了。所以,如果你遇到这种问题,感激我吧。

内存泄露

代码现场

public static function actionTest() {

$total = 10;

var_dump('开始内存'.memory_get_usage());

while($total){

$ret=User::findOne(['id'=>910002]);

var_dump('end内存'.memory_get_usage());

unset($ret);

$total--;

}

}

上面代码的内存一直在增长, 按照原本想法来看, 变量被释放了,内存就算增长也不会一直增长。因为每循环一次内存都会被释放。

分析问题 上面这段代码涉及到了数据库的操作,而我们知道,数据库的很多地方都能引起内存泄漏。 所以先屏蔽数据库相关操作, 我手写了一个原生的数据库查询操作, 发现内存正常,没有问题。

$dsn = "mysql:dbname=test;host=localhost";

$db_user = 'root';

$db_pass = 'admin';

//查询

$sql = "select * from buyer";

$res = $pdo->query($sql);

foreach($res as $row) {

echo $row['username'].'
';

}

这时候答案呼之欲出--- 是yii2框架搞了鬼

定位问题 既然知道了是yii2 框架的问题那就可以进一步缩小问题。

public static function actionTest() {

$total = 10;

var_dump('开始内存'.memory_get_usage());

while($total){

$ret= new User();

var_dump('end内存'.memory_get_usage());

unset($ret);

$total--;

}

}

内存还是一直增长。 这时候我测试了一个其他的yii2类 发觉内存不增长了。 这就可以联想到是在new 对象的时候yii2内部自己执行了什么操作,然后导致内存泄漏。 什么方法是new 的时候就执行的呢。。。 对的 构造方法 __construct 。 然后 我一步一步的从model 查到object 发觉都没有能引起泄漏的地方。

这个时候我们不妨换个思路, 既然是yii2框架下出现的泄漏, 那肯定就是yii2独有的功能, 那什么功能是yii2独有的,又是在new 对象的时候就会执行的呢?

行为(Behavior) 发觉我的模型类里面果然有用了行为

public function behaviors()

{

return [

TimestampBehavior::class,

];

}

最普通不过的代码。 我们知道 行为最后调用的地方是 yii\base\Component->attachBehaviors 最后定位到

private function attachBehaviorInternal($name, $behavior)

{

if (!($behavior instanceof Behavior)) {

$behavior = Yii::createObject($behavior);

}

if (is_int($name)) {

$behavior->attach($this);

$this->_behaviors[] = $behavior;

} else {

if (isset($this->_behaviors[$name])) {

$this->_behaviors[$name]->detach();

}

$behavior->attach($this);

$this->_behaviors[$name] = $behavior;

}

return $behavior;

}

我们观察这段代码,发觉他把自己传进去了$behavior->attach($this); 最后调用的是 yii\base\Behavior->attach

public function attach($owner)

{

$this->owner = $owner;

foreach ($this->events() as $event => $handler) {

$owner->on($event, is_string($handler) ? [$this, $handler] : $handler);

}

}

问题总结

这个时候答案已经呼之欲出, Yii2为了实现行为这一功能, 把自身this传进去,以便能注册事件、触发事件、解除事件。 这就导致了一个循环引用的问题。 所以导致对象refcount一直不为0 一直回收不了。

接下来就好办了。将查询换成原始的连接试试。果然,内存上升的非常慢了,可以说这才是正常现象。现在的内存也就是50m左右,cpu也稳定在7%左右。

代码优化后,再跑脚本,1分钟左右吧,脚本就跑完了。重点是不会再报出内存错误了。所以,以后考虑问题还是要深入。敢于质疑。以后如果遇到这种内存错误,一定要先检查自己的代码是不是有内存泄漏的地方。不要想着先设置php的内存。这样只会治标不治本。

总结

1、从开发速度方面,借助于gii脚手架,可以快速生成代码,也就是说搭建一个可以增删改查的系统可能一行代码都不用写,而且集成了jquery和bootstrap,特效和样式基本也不需要写了,这对于设计和审美能力普遍较差的后端程序员来说简直是一大福利。不过在前后端完全的分离的趋势下,Yii2前后端的耦合的还是有些重了。

2、从代码的可读性方面,Yii不会为了刻板地遵照某种设计模式而对代码进行过度的设计。基本上类在IDE里不借助第三方组件是可以跳转阅读源码的。这点上Yii要比Laravel略胜一筹。

3、从开源生态圈方面,Yii因为人少,稍微偏门一点的资料就很少,需要强大的谷歌能力和阅读英文文档的能力。

不可否认,Yii是一个优秀的开发框架,值得PHP开发者上手学习,踩坑的过程也是一种成长与积累。最后祝愿PHP小伙伴们都健健康康,事业有成。

yii2必须掌握php,Yii2框架中一些折磨人的坑相关推荐

  1. 在YII2框架中使用UEditor编辑器发布文章

    在YII2框架中使用UEditor编辑器发布文章 创建文章数据表 文章数据表主要有4个字段 id  主键(int) title 标题(varchar) content 内容(text) created ...

  2. yii引入php文件,Yii2框架中CSS、JS文件引入要领_PHP开发框架教程

    在yii2中,因为yii2版本升级致使了,许多yii2的用法跟yii1有着很大的区分,这几天一直在view层的视图界面徜徉着,碰到什么问题呢? (引荐进修:yii框架) 问题就是搞不清我该怎样去引入C ...

  3. php 接收curl json数据格式,curl发送 JSON格式POST数据的接收,以及在yii2框架中的实现原理【精细剖析】...

    1.通过curl发送json格式的数据,譬如代码: function http_post_json($url, $jsonStr) { $ch = curl_init(); curl_setopt($ ...

  4. 在php的yii2框架中整合hbase库

    为什么80%的码农都做不了架构师?>>>    Hbase通过thrift这个跨语言的RPC框架提供多语言的调用. Hbase有两套thrift接口(thrift1和thrift2) ...

  5. behavior php,YII2框架中behavior行为的理解与使用方法示例

    本文实例讲述了YII2框架中behavior行为的理解与使用方法.分享给大家供大家参考,具体如下: YII2中的行为说白了就是对组件功能的扩展,在不改变继承关系的条件下. 行为附加到组件后,行为将注入 ...

  6. YII2框架中 where limit offset 函数对分页的使用

    YII2框架中 where limit offset 函数对分页的使用 以前公司用的是TP框架,直接往limit函数里面传两个动态的参数就可以实现分页的效果了,刚换了公司这个公司用的是YII2 框架, ...

  7. 手工集成7牛SDK到YII2框架中

    手工集成7牛SDK到YII2框架中 7牛地址:qiniu.com 7牛云的产品列表中有:对象存储.自定义数据处理.多媒体处理.融合CDN加速.直播空间等资源. 我们上传图片文件需要的是『对象存储』,关 ...

  8. 教你在Yii2.0框架中如何创建自定义小部件

    本教程将帮助您创建自己的自定义小部件在 yii framework 2.0.部件是可重用的模块和用于视图. 创建一个小部件,需要继承 yii\base\Widget,覆盖重写 yii\base\Wid ...

  9. yii2 redis封装类 php,yii2项目中如何使用redis

    想要在Yii2这个PHP框架中很好的使用redis键值存储,那么首先就要推荐yii2-redis这个官方的Github库.这个库能够很好的帮助我们在Yii2框架中使用redis,它提供缓存,Sessi ...

最新文章

  1. dede修改mysql,Dedecms(织梦)程序MySQL修复表和文章路径修改方法
  2. 微软软件推送服务器,向 UWP 应用添加推送通知 - Azure Mobile Apps | Microsoft Docs
  3. python窗口程序-窗口程序python
  4. hibernate里的generator中class =value介绍
  5. oracle逻辑结构包含,在Oracle中,逻辑结构由哪几个部分组成?
  6. c++图书管理系统_轻松学做C语言课程设计:图书管理系统-数组实现
  7. 用python定义一个员工类_python类的定义和使用
  8. Anuglar中的常用通道-大小写转换、日期转换、小数位数、Json、slice、管道链
  9. 俄罗斯方块剖析之一总体计划
  10. EF CodeFirst 如何通过配置自动创建数据库当模型改变时
  11. CodeForces - 796D Police Stations bfs
  12. HTTPS 的 7 次握手以及 9 倍时延
  13. 图像形状特征提取c语言,OpenCV_局部图像特征的提取与匹配_源代码
  14. 【每日一练 088】性能优化-SQL tuning(一)
  15. [译] 第十一天: AeroGear Push Server - 轻松推送提示信息
  16. 【图像分割】基于matlab FCM侧扫声呐图像分割【含Matlab源码 1478期】
  17. input datetime-local 时间控件精确到秒
  18. 22牛客多校1 J.Serval and Essay (启发式合并)
  19. go语言导入自定义包出现: package xxx is not in GOROOT (/xxx/xxx) 的解决方案
  20. 成都链安预警:EOS竞猜类游戏SKReos再次遭受攻击

热门文章

  1. 移动app开发如何做接口的版本控制
  2. [笔记] 《互联网思维独孤九剑》
  3. 字母哥36+15稳固第一 塔图姆18中2绿军失利
  4. 毒蛇咬伤后的紧急处理
  5. python实现对比两个有增减的excel表格的不同并填充不同颜色
  6. Unity物体破碎效果
  7. Nginx——Session共享
  8. 率土之滨鸿蒙团,率土之滨平民武将组合 平民搭配什么阵容
  9. armbian取消休眠去屏保并安装中文输入法
  10. 制育秧钵机/铲平机/C616数控车床/车床主传动系统/熟料圆锥式破碎机/船舶起重机/龙门铣床/桥式起重机/采煤机摇臂减速箱/圆锥筛/通道穿梭车/车床尾座体/变速器拨叉/刮板输送机/油菜排种器……设计