deep investigation of PHP_Callback

From: Date: Sat, 05 May 2007 18:43:21 +0000
Subject: deep investigation of PHP_Callback
Groups: php.pear.dev 
Request: Send a blank email to pear-dev+get-46619@lists.php.net to get a copy of this message
Hi, As your newly elected president, I must step in here. The debate over PHP_Callback is approaching an emotional flame war. Anyone who posts a flame reply after this email will be sent to the principal's office and given detention. I mean it. As for PHP_Callback, I did some work to test the assertions of those involved. I created two scripts. Here is the first script: callable.php <?php $a = array('myclass', 'myfunc'); if (!is_callable($a)) { throw new BadMethodCallException('test'); } ?> and here is the second: phpcallback.php <?php require_once 'PHP/Callback.php'; $p = new PHP_Callback('myclass', 'myfunc'); ?> Then, I ran apache benchmark on them. For comparison, I ran it on a static index.html file that comes with apache2. Here are the results: index.html: Requests per second: 3269.72 [#/sec] (mean) callable.php: Requests per second: 1066.97 [#/sec] (mean) phpcallback.php: Requests per second: 303.20 [#/sec] (mean) Using PHP_Callback was 3.5 times slower than the native PHP, with a difference of 1 line of extra code in the native PHP. This of course, is the simplest and an unlikely case. I estimate that probably most scripts will be instantiating at most 100 PHP_Callback or processing 100 callbacks. I modified the scripts as follows: callable.php <?php $a = array('myclass', 'myfunc'); for ($i = 0; $i < 100; $i++) { try { if (!is_callable($a)) { throw new BadMethodCallException('test'); } } catch (Exception $e) { } } ?> phpcallback.php <?php require_once 'PHP/Callback.php'; for ($i = 0; $i < 100; $i++) { try { $p = new PHP_Callback('myclass', 'myfunc'); } catch (Exception $e) { } } ?> index.html: Requests per second: 3410.66 [#/sec] (mean) callable.php: Requests per second: 524.53 [#/sec] (mean) phpcallback.php: Requests per second: 159.20 [#/sec] (mean) In this case, using PHP_Callback was 3.29 times slower, so with volume, the inefficiency decreases slightly, but is still a marked reduction in speed. One argument for using PHP_Callback is that it allows validation at callback creation. The scripts I used for benchmarking show that this is a difference of 1 line of code, and a reduction in efficiency of 3x. Another argument for using PHP_Callback is that it unifies execution of a callback. Here is the difference: old: <?php call_user_func_array($callback, array('param1', 'param2')); ?> new: <?php $callback->execute('param1', 'param2'); ?> To test this, I used these scripts: callable.php <?php require_once 'PHP/Callback.php'; function myfunc($a, $b) { echo $a.$b; } $p = new PHP_Callback('myfunc'); $a = 'myfunc'; for ($i = 0; $i < 10000; $i++) { call_user_func_array($a, array('hi', ' there')); } ?> phpcallback.php <?php require_once 'PHP/Callback.php'; function myfunc($a, $b) { echo $a.$b; } $p = new PHP_Callback('myfunc'); for ($i = 0; $i < 10000; $i++) { $p->execute('hi', ' there'); } ?> Note that the PHP_Callback creation time is evened out, and so the benchmarking is truly testing the time it takes to execute a callback. Again, the efficiency lost is tremendous. index.html: Requests per second: 3378.96 [#/sec] (mean) callable.php: Requests per second: 30.20 [#/sec] (mean) phpcallback.php: Requests per second: 14.53 [#/sec] (mean) PHP_Callback is twice as slow as native PHP, and the difference between the two calls is a few characters. The third argument is that it can be used as a type hint in a function or method. This results in a difference of this code: function blah(PHP_Callback $callback) { $callback->execute(...) } and function blah($callback) { if (!is_callable($callback)) { throw new BadMethodCallException(...) } call_user_func_array($callback, array(...)); } Here, we require three extra lines of code to validate the callback, and it is 3 times as efficient. In addition, if there is any complex callback validation (i.e. it must be an array with an object that is an instanceof class "Blah"), there is no advantage to using PHP_Callback, and it actually becomes difficult to figure out how to validate the callback. Finally, type-hinting is a fatal error in PHP, and cannot be caught and handled, although it has become a recoverable fatal error in PHP 5.2, this still kicks execution to the global scope. In short, I see no advantages to using PHP_Callback over native PHP. Is there something I missed Travis? I'm sorry you received such a sour reception initially, I hope you can forgive our lack of patience. All of the seasoned PEAR developers are going to learn from this experience and act differently in the future. Thanks, Greg

« previous php.pear.dev (#46619) next »