Migrating to PHP 5.3: call_user_func_array()

by
Annika Backstrom
in misc, on 11 March 2011. It is tagged #Programming, #PHP, and #php 5.3.

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.