php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #52937 call_user_func warning is inappropriate
Submitted: 2010-09-27 19:31 UTC Modified: 2015-03-23 07:51 UTC
Votes:5
Avg. Score:3.8 ± 0.7
Reproduced:5 of 5 (100.0%)
Same Version:2 (40.0%)
Same OS:1 (20.0%)
From: mryaggi at hotmail dot com Assigned:
Status: Wont fix Package: Scripting Engine problem
PHP Version: 5.3.3 OS: Seven
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: mryaggi at hotmail dot com
New email:
PHP Version: OS:

 

 [2010-09-27 19:31 UTC] mryaggi at hotmail dot com
Description:
------------
call_user_func now issue a warning in PHP5.3 when giving a value instead of a 
reference.
However, this warning shows up in inappropriate cases.

Test script:
---------------
<?php
//Function to be called via call_user_func
function test(&$z)
{
  echo "ok : " . $z . "\n";
}

// - 1 : With a local variable
//This should work, but...
$a = 1;
call_user_func('test',$a);//Warning: Parameter 1 to test() expected to be a reference, value given

// - 2 : Giving a constant
//This should issue a warning but ...
call_user_func('test',2);//works fine. Output "ok : 2"

// - 3 : Base on a parameter
//This should work, but ...
function test3($p=3)
{
call_user_func('test',$p);//Warning: Parameter 1 to test() expected to be a reference, value given
}
test3();
?>

Expected result:
----------------
ok : 1
<b>Warning</b>:  Parameter 1 to test() expected to be a reference, value given in 
...
ok : 3

Actual result:
--------------
<b>Warning</b>:  Parameter 1 to test() expected to be a reference, value given in 
...
ok : 2
<br />
<b>Warning</b>:  Parameter 1 to test() expected to be a reference, value given in 
...

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-09-28 03:13 UTC] cataphract@php.net
Yes, this may be confusing. The problem is that the level of indirection added means when the function test() is actually called by the implementation of call_user_func() via zend_call_function, information about the arguments is lost. Use call_user_func_array().

Tests 1 and 3 are basically the same. The manual for call_user_func has a note:

«Note that the parameters for call_user_func() are not passed by reference.»

Since call_user_func doesn't receive its parameters by reference, by the time they reach it, it has no way of knowing if the parameters were sent by reference or not.

Test 2 is a different matter. But you are actually passing a reference to test() because zend_call_function() is nice and when you pass a non-reference to a function that expects a reference and the non-reference has refcount 1, it converts it into a reference. See

http://lxr.php.net/opengrok/xref/PHP_TRUNK/Zend/zend_execute_API.c#860

Again, this makes sense if you're writing an extension and using zend_call_function() and passing it a variable you've created. I don't see how this can be fixed, except by adding something like fci->no_separation that doesn't create a reference even if the refcount is 1. Not sure if it's worth the trouble.
 [2010-09-29 19:31 UTC] mryaggi at hotmail dot com
Thank you for your quick reply.
You say : "Use call_user_func_array()."
But you can run the test script with call_user_func_array() instad, and still you will get those warnings.

My point is : when you call call_user_func you have no idea whether the function called expects references or not (values).

The only solution I see is to always give references to call_user_func*()

For ex:
-----------------
function MY_call_user_func_array($Func,$Args)
{
  foreach(array_keys($Args) as $i) { $Args[$i] =& $Args[$i]; }//make it a ref
  call_user_func_array($Func,$Args);
}

function test(&$z){  echo "ok : " . $z . "\n";}
$a = 1;
MY_call_user_func_array('test',array($a));
-----------------

And here we are! IT WORKS!
but it I feel like I just learnt how to poo in PHP...
 [2010-10-03 00:02 UTC] cataphract@php.net
You *should* know whether the function takes references; it's part of its signature.

If it does take references you cannot use call_user_func, and have to use call_user_func_array instead.
 [2010-10-11 11:00 UTC] mryaggi at hotmail dot com
Ok, I'm going to keep it simple.

You say "[I] have to use call_user_func_array instead".
Please show me how.

Note : I'm just trying to avoid the warning properly.

This would be quite a solution :
call_user_func('ExprectRef',               &   $p);
call_user_func_array('ExprectRef',array(   &   $p));

But it's a pity you have to write & TWICE. One in the signature, one in the call_user_func call. 

...it looks so much like the depreciated call-time pass-by-reference.

Don't you think?
 [2011-08-04 02:11 UTC] lucas at threeamdesign dot com
to work around this problem put the reference in the argument array. e.g.:

function foo(&$bar) {
	$bar = 5;
}
$a = 1;
$b = 2;
call_user_func('foo', $a);
call_user_func_array('foo', array(&$b));
var_dump($a, $b);

gives

int(1)
int(5)
 [2012-03-20 08:21 UTC] georgir at gmail dot com
The "workaround" from the last comment is not a good idea, because it can not be applied generally. If you use call_user_func_array('foo', array(&$b)) when the function foo takes an argument by copy instead of by reference and modifies it thinking it doesn't matter, $b will still be modified, which is most certainly undesired.

Also, the issue is more serious than described in the original post. It affects call_user_func_array as well as call_user_func, and it is not just a warning that is given, but the whole function does not get executed. This is actually a big deal, it can break a lot of programs. A setting to allow execution of the function in this case would be welcome.
 [2015-03-21 20:23 UTC] nikic@php.net
-Status: Open +Status: Wont fix
 [2015-03-21 20:23 UTC] nikic@php.net
Closing as Won't Fix, because
a) since PHP 5.4 you can just use $callback($arg1, $arg2, ...) instead (which supports references)
b) you can use call_user_func_array() if you really want to stick with it.
 [2015-03-22 17:15 UTC] georgir at gmail dot com
wow, that wontfix...

as said above, call_user_func_array is affected by the same bug.
and even for call_user_func, $func() isn't always an option as it is not usable with method calls, etc.
 [2015-03-22 22:30 UTC] cmbecker69 at gmx dot de
> $func() isn't always an option as it is not usable with method
> calls, etc.

To my knowledge, variable functions work for all callables (i.e.
is_callable($func) !== true) since PHP 5.4. Older versions have
already reached their EOL.
 [2015-03-23 02:52 UTC] georgir at gmail dot com
cmbecker69, you are right, so we have an ok workaround for call_user_func.
it is still strange why two things that should be equivalent can not be implemented equivalently, however.
and it still leaves the more used call_user_func_array bugged with no workaround.
 [2015-03-23 07:51 UTC] nikic@php.net
@georgir: What's the issue you're seeing with call_user_func_args? Your statement

> If you use call_user_func_array('foo', array(&$b))
> when the function foo takes an argument by copy
> instead of by reference and modifies it thinking
> it doesn't matter, $b will still be modified,
> which is most certainly undesired.

is not true for any supported versions of PHP, as you can easily verify: http://3v4l.org/dWMfe
 [2015-03-23 10:05 UTC] georgir at gmail dot com
> What's the issue you're seeing with call_user_func_array?
The issue is that a) the check for a reference does not catch the case when a literal value is provided (seems fixed in php7); b) the function is not executed at all, instead of the more graceful fallback of executing it without changing the value that was not a reference (while still issuing the warning).
http://3v4l.org/WRkIH

Thank you for pointing out the change in 5.4, nikic, I was not aware of it. With it, a decent workaround is possible:
http://3v4l.org/rJ2TZ

So this means that we have good workarounds for all cases now. And those that want can even "fix" PHP with some runkit magic in a prepend script to replace those functions with the workarounds.

Now the only thing left that I do not understand is why they would not be fixed in the PHP core directly. But it is of less concern to me now that I'm aware of the workarounds.

Thank you for your time.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Apr 20 03:01:28 2024 UTC