Categories
Posts

PHP array_map Callback Expects The Full Namespace

The array_map function in PHP has a quirk ( I know, shocker ) that I’ve managed to forget several times. Hopefully writing it here will cement it in my memory.

Using array_map isn’t hard, here is a very contrived example:

[sourcecode lang=”php”]
namespace EXAMPLE;

function lowercase_string( $name ) {
return strtolower( $name );
}

function lowercase_array( $names ) {
return array_map( ‘lowercase_string’, $names );
}

print_r( lowercase_array( [
‘Bill’,
‘Max’,
‘Julie’,
‘Norm’
] ) );
echo "n";
[/sourcecode]

You feed an array to lowercase_array() and it will return lower cased versions of all the array entries. Do you know what happens when you run this code?

[sourcecode lang=”plain”]
PHP Warning: array_map() expects parameter 1
to be a valid callback, function ‘lowercase_string’
not found or invalid function name in
/home/josephscott/tmp/array-map.php on line 9
[/sourcecode]

( I broke this up into multiple lines to make it easier to read )

How can lowercase_string not be a valid callback? It is even defined right there in the same file. The answer to this question is in the very first line of my example.

When using array_map() inside of a namespace you must provide the full namespace reference to the callback. In this case that means replacing lowercase_string with EXAMPLElowercase_string:

[sourcecode lang=”php”]
namespace EXAMPLE;

function lowercase_string( $name ) {
return strtolower( $name );
}

function lowercase_array( $names ) {
return array_map( ‘EXAMPLElowercase_string’, $names );
}

print_r( lowercase_array( [
‘Bill’,
‘Max’,
‘Julie’,
‘Norm’
] ) );
echo "n";
[/sourcecode]

Running this corrected version gives us what we’d expect:

[sourcecode lang=”plain”]
Array
(
[0] => bill
[1] => max
[2] => julie
[3] => norm
)
[/sourcecode]

I think the reason that I keep forgetting this is because it feels more obvious that array_map() should know it is already in a namespace and try to dereference the callback based on that namespace first, the same way a regular function call would. This feels like a violation of the principle of least astonishment (POLA).

While the array_map() documentation makes no mention of the namespace requirement for the callback, it does appear to be the intended behavior. A PHP bug was filed describing this exact issue. It was closed as “this is not a bug”.

2 replies on “PHP array_map Callback Expects The Full Namespace”

Hey Joseph!
Thanks for writing about this nasty “feature”!
Had similar adventure, but a bit different and I’d like to share my solution.
My callback function was sitting inside another function, something like:

puclic function foo($a)
{

function helper($e) { return $e + $a; }

}

…and I couldn’t figure it out how should I namespace it, because I didn’t want to take out helper from the foo function.
But there is a another way! Good ol’ lambda-style function saved me.
Example 1:
$helper = create_function(‘$e’, ‘return $e+’ . $a . ‘;’);
$errors = array_map($helper, $errors);

However, create function is deprecated since php 7.2 I ended up with cleaner solution:
$helper = function($e) use ($a) { //$a is inherited from parent function
return $e + $a;
};
$errors = array_map($helper, $errors);

Thanks for the notice. It helped a lot. I just wanted to note that it needs a backslash in my case to work:
array_map( ‘EXAMPLE\lowercase_string’, $names );
Maybe it helps someone else.

Leave a Reply

Your email address will not be published. Required fields are marked *