Migrating to PHP 5.3: call_user_func_array()
I've been updating various old pieces of code after our PHP 5.3 upgrade
yesterday afternoon. Today, call_user_func_array()
has been a
recurring theme. Notably, some older WordPress plugins have been causing
issues, as the WordPress plugin API is heavily dependent on
call_user_func_array()
.
Passing non-arrays
Let's first look what happens when we pass a non-array as the second
argument to call_user_func_array()
:
function foo() {
var_dump( func_get_args() );
return 7;
}
var_dump( call_user_func_array( 'foo', 3 ) );
Here's the output in PHP 5.2.17:
array(1) {
[0]=>
int(3)
}
int(7)
And here's PHP 5.3.5:
PHP Warning: call_user_func_array() expects parameter 2 to be array, integer given in /tmp/call_user_func_array.php on line 9
NULL
In my definition a "warning" wouldn't refuse to run a specific command
(the callback) and then continue with program execution, but c'est la
vie. One fix is to typecast as array
, which turns a scalar into a
single-element array:
call_user_func_array( 'foo', (array)3 ); // array( 0 => 3 )
However, typecasting would turn an object into an array. An object with
three properties would become an array with three elements, so the
callback would get three arguments (the properties) rather than one
argument (the object). The best solution is to check is_array()
:
call_user_func_array( 'foo', is_array($args) ? $args : array($args) ); // $args, or array( 0 => $args )
Passing by reference
Let's try a callback that expects to get parameters by reference:
function foo( &$input ) {
var_dump( $input );
return 'goodbye';
}
$args = array( 'hello' );
var_dump( call_user_func_array( 'foo', $args ) );
The old PHP 5.2.17 behavior:
string(5) "hello"
string(7) "goodbye"
And now, in PHP 5.3.5:
PHP Warning: Parameter 1 to foo() expected to be a reference, value given in /tmp/call_user_func_array.php on line 9
NULL
Same deal as before: things basically fall of the tracks, with the
callback never executing and the function returning null. One fix is to
pass $args
by reference, and remove the &
from the function
definition:
function foo( $input ) {
var_dump( $input );
return 'goodbye';
}
$args = array( 'hello' );
var_dump( call_user_func_array( 'foo', &$args ) );
Or if you don't really require pass by reference (you're not modifying
the input) just remove the &
from the function definition and be done
with it.