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.