php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #55416 array_map() throws PHP warning if the callback throws an exception
Submitted: 2011-08-13 19:58 UTC Modified: 2015-01-06 00:29 UTC
Votes:10
Avg. Score:4.3 ± 0.6
Reproduced:9 of 9 (100.0%)
Same Version:0 (0.0%)
Same OS:4 (44.4%)
From: roan dot kattouw at gmail dot com Assigned: levim (profile)
Status: Closed Package: Arrays related
PHP Version: 5.4.0alpha3 OS: Ubuntu Natty
Private report: No CVE-ID: None
 [2011-08-13 19:58 UTC] roan dot kattouw at gmail dot com
Description:
------------
If you map a function on an array using array_map(), and that function (the callback) then throws an exception, you get a PHP Warning saying "An error occurred while invoking the map callback". I guess this is sort of reasonable if an actual error occurred (even though that error would presumably have reported itself already, so it's superfluous), but an exception isn't an actual error unless the code that catches the exception decides it is.

This is annoying me because I'm squashing PHP warnings and notices in my code, but I can't get rid of this warning. I have a legitimate use case for throwing an exception from my map function, so I can either rewrite my code to not use an exception there (which would be extremely awkward) or write a foreach equivalent of the array_map() call (which would just be ridiculous; PHP provides array_map() for a reason: so you can write one-liners instead of repetitive foreach loops), or remove the IMHO misplaced warning in PHP.

I'll dig into the PHP source and see if I can come up with a patch.

Test script:
---------------
error_reporting(E_ALL);

function barf($i) {
	$foo = $bar['baz'];
	throw new Exception('barf');
}

$a = array(1, 2, 3);

try {
	array_map('barf', $a);
} catch(Exception $e) {
	echo $e;
}


Expected result:
----------------
Notice: Undefined variable: bar in /home/catrope/php-5.4.0alpha3/testcase.php on line 3

exception 'Exception' with message 'barf' in /home/catrope/php-5.4.0alpha3/testcase.php:3
Stack trace:
#0 [internal function]: barf(1)
#1 /home/catrope/php-5.4.0alpha3/testcase.php(6): array_map('barf', Array)


Actual result:
--------------
Notice: Undefined variable: bar in /home/catrope/php-5.4.0alpha3/testcase.php on line 3

Warning: array_map(): An error occurred while invoking the map callback in /home/catrope/php-5.4.0alpha3/testcase.php on line 6
exception 'Exception' with message 'barf' in /home/catrope/php-5.4.0alpha3/testcase.php:3
Stack trace:
#0 [internal function]: barf(1)
#1 /home/catrope/php-5.4.0alpha3/testcase.php(6): array_map('barf', Array)


Patches

skip-array-map-warning-on-exception.patch (last revision 2011-08-13 20:27 UTC by roan dot kattouw at gmail dot com)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-08-13 20:32 UTC] roan dot kattouw at gmail dot com
The attached patch fixes this by not throwing the warning if EG(exception) is not NULL. IMO the warning should be removed completely (because it's superfluous, and I don't see offhand how it can be triggered other than by an exception, but I don't know PHP core at all), but it's easy to tweak my patch into doing that instead; this patch is just the minimal solution.
 [2011-08-13 20:40 UTC] roan dot kattouw at gmail dot com
Something I forgot to mention in the original report:

Using @array_map(...) suppresses the warning. However, it also suppresses any other notices or warnings that might happen in the callback. That's why I put the $foo = $bar['baz']; bit in barf(): if you use @array_map, the "Undefined variable" notice, which is legitimate, goes away as well.

More generally, @ is kind of a blunt instrument that often suppresses more than you expect it to, masking mistakes.
 [2011-08-15 08:54 UTC] laruence@php.net
I was wondering what kind of situation needs throw a exception in map function?

and why doing that? thanks
 [2011-08-15 09:35 UTC] roan dot kattouw at gmail dot com
Basically what I'm doing is

implode("\n", array_map('readStyleFile', $files)

where $files is an array of file names, and readStyleFile() can throw an exception if something is wrong (in this case it's if the file doesn't exist, but other cases are imaginable). This exception then propagates up a few levels and is caught and handled. Throwing an exception from inside the map callback works just fine: it stops the callback, stops array_map(), propagates up to the caller of array_map(), then works its way up the call stack like any other exception. The only issue is that warning that won't go away.
 [2011-08-16 16:20 UTC] mah at everybody dot org
http://www.mediawiki.org/wiki/Special:Code/MediaWiki/94433 has Roan's actual use case.
 [2012-02-12 16:15 UTC] harvey dot robin at gmail dot com
I've just come across this too and I'm really surprised at this inconsistent behavior, for me the question isn't "what kind of situation needs throw a exception in map function" but "why would the map function behave differently to any other situation".  If you change array_map to array_walk in the original example then the exception propagates upward, as expected.

At the very least, I'd expect to see a note on the PHP manual page warning users that exceptions work differently for the callback parameter to array_walk.
 [2014-10-30 20:50 UTC] tomasz at kowalczyk dot cc
Unfortunately I have the same problem on PHP 5.5.16 from Ondrej's PPA (Ubuntu 14.04). This is an inconsistent behavior as other functions like array_walk() or array_reduce() don't do that.
 [2015-01-05 21:03 UTC] levim@php.net
-Status: Open +Status: Analyzed -Assigned To: +Assigned To: levim
 [2015-01-06 00:29 UTC] levim@php.net
Note that this same issue happens for array_reduce and array_filter, but not array_walk.
 [2015-01-06 00:31 UTC] levim@php.net
Automatic comment on behalf of levim
Revision: http://git.php.net/?p=php-src.git;a=commit;h=bb7ceb046eb5f0d5fa25066a3d7f9b4eb8cc3c93
Log: Fix bug #55416
 [2015-01-06 00:31 UTC] levim@php.net
-Status: Analyzed +Status: Closed
 [2015-01-06 16:13 UTC] danack@php.net
The same odd behaviour is in SQLite3 where the code below gives the output:

PHP Warning:  SQLite3::querySingle(): Unable to execute statement:  in /test/src/exceptionTest1.php on line 11
Exception: test exception


<?php
function my_udf_md5($string) {
    throw new \Exception("test exception\n");
}

$db = new SQLite3('mysqlitedb.db');
$db->createFunction('my_udf_md5', 'my_udf_md5');

try {
    $result = $db->querySingle('SELECT my_udf_md5("test")');
    var_dump($result);
}
catch(\Exception $e) {
    echo "Exception: ".$e->getMessage();
}
 [2016-07-20 11:40 UTC] davey@php.net
Automatic comment on behalf of levim
Revision: http://git.php.net/?p=php-src.git;a=commit;h=bb7ceb046eb5f0d5fa25066a3d7f9b4eb8cc3c93
Log: Fix bug #55416
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Jun 13 07:01:34 2024 UTC