- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Yii2框架中一些折磨人的坑由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
说点闲话 。
距离上次写博客,已经有一年了。在动手写之前,总是带着深深的罪恶感。被它折磨许久,终于,还是,动手了.
值得庆祝的一件事:最近开始健身了。每天动感单车45分钟,游泳45分钟,真的是(生)爽(不)到(如)爆(死).
好了,扯淡完毕,步入正题.
ActiveRecord被莫名写入?
准备知识 。
ActiveRecord的基本用法。如果不理解,可参考这里.
代码现场 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* @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();
//保存,会发现此行的其它字段都被写成默认值了。
|
总结问题 。
这个例子的问题在于:
解决方法 。
然而,我们有什么解决办法呢?提供几种思路:
你的Transaction生效了吗?
代码现场 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
/**
* @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();
}
|
问题总结 。
这段代码的问题在于:
解决方法 。
在$transation块内,所有的save()都要判断下返回值,如果为false,则直接抛出异常.
'Y-m-d'不被识别?
代码现场 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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,世界就清净了。所以,如果你遇到这种问题,感激我吧.
内存泄露 。
代码现场 。
1
2
3
4
5
6
7
8
9
10
|
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
--;
}
}
|
上面代码的内存一直在增长, 按照原本想法来看, 变量被释放了,内存就算增长也不会一直增长。因为每循环一次内存都会被释放.
分析问题 上面这段代码涉及到了数据库的操作,而我们知道,数据库的很多地方都能引起内存泄漏。 所以先屏蔽数据库相关操作, 我手写了一个原生的数据库查询操作, 发现内存正常,没有问题.
1
2
3
4
5
6
7
8
9
|
$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'
].
'<br/>'
;
}
|
这时候答案呼之欲出--- 是yii2框架搞了鬼 。
定位问题 既然知道了是yii2 框架的问题那就可以进一步缩小问题.
1
2
3
4
5
6
7
8
9
10
|
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) 发觉我的模型类里面果然有用了行为 。
1
2
3
4
5
6
|
public
function
behaviors()
{
return
[
TimestampBehavior::
class
,
];
}
|
最普通不过的代码。 我们知道 行为最后调用的地方是 yii\base\Component->attachBehaviors 最后定位到 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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 。
1
2
3
4
5
6
7
|
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小伙伴们都健健康康,事业有成.
原文链接:https://www.cnblogs.com/zydj333/p/12038025.html 。
最后此篇关于Yii2框架中一些折磨人的坑的文章就讲到这里了,如果你想了解更多关于Yii2框架中一些折磨人的坑的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
前言 每日站会(Daily Standup)是团队统一节奏的、在固定时间发生的、帮助团队内部快速同步进展的敏捷实践活动: 站会的目的是让团队能更好地对齐 Sprint 目标;
jdbcTemplate 中的queryForList,你真的懂吗? 你想象中的queryForList是不是应该长成下面这种模样? String sql = "select *
python是一门清晰简洁的语言,如果你对一些细节不了解的话,就会掉入到那些深不见底的“坑”里,下面,我就来总结一些python里常见的坑。 列表创建和引用 嵌套列表的创建 使用*号来创建一个
如今,在DevOps当中建立安全体系显得比以往任何时候都更加重要。《2021年企业DevOps技能提升报告》指出,56%的受访者表示DevSecOps已经成为自动化工具中的一大必备要素。然而,D
前言 相信看到这个题目,可能大家都觉得是一个老生常谈的月经topic了。一直以来其实把握一个“值传递”基本上就能理解各种情况了,不过最近遇到了更深一点的“小坑”,与大家分享一下。 首先还是从最简
前言 Go 中的for range组合可以和方便的实现对一个数组或切片进行遍历,但是在某些情况下使用for range时很可能就会被"坑",下面用一段代码来模拟下:
大家好,我是明哥。 在开始之前,先考你一个非常 Go 味的经典问题:如何判断一个 interface{} 的值是否为 nil ? 这也是面试有可能会被问到的一个问题,这个问题很 “迷”,平时
ava并发包有很大一部分内容都是关于并发容器的,因此学习和搞懂这部分的内容很有必要。 Java 1.5 之前提供的同步容器虽然也能保证线程安全,但是性能很差,而 Java 1.5 版本之后提供的并发
大家好,我是煎鱼。 前几天在读者交流群里看到一位小伙伴,针对 interface 的使用有了比较大的疑惑。 无独有偶,我也在网上看到有小伙伴在 Go 面试的时候被问到了:
我是一名优秀的程序员,十分优秀!