Re: deep investigation of PHP_Callback

From: Date: Sun, 06 May 2007 00:22:48 +0000
Subject: Re: deep investigation of PHP_Callback
References: 1  Groups: php.pear.dev 
Request: Send a blank email to pear-dev+get-46621@lists.php.net to get a copy of this message
Howdy all, If you'll indulge me, I'm going to respond to this out of order. Gregory Beaver wrote:
In short, I see no advantages to using PHP_Callback over native PHP. Is there something I missed Travis?
Greg, excellent due diligence on providing some benchmarks on how PHP_Callback works against straight PHP. I think, however, you've missed the point of this code entirely. It's there to provide a OO wrapper - for those choosing to use an OO methodology in PHP - to a core "type" for which there is no way other than peppering the code with if statements to determine what is happening. This object allows for DRY programming by encapsulating that checking for you. It's not meant to be as fast or faster than core PHP code, as no wrapper is. The shear act of wrapping anything in an object or function automatically slows PHP code down. Changing this: echo strtolower('Hello World'); to this: function msg($string) {
       return strtolower($string);
} echo msg('Hello World'); increases the execution time by approximately 20%
proc.php:Requests per second:    697.25 [#/sec] (mean)
func.php:Requests per second:    574.52 [#/sec] (mean)
If speed is the ultimate goal, PEAR should be a series of procedural includes that run various functions. Given that I don't believe speed is the ultimate goal, I don't see this as an issue. Speed is sacrificed for readability, encapsulation, and in the case of object oriented code polymorphism. As an interesting foot note to these tests, I put together the following object to see how it fared compared to the two methods above with the certain expectation that it would run more slowly than the function: class MyString {
       private $_var = '';
       function __construct($string) {
           $this->_var = strtolower($string);
       }
       function output() {
           return $this->_var;
       }
} $obj = new MyString('Hello World'); echo $obj->output(); The result kind of surprised me:
proc.php:Requests per second:    697.25 [#/sec] (mean)
func.php:Requests per second:    574.52 [#/sec] (mean)
class.php:Requests per second:    614.61 [#/sec] (mean)
Anyhow, back on topic ...
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. ... snip ... 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.
I disagree with the way these benchmarks were done. Of course, depending on what you're after you can make any benchmark look better or worst than another, so this is 100% subjective. That said... PHP_Callback allows you to adhere to the DRY principle. By passing this object around, you know you have a valid callback without having to do is_callable() on the variable. With that in mind, assuming you use a single callback one times within the code, you will have one instantiation of PHP_Callback with 100 calls to execute(), while the straight procedural code will have one setup of the variable with 100 if() statements and 100 call_user_func_array() calls. Using this assumption as the base, I put together a quick script to generate PHP files to benchmark against (script is available at http://plumb.domain51.com/pear/gen.phps). Using the following ab command I got these results: ab -c 100 -t 60 http://localhost/path/to/[proc|pear].php > proc|pear.ab
proc.php:Requests per second:    165.57 [#/sec] (mean)
pear.php:Requests per second:    111.89 [#/sec] (mean)
Using this as the code to benchmark against, PHP_Callback (pear) code is roughly 33% slower, not 320% - 350%. It is still a sizable amount and one to be taken into account when optimizing code, but given the other areas where code is almost certainly not optimized within any given project, this would probably be one of the last areas you were start trying to squeeze the milliseconds out of the code. It should also be noted that proc.php is 3 times the size of pear.php (303 vs 104 lines) as it has to perform an if() at each execution.
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: ... snip ... 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.
In the case of complex validation, it's quite simple to Decorate PHP_Callback and make your new MyGreatCallbackValidator perform the additional functionality for you. This can be done by containing a PHP_Callback object (my preference) or by extending it.
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.
If you're type hinting and the wrong variable type is passed in, wouldn't you want PHP to throw a fatal error? Something unexpected - and generally unrecoverable - has just happened; something that according to the design that's been laid out should have never happened. One of the points of type hinting is to say "this method will only ever take this type". In my opinion, if you condition there's a much larger issue at play here than whether a fatal error is thrown or not.
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 for your apology, Greg. It's completely unnecessary, but appreciated. As noted by one of those who left comments, this is an extremely simple package and one that nearly any PHP coder could put together in very short order. My biggest concern is what would have happened had this package been proposed by someone new to the PEAR community and/or PHP in general? Anyone coming from an OO background would have seen this as an obviously useful package and just considered it an oversight that it wasn't already available. That person, who might have even learned a thing or two about PHP via code available from PEAR, would have been excited about the prospect of being able to contribute something back to the community. To have their contribution greeted by having it declared "useless" would have been dispiriting to say the least, and at worst might discouraged them from future contributions. As it stands, I have a few other ideas up my sleeves that as time permits will make their way into PEPr... though Alexey took the PHP_String from me. ;-) -Travis

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