Some torture pits in yii2 frame

Time:2021-11-29

Gossip

It’s been a year since I last wrote a blog. Before writing, always with a deep sense of guilt. Tortured by it for a long time, finally, I did it.

One thing to celebrate: I started working out recently. 45 minutes of dynamic cycling and 45 minutes of swimming every day are really cool (not) to (e.g.) explosive (dead).

All right, that’s bullshit. Let’s get to the point.

Activerecord is written inexplicably?

Preparation knowledge

Basic usage of activerecord. If you don’t understand, please refer to here.

Code scene

/**
 * @property integer $id
 * @property string $name
 * @property string $detail
 * @property double $price
 * @property integer $area
 **/
class OcRoom extends ActivieRecord
{
 ...
}

$room = ocroom:: find() // take out an object first.
 ->Select (['id ']) // only the' ID 'column is retrieved
 ->where(['id'=>20])
 ->one();
$room->save();    // After saving, you will find that other fields in this line have been written as default values.

Summary questions

The problem with this example is:

  1. I took a row from the database, that is, $room in the code, but only the ID field was taken out, and other fields are naturally the default values.
  2. When I $room – > Save (), the fields with default values are also saved to the database. what!?
  3. In other words, when you want to save resources and do not take out all fields, you must pay attention not to save, otherwise, many data will be inexplicably modified to the default value.

resolvent

However, what solutions do we have? Several ideas are provided:

  1. Always pay attention to avoid saving activerecord that is not completely taken out.
  2. Modify or inherit activerecord so that when this object is newly created by find() and the field is not completely taken out, the save() method is called and an exception is thrown.
  3. Modify or inherit activerecord so that when this object is newly created by find() and the fields are not completely fetched, when the save() method is called, only the fetched fields are saved, and other fields are ignored.

Has your transaction taken effect?

Code scene

/**
 * @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 is an empty string and does not meet the rules() condition.

$b = new OcHouse();
$B - > name = 'my room'// Name is legal and can be saved.

$transaction = Yii::$app->db->beginTransaction();
try{
 $a->save();    // The name field is illegal and cannot be verified. False has been returned in the validate () stage. The database storage step will not be carried out, so no exception will be thrown.
 $b->save();    // The name field is legal and can be saved normally.

 $transaction->commit(); // After submitting, $a failed to save and $B succeeded.
}
catch (Exception $e) 
{
 Yii::error($e->getTraceAsString(),__METHOD__);
 $transaction->rollBack();
}

Problem summary

The problem with this code is:

  1. As we all know, the significance of $transaction is to ensure that the whole database storage code is either successful or failed.
  2. Obviously, in this example, the transaction does not achieve the desired effect: $a does not report an error when $transaction – > commit() because it has not passed validate().

resolvent

In the $transition block, all save() must determine the return value. If it is false, an exception will be thrown directly.

‘y-m-d’ is not recognized?

Code scene

OcRenterBill extends ActiveRecord
{
 public function rules()
 {
  return [
   ['start_time','date','format'=>'Y-m-d'],
  ];
 }
}

$a = new OcRenterBill();
$a = '2015-09-12';
$a->save();     // An error will be reported, saying that the format is wrong

Problem summary

If the Yii framework reports an error at the beginning, this is not a pit. The problem is that when I develop on the Mac, this can work completely normally, and after publishing to the online environment (Ubuntu), the error of “invalid format of attribute start_time” pops up. Referring to the official documents, it is found that this format is an allowed official document.

Ah, ah. All kinds of trial and error. Finally, I found that if it was changed to PHP: y-m-d, the world would be clean. So, if you encounter this kind of problem, thank me.

Memory leak

Code scene

public static function actionTest() {
  $total = 10;
  var_ Dump ('Start memory '. Memory_get_usage());
  while($total){
   $ret=User::findOne(['id'=>910002]);
   var_ Dump ('end memory '. Memory_get_usage());
   unset($ret);
   $total--;
  }
 }

The memory of the above code has been growing. According to the original idea, if the variables are released, the memory will not grow all the time even if it grows. Because the memory will be released every time the loop.

Analyze the problem. The above code involves the operation of the database, and we know that many parts of the database can cause memory leakage. Therefore, first shield the database related operations. I wrote a native database query operation and found that the memory was normal and there was no problem.

$dsn = "mysql:dbname=test;host=localhost";
$db_user = 'root';
$db_pass = 'admin';
//Inquiry
$sql = "select * from buyer";
$res = $pdo->query($sql);
foreach($res as $row) {
 echo $row['username'].'<br/>';
}

At this time, the answer is coming out – it’s yii2 framework

Now that we know that the positioning problem is a yii2 framework problem, we can further narrow the problem.

public static function actionTest() {
  $total = 10;
  var_ Dump ('Start memory '. Memory_get_usage());
  while($total){
   $ret= new User();
   var_ Dump ('end memory '. Memory_get_usage());
   unset($ret);
   $total--;
  }
 }

Memory is still growing. At this time, I tested another yii2 class and found that the memory did not grow. This can be associated with the operation performed by yii2 itself during the new object, which leads to memory leakage. What method is executed when new… Construction method of pair__ construct 。 Then I checked the object step by step from the model and found that there was nothing that could cause leakage.

At this time, we might as well change our thinking. Since it is a leak under the yii2 framework, it must be the unique function of yii2. What function is unique to yii2 and will be executed when the new object is used?

Behavior finds that behavior is really useful in my model class


public function behaviors()
 {
  return [
   TimestampBehavior::class,
  ];
 }

The most common code. We know that the last thing the action calls is yii\base\Component->attachBehaviors finally positioned.


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;
 }

We observed this code and found that he passed himself in $behavior – > attach ($this); The last call is 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);
  }
 }

Problem summary

At this time, the answer is ready to come out. In order to realize the function of behavior, yii2 transmits its own this so that it can register events, trigger events and cancel events. This leads to a circular reference problem. Therefore, the refcount of the object is not always 0 and cannot be recycled.

It’s easy to do next. Try changing the query to the original connection. Sure enough, the memory rises very slowly. It can be said that this is a normal phenomenon. The current memory is about 50m, and the CPU is stable at about 7%.

After code optimization, run the script again. After about 1 minute, the script will be finished. The point is that no more memory errors will be reported. Therefore, we should consider the problem further in the future. Dare to question. If you encounter this memory error in the future, you must first check whether your code has a memory leak. Don’t think about setting PHP’s memory first. This will only cure the symptoms, not the root cause.

summary

1. In terms of development speed, code can be generated quickly with the help of GII scaffold, that is, to build a system that can add, delete, modify and query, there may be no need to write a line of code, and jQuery and bootstrap are integrated, so there is basically no need to write special effects and styles, which is a great benefit for back-end programmers with poor design and aesthetic ability. However, under the trend of complete separation of the front and rear ends, the coupling of the front and rear ends of yii2 is still a little heavy.

2. In terms of code readability, Yii will not over design the code in order to rigidly follow a design pattern. Basically, classes can jump to the source code without the help of third-party components in the IDE. At this point, Yii is slightly better than laravel.

3. In terms of the open source ecosystem, Yii has few people and little information. It needs strong Google ability and the ability to read English documents.

It is undeniable that Yii is an excellent development framework, which is worth learning by PHP developers. The process of stepping on the pit is also a kind of growth and accumulation. Finally, I wish PHP partners good health and success in their careers.

Recommended Today

On the mutation mechanism of Clickhouse (with source code analysis)

Recently studied a bit of CH code.I found an interesting word, mutation.The word Google has the meaning of mutation, but more relevant articles translate this as “revision”. The previous article analyzed background_ pool_ Size parameter.This parameter is related to the background asynchronous worker pool merge.The asynchronous merge and mutation work in Clickhouse kernel is completed […]