Use PHP's citation reason analysis with caution

  • 2020-05-24 05:17:43
  • OfStack

Reference types (Reference) are used in many computer languages and exist as a very powerful and useful feature. It has a pointer like (Pointer) implementation, but a different representation. For example, the reference of C++ can make different variables point to the same object, while maintaining the direct use of dot to get object members, without the tedious use of dereference operator (*) and Pointer to Member operator (-) > ). Java and C# use references directly as the primary type, and try to keep developers from using Pointers.

The reference type is also introduced in PHP, which is basically the same as Java/C# for reference passing (see Objects and references for details). But it also supports reference operators ( & ) to get a reference to the content. However, in practical use, the reference type of PHP has many problems due to the overall PHP design structure, resulting in unexpected results in the program.


A reference variable can be assigned a new reference

In C++, a variable of a reference type can only be assigned a reference value when it is defined, so we can know what the variable is manipulating just by tracing it to the definition of the variable.

However, PHP is different. The definition of variables is blurred in PHP, so you can use variables without defining them. So you can have variables be assigned reference values multiple times.

 
$x = 21; 
$y = 7; 

$z = &$x; 
$z = &$y; 

var_dump($x,$y,$z); 

At first glance, it feels like $z becomes a reference to $x, and then the content of $z becomes a reference to $y, meaning that $x and $z are paired with references to $y. But the actual output is:
 
int(21) 
int(7) 
int(7) 

As you can see from the results, $x remains the same, except that $z is changed to a reference to $y. This is equivalent to adding $z to unset and then adding a new value.
 
$z = &$x; 
unset($z); 
$z = &$y; 

For example, in the code below, we don't get a reference to a reference (Reference refer to a Referenece) that is similar to "pointer to pointer" (Pointer point to to a Pointer), but just multiple reference variables that refer to the same block of content.
 
$x = 21; 
$y = &$x; 
$z = &$y 


Referencing an array element makes that element a reference type

Taking a reference on a variable does not change the type of the original variable, but if you take an element from an array, it makes that element a reference type as well.

Before looking at the problem code, the first thing to point out is:
Array assignment always involves value copying. Use the reference operator to copy an array by reference.

That is, the array assignment for PHP is copy instead of a reference, and the assignment process creates a new array to assign to the assigned variable. An array operation on a new variable does not affect the contents of the original array variable.
 
$a = array(21, 7); 
$b = $a; 
$b[0] = 7; 
var_dump($a); 
echo '<br/>'; 
var_dump($b); 

//Output: 
//array(2) { [0]=> int(21) [1]=> int(7) } 
//array(2) { [0]=> int(7) [1]=> int(7) } 

Now let's see what happens if we reference an element in an array.
 
$a = array(21, 7); 
$c = & $a[0]; 
$b = $a; 
$b[0]= "21"; 
$b[1]= "7"; 

var_dump($a); 
echo '<br/>'; 
var_dump($b); 
echo '<br/>'; 
var_dump($c); 
echo '<br/>'; 

// Output: 
// array(2) { [0]=> &string(2) "21" [1]=> int(7) } 
// array(2) { [0]=> &string(2) "21" [1]=> string(1) "7" } 
// string(2) "21" 

In the code, $b is just a simple assignment with the previous one, except that it takes a reference to the first element in the previous one, but it should still copy a new array. The result is a change to $b, which also changes the first element of $a, while the second element has no effect.

From the output, we also see an unusual feature, which is that the first element of the array has an extra type of 1 '. & 'symbol. And this is the fetch reference operator. That is, the first element of the array has become a reference type. So the assignment is also a reference copy, not a value copy.

This is a strange problem, which also caused a lot of unnecessary trouble in the development. I originally thought that the copied array was not associated with the original array, but because of the unexpected reference type, I also affected the original array during the operation.

I don't know if this is bug in PHP or if it's intentional. After a long search on the Internet, there is no explanation for this convenience. Only Float Middle's PHP: References To Elements Elements Elements Risky and Symmetric Designs Problems w/accessing C array by reference Problems References C array by reference Designs Problems References References References C array by reference Reference reference Reference

Later, I saw several related reports in Bug Report of PHP (Bug6417, Bug7412, Bug15025, Bug20993). Some say it's an Bug and has been fixed in later versions. I didn't understand the details, so I had to avoid using references on arrays.

The more interesting thing is that if only one of the references in unset is left, then the array element will become a normal type with no reference.

 
unset($b); 
unset($c); 
var_dump($a); 

// Output: 
//array(2) { [0]=> string(2) "21" [1]=> int(7) } 


Avoid references to PHP

This is actually something to note in PHP Array Manual, most often in foreach, where you want to change the value of a far array by reference (see article).

In fact, we want to use foreach with reference to change the value of array elements, mainly because the array of PHP is Associative Array. This kind of array "has an indefinite length, the index can be discontinuous, and both string and integer can be used as index", so we cannot simply increase the integer index with for loop.

Of course we can change the values of the array elements directly with $key as in the code below, but this may have a definite efficiency problem.

 
foreach ($array_var as $key => $value) 
$array_var [$key] = $newValue; 

Another common place to use a reference is to pass parameters in a function call using a reference. The main reason is that you want the function implementation to return multiple return values in this way. For example, we want one representation to indicate whether error is present in the execution of the function and the return value is invalid.

However, because PHP's functions can return different types, there is no need to pass in a reference parameter as a representation. Even if you do need multiple return values, you can solve this problem by returning an "array of strings with the primary key," though you might need to indicate in the document that each element corresponds to that result.

A good way to do this is to use unset on a reference variable every time it is no longer needed and have it toggle with the content. And even if the variable is not a reference type, we make sure that it is no longer in use, and we have no problem calling unset on it. At least make sure that when you re-assign the variable later, it doesn't affect the previous result.

Problems w/accessing a PHP array by reference - Symmetric Designs PHP: References To Array Elements Are Risky Float Middle References and foreach - Johannes Schluter References Explained - PHP Manual


Related articles: