PHP error suppressor of @ results in a reference pass failure Bug analysis

  • 2020-05-05 11:03:10
  • OfStack

Consider the following example:
 
<?php 
$array = array(1,2,3); 
function add (&$arr) { 
$arr[] = 4; 
} 
add(@$array); 
print_r($array); 
/** 
 At this time , $array There is no change , The output : 
Array 
( 
[0] => 1 
[1] => 2 
[2] => 3 
) 
*/ 
add($array); 
print_r($array); 
/** 
 Without error suppression ,  The output is normal : 
Array 
( 
[0] => 1 
[1] => 2 
[2] => 3 
[3] => 4 
) 
*/ 
?> 

This problem, I not met before, so in the first place to look for related information, see if there are any ready-made answers, Goolge, found that while someone already quoted PHP similar Bug: http: / / bugs php. net/bug php? id=47623, but PHP has not been officially resolved, nor has there been a reply.

Can't, can oneself are analyzed, before I have been introduced in the article the principle of error suppression operator (understand PHP principle of error suppression and embedded HTML), in principle, the error suppression just changed error_reporting level, it will not affect the morally context between the mechanism of the function call. Only through field trials.

After tracing gdb, it was found that after the error port was used, opcode before the function call was different :

 
// When no error suppressor is used  
OPCODE = SEND_REF 
// After using the error suppression symbol  
OPCODE = SEND_VAR_NO_RE 

The problem has been identified, but what is the cause of the discrepancy?

Now that OPCODE is different, it must be in the parsing phase, it has different branches, and when you think about this layer, the problem is easy to locate,

In the PHP parsing phase, the entries in the form of "@" +expr were reduced to expr_without_variable. The meaning of this node is the value of no variable, which is the literal value. We all know that the literal value cannot pass the reference (because it is not a variable), so this difference is caused by

The specific process is as follows:
1. Grammar analysis stage:
 
expr_without_variable: 
//... There are omitted  
| '@' { zend_do_begin_silence(&$1 TSRMLS_CC); } 
expr { zend_do_end_silence(&$1 TSRMLS_CC); $$ = $3; } 
// Go here ZEND_SEND_VAL branch  
non_empty_function_call_parameter_list: 
expr_without_variable { ....} // I took this branch by mistake  
| variable {..... } // normal  

As a result, different OPCODE were generated during compilation, which also resulted in the appearance of the problem.
Finally, I have explained the reason on PHP's bug page, and those who are interested can check my poor English level

Related articles: