欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Yii2框架中一些折磨人的坑

 更新時(shí)間:2019年12月15日 09:43:28   作者:有痣青年  
這篇文章主要給大家介紹了關(guān)于Yii2框架中一些折磨人的坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Yii2框架具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

說(shuō)點(diǎn)閑話

距離上次寫博客,已經(jīng)有一年了。在動(dòng)手寫之前,總是帶著深深的罪惡感。被它折磨許久,終于,還是,動(dòng)手了。

值得慶祝的一件事:最近開始健身了。每天動(dòng)感單車45分鐘,游泳45分鐘,真的是(生)爽(不)到(如)爆(死)。

好了,扯淡完畢,步入正題。

ActiveRecord被莫名寫入?

準(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)此行的其它字段都被寫成默認(rèn)值了。

總結(jié)問(wèn)題

這個(gè)例子的問(wèn)題在于:

  1. 我從數(shù)據(jù)庫(kù)中取出了一行,也就是代碼中的$room,但是只取出了id字段,而其他字段自然就是默認(rèn)值。
  2. 當(dāng)我$room->save()的時(shí)候,那些是默認(rèn)值的字段也被保存到數(shù)據(jù)庫(kù)里去了。what!?
  3. 也就是說(shuō),當(dāng)你想節(jié)約資源,不取出所有字段的時(shí)候,一定要注意不能保存,否則,很多數(shù)據(jù)會(huì)被莫名修改為默認(rèn)值。

解決方法

然而,我們有什么解決辦法呢?提供幾種思路:

  1. 自己時(shí)刻注意,避免未完全取出的ActiveRecord的保存。
  2. 修改或繼承ActiveRecord, 使得,當(dāng)此對(duì)象由find()新建,且字段沒有完全取出,調(diào)用save()方法,拋出異常。
  3. 修改或繼承ActiveRecord,使得,當(dāng)此對(duì)象由find()新建,且字段沒有完全取出,調(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)題在于:

  1. 大家知道$transaction的存在意義是保證整段數(shù)據(jù)庫(kù)存儲(chǔ)代碼要么全成功,要么全失敗。
  2. 顯然,在這個(gè)例子中,transaction并沒有達(dá)到我們想要的效果:$a因?yàn)関alidate()都沒過(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é)

如果一開始,Yii框架就報(bào)錯(cuò),這個(gè)還不算坑??拥氖俏以贛ac上開發(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('開始內(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)操作, 我手寫了一個(gè)原生的數(shù)據(jù)庫(kù)查詢操作, 發(fā)現(xiàn)內(nè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('開始內(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類 發(fā)覺內(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ā)覺都沒有能引起泄漏的地方。

這個(gè)時(shí)候我們不妨換個(gè)思路, 既然是yii2框架下出現(xiàn)的泄漏, 那肯定就是yii2獨(dú)有的功能, 那什么功能是yii2獨(dú)有的,又是在new 對(duì)象的時(shí)候就會(huì)執(zhí)行的呢?

行為(Behavior) 發(fā)覺我的模型類里面果然有用了行為

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ā)覺他把自己傳進(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ō)這才是正?,F(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、從開發(fā)速度方面,借助于gii腳手架,可以快速生成代碼,也就是說(shuō)搭建一個(gè)可以增刪改查的系統(tǒng)可能一行代碼都不用寫,而且集成了jquery和bootstrap,特效和樣式基本也不需要寫了,這對(duì)于設(shè)計(jì)和審美能力普遍較差的后端程序員來(lái)說(shuō)簡(jiǎn)直是一大福利。不過(guò)在前后端完全的分離的趨勢(shì)下,Yii2前后端的耦合的還是有些重了。

2、從代碼的可讀性方面,Yii不會(huì)為了刻板地遵照某種設(shè)計(jì)模式而對(duì)代碼進(jìn)行過(guò)度的設(shè)計(jì)?;旧项愒贗DE里不借助第三方組件是可以跳轉(zhuǎn)閱讀源碼的。這點(diǎn)上Yii要比Laravel略勝一籌。

3、從開源生態(tài)圈方面,Yii因?yàn)槿松?,稍微偏門一點(diǎn)的資料就很少,需要強(qiáng)大的谷歌能力和閱讀英文文檔的能力。

不可否認(rèn),Yii是一個(gè)優(yōu)秀的開發(fā)框架,值得PHP開發(fā)者上手學(xué)習(xí),踩坑的過(guò)程也是一種成長(zhǎng)與積累。最后祝愿PHP小伙伴們都健健康康,事業(yè)有成。

相關(guān)文章

  • CodeIgniter多語(yǔ)言實(shí)現(xiàn)方法詳解

    CodeIgniter多語(yǔ)言實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了CodeIgniter多語(yǔ)言實(shí)現(xiàn)方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了CodeIgniter實(shí)現(xiàn)多語(yǔ)言的具體步驟、實(shí)現(xiàn)方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2016-01-01
  • 在VS?Code?中調(diào)試遠(yuǎn)程服務(wù)器的PHP代碼詳解

    在VS?Code?中調(diào)試遠(yuǎn)程服務(wù)器的PHP代碼詳解

    這篇文章主要介紹了在VSCode中調(diào)試遠(yuǎn)程服務(wù)器的PHP代碼,這里通過(guò)xdebug配合vscode的php?debug插件來(lái)實(shí)現(xiàn),本文結(jié)合實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • PHP內(nèi)核探索之解釋器的執(zhí)行過(guò)程

    PHP內(nèi)核探索之解釋器的執(zhí)行過(guò)程

    這篇文章主要介紹了PHP內(nèi)核探索之解釋器的執(zhí)行過(guò)程的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • 利用laravel+ajax實(shí)現(xiàn)文件上傳功能方法示例

    利用laravel+ajax實(shí)現(xiàn)文件上傳功能方法示例

    最近在做的項(xiàng)目基于Laravel開發(fā),需要用到Ajax上傳,查了些資料,自己整理了下可用的方法。下面這篇文章主要給大家介紹了關(guān)于利用laravel+ajax實(shí)現(xiàn)文件上傳功能的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-08-08
  • 數(shù)組任意位置插入元素,刪除特定元素的實(shí)例

    數(shù)組任意位置插入元素,刪除特定元素的實(shí)例

    下面小編就為大家?guī)?lái)一篇數(shù)組任意位置插入元素,刪除特定元素的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • laravel框架數(shù)據(jù)庫(kù)操作、查詢構(gòu)建器、Eloquent ORM操作實(shí)例分析

    laravel框架數(shù)據(jù)庫(kù)操作、查詢構(gòu)建器、Eloquent ORM操作實(shí)例分析

    這篇文章主要介紹了laravel框架數(shù)據(jù)庫(kù)操作、查詢構(gòu)建器、Eloquent ORM操作,結(jié)合實(shí)例形式分析了laravel數(shù)據(jù)庫(kù)連接、增刪改查、排序及Eloquent ORM數(shù)據(jù)庫(kù)操作等相關(guān)使用技巧,需要的朋友可以參考下
    2019-12-12
  • php 在線導(dǎo)入mysql大數(shù)據(jù)程序

    php 在線導(dǎo)入mysql大數(shù)據(jù)程序

    我想很多人經(jīng)常會(huì)用phpmyadmin進(jìn)行數(shù)據(jù)的導(dǎo)入與導(dǎo)出,但是在很多使用虛擬主機(jī)的情況下,導(dǎo)出沒什么問(wèn)題但是導(dǎo)入就存在很大的問(wèn)題了,我想這里我也不多說(shuō)了站長(zhǎng)都會(huì)知道了.好了我們現(xiàn)在來(lái)看看php把mysql數(shù)據(jù)庫(kù)里面的數(shù)據(jù)導(dǎo)入程序吧.
    2015-06-06
  • PHP處理Ajax請(qǐng)求與Ajax跨域問(wèn)題

    PHP處理Ajax請(qǐng)求與Ajax跨域問(wèn)題

    這篇文章主要介紹了PHP處理Ajax請(qǐng)求與Ajax跨域問(wèn)題的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-02-02
  • php實(shí)現(xiàn)微信原生支付(掃碼支付)功能

    php實(shí)現(xiàn)微信原生支付(掃碼支付)功能

    這篇文章主要為大家詳細(xì)介紹了php實(shí)現(xiàn)微信原生支付,掃碼支付功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • PHP自動(dòng)補(bǔ)全表單的兩種方法

    PHP自動(dòng)補(bǔ)全表單的兩種方法

    這篇文章主要介紹了PHP自動(dòng)補(bǔ)全表單的兩種方法,第一種從數(shù)據(jù)庫(kù)中檢索之后補(bǔ)全,第二種郵箱等純前端的補(bǔ)全,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03

最新評(píng)論