您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Yii2框架中有什么坑,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
ActiveRecord被莫名寫(xiě)入?
準(zhǔn)備知識(shí)
ActiveRecord的基本用法。如果不理解,可參考這里。
代碼現(xiàn)場(chǎng)
/** * @property integer $id * @property string $name * @property string $detail * @property double $price * @property integer $area **/ class OcRoom extends ActivieRecord { ... } $room = OcRoom::find() //先取出一個(gè)對(duì)象。 ->select(['id']) //只取出'id'列 ->where(['id'=>20]) ->one(); $room->save(); //保存,會(huì)發(fā)現(xiàn)此行的其它字段都被寫(xiě)成默認(rèn)值了。
總結(jié)問(wèn)題
這個(gè)例子的問(wèn)題在于:
我從數(shù)據(jù)庫(kù)中取出了一行,也就是代碼中的$room,但是只取出了id字段,而其他字段自然就是默認(rèn)值。
當(dāng)我$room->save()的時(shí)候,那些是默認(rèn)值的字段也被保存到數(shù)據(jù)庫(kù)里去了。what!?
也就是說(shuō),當(dāng)你想節(jié)約資源,不取出所有字段的時(shí)候,一定要注意不能保存,否則,很多數(shù)據(jù)會(huì)被莫名修改為默認(rèn)值。
解決方法
然而,我們有什么解決辦法呢?提供幾種思路:
自己時(shí)刻注意,避免未完全取出的ActiveRecord的保存。
修改或繼承ActiveRecord, 使得,當(dāng)此對(duì)象由find()新建,且字段沒(méi)有完全取出,調(diào)用save()方法,拋出異常。
修改或繼承ActiveRecord,使得,當(dāng)此對(duì)象由find()新建,且字段沒(méi)有完全取出,調(diào)用save()方法時(shí),只保存取出過(guò)的字段,其他字段被忽略。
你的Transaction生效了嗎?
代碼現(xiàn)場(chǎng)
/** * @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字段不合法,無(wú)法驗(yàn)證通過(guò),在validate()階段已經(jīng)返回false,不會(huì)進(jìn)行數(shù)據(jù)庫(kù)存儲(chǔ)的步驟,所以也不會(huì)拋出異常。 $b->save(); //name字段合法,可以正常保存。 $transaction->commit(); //提交后,發(fā)現(xiàn)$a保存失敗,而$b保存成功。 } catch (Exception $e) { Yii::error($e->getTraceAsString(),__METHOD__); $transaction->rollBack(); }
問(wèn)題總結(jié)
這段代碼的問(wèn)題在于:
大家知道$transaction的存在意義是保證整段數(shù)據(jù)庫(kù)存儲(chǔ)代碼要么全成功,要么全失敗。
顯然,在這個(gè)例子中,transaction并沒(méi)有達(dá)到我們想要的效果:$a因?yàn)関alidate()都沒(méi)過(guò),所以$transation->commit()的時(shí)候并不會(huì)報(bào)錯(cuò)。
解決方法
在$transation塊內(nèi),所有的save()都要判斷下返回值,如果為false,則直接拋出異常。
'Y-m-d'不被識(shí)別?
代碼現(xiàn)場(chǎng)
OcRenterBill extends ActiveRecord { public function rules() { return [ ['start_time','date','format'=>'Y-m-d'], ]; } } $a = new OcRenterBill(); $a = '2015-09-12'; $a->save(); //會(huì)報(bào)錯(cuò),說(shuō)格式不對(duì)
問(wèn)題總結(jié)
如果一開(kāi)始,Yii框架就報(bào)錯(cuò),這個(gè)還不算坑??拥氖俏以贛ac上開(kāi)發(fā)時(shí),這個(gè)可以完全正常的工作,而發(fā)布到線上環(huán)境(Ubuntu)后,就彈出“屬性start_time格式無(wú)效”的錯(cuò)誤。而參考官方文檔,發(fā)現(xiàn)這種格式是允許的官方文檔。
啊啊啊。各種試錯(cuò),最后發(fā)現(xiàn)如果改成php:Y-m-d,世界就清凈了。所以,如果你遇到這種問(wèn)題,感激我吧。
內(nèi)存泄露
代碼現(xiàn)場(chǎng)
public static function actionTest() { $total = 10; var_dump('開(kāi)始內(nèi)存'.memory_get_usage()); while($total){ $ret=User::findOne(['id'=>910002]); var_dump('end內(nèi)存'.memory_get_usage()); unset($ret); $total--; } }
上面代碼的內(nèi)存一直在增長(zhǎng), 按照原本想法來(lái)看, 變量被釋放了,內(nèi)存就算增長(zhǎng)也不會(huì)一直增長(zhǎng)。因?yàn)槊垦h(huán)一次內(nèi)存都會(huì)被釋放。
分析問(wèn)題 上面這段代碼涉及到了數(shù)據(jù)庫(kù)的操作,而我們知道,數(shù)據(jù)庫(kù)的很多地方都能引起內(nèi)存泄漏。 所以先屏蔽數(shù)據(jù)庫(kù)相關(guān)操作, 我手寫(xiě)了一個(gè)原生的數(shù)據(jù)庫(kù)查詢操作, 發(fā)現(xiàn)內(nèi)存正常,沒(méi)有問(wèn)題。
$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/>'; }
這時(shí)候答案呼之欲出--- 是yii2框架搞了鬼
定位問(wèn)題 既然知道了是yii2 框架的問(wèn)題那就可以進(jìn)一步縮小問(wèn)題。
public static function actionTest() { $total = 10; var_dump('開(kāi)始內(nèi)存'.memory_get_usage()); while($total){ $ret= new User(); var_dump('end內(nèi)存'.memory_get_usage()); unset($ret); $total--; } }
內(nèi)存還是一直增長(zhǎng)。 這時(shí)候我測(cè)試了一個(gè)其他的yii2類(lèi) 發(fā)覺(jué)內(nèi)存不增長(zhǎng)了。 這就可以聯(lián)想到是在new 對(duì)象的時(shí)候yii2內(nèi)部自己執(zhí)行了什么操作,然后導(dǎo)致內(nèi)存泄漏。 什么方法是new 的時(shí)候就執(zhí)行的呢。。。 對(duì)的 構(gòu)造方法 __construct 。 然后 我一步一步的從model 查到object 發(fā)覺(jué)都沒(méi)有能引起泄漏的地方。
這個(gè)時(shí)候我們不妨換個(gè)思路, 既然是yii2框架下出現(xiàn)的泄漏, 那肯定就是yii2獨(dú)有的功能, 那什么功能是yii2獨(dú)有的,又是在new 對(duì)象的時(shí)候就會(huì)執(zhí)行的呢?
行為(Behavior) 發(fā)覺(jué)我的模型類(lèi)里面果然有用了行為
public function behaviors() { return [ TimestampBehavior::class, ]; }
最普通不過(guò)的代碼。 我們知道 行為最后調(diào)用的地方是 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; }
我們觀察這段代碼,發(fā)覺(jué)他把自己傳進(jìn)去了$behavior->attach($this); 最后調(diào)用的是 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); } }
問(wèn)題總結(jié)
這個(gè)時(shí)候答案已經(jīng)呼之欲出, Yii2為了實(shí)現(xiàn)行為這一功能, 把自身this傳進(jìn)去,以便能注冊(cè)事件、觸發(fā)事件、解除事件。 這就導(dǎo)致了一個(gè)循環(huán)引用的問(wèn)題。 所以導(dǎo)致對(duì)象refcount一直不為0 一直回收不了。
接下來(lái)就好辦了。將查詢換成原始的連接試試。果然,內(nèi)存上升的非常慢了,可以說(shuō)這才是正常現(xiàn)象?,F(xiàn)在的內(nèi)存也就是50m左右,cpu也穩(wěn)定在7%左右。
代碼優(yōu)化后,再跑腳本,1分鐘左右吧,腳本就跑完了。重點(diǎn)是不會(huì)再報(bào)出內(nèi)存錯(cuò)誤了。所以,以后考慮問(wèn)題還是要深入。敢于質(zhì)疑。以后如果遇到這種內(nèi)存錯(cuò)誤,一定要先檢查自己的代碼是不是有內(nèi)存泄漏的地方。不要想著先設(shè)置php的內(nèi)存。這樣只會(huì)治標(biāo)不治本。
總結(jié)
1、從開(kāi)發(fā)速度方面,借助于gii腳手架,可以快速生成代碼,也就是說(shuō)搭建一個(gè)可以增刪改查的系統(tǒng)可能一行代碼都不用寫(xiě),而且集成了jquery和bootstrap,特效和樣式基本也不需要寫(xiě)了,這對(duì)于設(shè)計(jì)和審美能力普遍較差的后端程序員來(lái)說(shuō)簡(jiǎn)直是一大福利。不過(guò)在前后端完全的分離的趨勢(shì)下,Yii2前后端的耦合的還是有些重了。
2、從代碼的可讀性方面,Yii不會(huì)為了刻板地遵照某種設(shè)計(jì)模式而對(duì)代碼進(jìn)行過(guò)度的設(shè)計(jì)。基本上類(lèi)在IDE里不借助第三方組件是可以跳轉(zhuǎn)閱讀源碼的。這點(diǎn)上Yii要比Laravel略勝一籌。
3、從開(kāi)源生態(tài)圈方面,Yii因?yàn)槿松?,稍微偏門(mén)一點(diǎn)的資料就很少,需要強(qiáng)大的谷歌能力和閱讀英文文檔的能力。
關(guān)于“Yii2框架中有什么坑”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。