When a built-in function is defined with ZEND_SEND_PREFER_REF, PHP will issue a strict warning if you use an assignment expression as the parameter.
As an example, current() show this behaviour. current() has this arginfo defined: ZEND_BEGIN_ARG_INFO(arginfo_current, ZEND_SEND_PREFER_REF) ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, arg) ZEND_END_ARG_INFO() and a call like: <?php current($foo = array("bar")); ?> Presents you with: PHP Strict Standards: Only variables should be passed by reference in %s on line %d I think it is wrong to warn about this, because, to me, the _PREFER_ part of ZEND_SEND_PREFER_REF, conveys that we should prefer to pass by reference, if possible, and not care otherwise. The below patch implements the not care part that was missing until now. This is done by having the lexer mark the result variable of the assignment expression as not passable by reference, and changing the function zend_do_pass_param() to ignore the marked variables when considering if a parameter could be passed by reference or not. I have run make test, before and after, with no regressions. The only test affected was the one I sent earlier to specifically test for this bug. I am, however, not sure if this is the right approach to solve the problem, in which case, I hope to at least have put one of you on the right track to the _real_ solution, and to have made you interested in fixing it properly. The patch is for php-5.3.8 Daniel K. diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c325a7e..df30d3d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2096,7 +2096,7 @@ void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC) /* {{ if (function_ptr) { if (ARG_MAY_BE_SENT_BY_REF(function_ptr, (zend_uint) offset)) { - if (param->op_type & (IS_VAR|IS_CV)) { + if (param->op_type & (IS_VAR|IS_CV) && param->u.EA.type != ZEND_PARSED_EXPR_NO_PASS_BY_REF) { send_by_reference = 1; if (op == ZEND_SEND_VAR && zend_is_function_or_method_call(param)) { /* Method call */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 15f24df..475f976 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -650,6 +650,7 @@ int zendlex(znode *zendlval TSRMLS_DC); #define ZEND_PARSED_VARIABLE (1<<4) #define ZEND_PARSED_REFERENCE_VARIABLE (1<<5) #define ZEND_PARSED_NEW (1<<6) +#define ZEND_PARSED_EXPR_NO_PASS_BY_REF (1<<7) /* unset types */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 1753e97..666b9e2 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -578,7 +578,7 @@ non_empty_for_expr: expr_without_variable: T_LIST '(' { zend_do_list_init(TSRMLS_C); } assignment_list ')' '=' expr { zend_do_list_end(&$$, &$7 TSRMLS_CC); } - | variable '=' expr { zend_check_writable_variable(&$1); zend_do_assign(&$$, &$1, &$3 TSRMLS_CC); } + | variable '=' expr { zend_check_writable_variable(&$1); zend_do_assign(&$$, &$1, &$3 TSRMLS_CC); $$.u.EA.type = ZEND_PARSED_EXPR_NO_PASS_BY_REF; } | variable '=' '&' variable { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$4, BP_VAR_W, 1 TSRMLS_CC); zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); zend_do_assign_ref(&$$, &$1, &$4 TSRMLS_CC); } | variable '=' '&' T_NEW class_name_reference { zend_error(E_DEPRECATED, "Assigning the return value of new by reference is deprecated"); zend_check_writable_variable(&$1); zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$4, &$5 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$3, &$4, &$7 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); $3.u.EA.type = ZEND_PARSED_NEW; zend_do_assign_ref(&$$, &$1, &$3 TSRMLS_CC); } | T_NEW class_name_reference { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$1, &$2 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$$, &$1, &$4 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php