Ignored magic-delay value assignment for php reference (late data Delay Binding) and value assignment binding
I am talking about the variable reference feature of php today, but what is the problem of delayed assignment? This is mainly an idea I have recently optimized some features. I think it is still good and I plan to record it. Look at the pseudocode below the example:
// This code may be used to avoid table join, because the efficiency of table join is 3 to 20 times that of table join for some business needs. // This is basically the same in my project, much more efficient than previously joined tables $ a = DB: query ("select id from a"); $ aid = ""; foreach ($ a as $ v) {$ aid. = $ v ['id']. ',';} $ aid = substr ($ aid, 0,-1); if ($ aid) {$ B = DB :: query ("select * from B where aid in ({$ aid})"); // omitted here}
The reason for using this code example is that there are a lot of code similar to this, which is easy to understand, but not necessarily suitable for assigning values with latencies in the future, because it is more understandable and more efficient, however, it can be in stark contrast with the delayed assignment in the later stages, so that you can better understand the implementation methods below.
After reading the above example, let's look at a complicated requirement. The data is required to obtain a list of the last 10 articles by one person, read 5 comments from each article, and contain the id of the article initiator and reviewer, name: Package the data into json in the specified format and return it to the client. See the following code:
// This requirement is far from being used to obtain user information from a join table. It is highly efficient to collect user IDs for in queries. $ data = array (); $ article = DB: query ("select id, uid, title, content from article where uid = {$ _ GET ['uid']} order by id desc limit 10 "); foreach ($ article as $ v) {$ uid = $ v ['uid']; $ comment = DB: query ("select id, uid, content from comment where aid = {$ v ['id']} order by id asc limit 5 "); foreach ($ comment as $ value) {$ uid. = ','. $ value ['uid'];} // here, the second parameter requires that the array returned by the DB class be indexed with uid $ member = DB: query ("select uid, username from user where uid in ({$ uid}) ", 'uid'); $ commentList = array (); $ data [] = array ('id' => $ v ['id'], 'title' => $ v ['title'], 'content' => $ v ['content'], 'uid' => $ v ['uid'], 'username' => $ member [$ v ['uid'] ['username'], 'comment' => & $ commentList); foreach ($ comment as $ value) {$ commentList [] = array ('id' => $ value ['id'], 'content' => $ value ['content'], 'uid' => $ value ['uid'], 'username' => $ member [$ value ['uid'] ['username'])} echo json_encode ($ data); exit;
After carefully reading this code, we will find that the value of $ data [] ['comment'] references the variable $ commentList at the beginning, and then changes the value of $ commentList at the end, at the same time, the value of $ data [] ['comment'] is changed along with each other. Here, the value of latency assignment is also later, but it is relatively simple. You can learn about this implementation principle accordingly.
I believe most people have written similar code, and few people think this Code may be faulty. I will analyze the logic of this article. The comment information is that five articles are required to be obtained. This cannot be merged into one simple SQL statement. As the requirement is to obtain only 10 articles, writing complex SQL statements is not as efficient as loop queries (If your intranet latency is low, if you consider thousands of data records, but if you are dealing with tens of millions of data records, complex SQL statements are not as efficient as simple SQL statements, so here we can't continue to optimize loop queries. However, we can see that user information is also queried in a loop. This is not good. I do not want to see such code in my project team. 10 posts are initiated by one, there will be a lot of data for active users in the last 50 comments, so that the probability of Duplicate User Information queried in each loop is very high, in a business logic, it is best not to obtain duplicate information from the database but to reuse it. How can we reuse user information? You can obtain the comment information of the article cyclically before assembling the final data, collect the user ID, obtain the user information, and then assemble the final data. This is a simple solution, not the focus of today. Let's look at an elegant processing method-latency assignment.
// This requirement is far from being used to obtain user information from a join table. It is highly efficient to collect user IDs for in queries. $ data = array (); $ article = DB: query ("select id, uid, title, content from article where uid = {$ _ GET ['uid']} order by id desc limit 10 "); $ member = array (); foreach ($ article as $ v) {$ comment = DB: query ("select id, uid, content from comment where aid = {$ v ['id']} order by id asc limit 5 "); $ commentList = array (); $ data [] = array ('id' => $ v ['id'], 'title' => $ v ['title'], 'content' => $ v ['content'], 'uid' => $ v ['uid'], 'username' => & $ member [$ v ['uid'] ['username'], 'comment' => & $ commentList ); foreach ($ comment as $ value) {$ commentList [] = array ('id' => $ value ['id'], 'content' => $ value ['content'], 'uid' => $ value ['uid'], 'username' => & $ member [$ value ['uid'] ['username']) }}$ uid = array_keys ($ member); if ($ uid) {$ uid = implode (',', $ uid); $ user = DB: query ("select uid, username from user where uid in ({$ uid })", 'uid'); foreach ($ member as $ uid => $ value) {$ member [$ uid] ['username'] = $ user [$ uid] ['username'];} unset ($ member, $ user );} echo json_encode ($ data); exit;
This code is not quite the same as before. The most obvious thing is that there is a piece of code at the bottom, and we don't know what it is for now. It seems useless. Let's look at it step by step, first, a variable $ member = array () is initialized before the circular document data, and then $ uid assignment is missing in the loop, and the reviewer's id is collected cyclically, in addition, the SQL statement for querying user data is gone, as if to the bottom of the Code that cannot be understood. After carefully searching, we found that & $ member [$ v ['uid'] ['username'] And & $ member [$ value ['uid'] ['username'] There are more & referenced symbols in the place, this is why code writing is missing in the loop. Recall that $ commentList was assigned a value after it was referenced and changed $ data [] ['comment']. This principle is the same. First, no user information is queried and only an empty reference is performed. When a non-existent variable is referenced, php creates this variable first, for example, & $ member [$ v ['uid'] ['username'], php detection $ member is an array already declared, however, if $ member [$ v ['uid'] ['username'] does not exist, it is created in the memory and its value is null.
After the data is printed in the loop, the username information is null. Of course, the user information is not used before. php gave us a null value when referencing and assigning values. Then get the id information of all users through $ uid = array_keys ($ member,
Why can array_keys obtain the user ID? Because php helped us create the $ member array When referencing it. Note that the uid here is unique, after that, we will use in to retrieve user information in the user table. We must note that the returned data cannot be assigned to $ member because the previous data references the data in $ member. If $ member is overwritten, the addresses of the two variables in the memory are different, which is equivalent to re-creating an array. Here we assign a value to $ user. What is the following loop, of course, the value of the referenced data is modified, in the $ member variable, $ user [$ uid] ['username'] is assigned to $ member [$ uid] ['username'] to change the value of the referenced variable. After we bind the data to the referenced variable, do not ignore the use of uset to delete $ member, mainly to prevent code that operates the $ member variable from appearing in subsequent code, accidentally overwrite the previously bound data. Why is there no data loss after $ member is deleted? It is mainly a reference feature. When multiple variables reference one memory address, deleting one of the variables does not affect other variables, the data in the memory will be deleted unless all variables are deleted. As explained in the php manual, "When unset is a reference, the binding between the variable name and the variable content is broken. This does not mean that the variable content is destroyed ". Unset ($ user); because $ user is a temporary variable, it can be directly released from the memory after use.
For this programming method, I named it "Late data latency binding". The title is "latency variable assignment", which is easy to understand. Php references a wide range of functions. The examples mentioned in this article are only a partial method of use, which is used to solve a similar business needs in programming, of course, the Programming Method of Data Delay Binding in the later stage is also widely used. I hope you will not be limited to the scenarios in this example. The example in this article is one of the most common problems in programming. I have compiled a function to handle data Delay Binding, reducing the need to write data binding logic everywhere.
/*** Common data Delay Binding method ** @ access public * @ param array $ bindingVar variable to be bound * @ param array $ data to be bound * @ param mixed $ default default Value * @ return void */function bindingData (& $ bindingVar, $ data, $ default = '') {foreach ($ bindingVar as $ key => $ tmp) {foreach ($ tmp as $ k => $ v) {$ bindingVar [$ key] [$ k] = isset ($ data [$ key] [$ k])? $ Data [$ key] [$ k]: $ default ;}} unset ($ bindingVar );}
Using this function, we can change the code that previously processes data binding to the following:
$uid = array_keys($member);if($uid){$user = DB::query("select uid,username from user where uid in({$uid})", 'uid');bindingData($member, $user);unset($member,$user);}