Re: Why change require_once? A brief explanation of motives

From: Date: Tue, 17 Jul 2007 11:52:05 +0000
Subject: Re: Why change require_once? A brief explanation of motives
References: 1 2 3 4 5  Groups: php.pear.dev 
Request: Send a blank email to pear-dev+get-47550@lists.php.net to get a copy of this message
On 7/17/07, Lukas Kahwe Smith <mls@pooteeweet.org> wrote:
Alexey Borzov wrote: Lukas Kahwe Smith wrote:
What are you arguing against? Is having to define that __autoload() function really worth not providing this flexibility?
Failure to __autoload() a class always results in a fatal error, exceptions thrown in __autoload() cannot be caught. You didn't provide sample code Philippe asked you about [1] and I suppose that's because you simply can't. ====== Bar.php ======= class Bar { public static function factory($driver) {
     $class_name = 'Bar_'.$driver;
     if (__autoload($class_name)) {
       return new $class_name();
     }
     throw new Exception('unable to load');
Better would be: if (!class_exists($class_name)) {
       throw new Exception('Unable to load');
} return new $class_name; as this would not depend on the function __autoload() being defined, but would utilize autoloading if available. It's basically the same as the recommended practice in the RFC at this point, but within a userland factory.
} } ====== Bar/Meat.php ======= class Bar_Meat { } ====== foo.php ======= function __autoload($class_name) { // if the user has some kind of preference about how to confirm that the file // exists, or how syntax errors should be handled when __autoload() is called // directly, then he could implement those here return include str_replace('_', '/', $class_name).'.php'; } $bar = Bar::factory('Meat'); var_dump(get_class($bar)); $bar = Bar::factory('Meaty'); var_dump(get_class($bar)); ========== This gives me the output: Bar_Meat + 2 Warnings and an Exception
The original complaint was that __autoload and spl_autoload do not allow you to throw an exception if the class file was not found, and instead raises an E_FATAL. The example you show here pushes the checks into the library code, which is fine for factories, but a PITA when it comes to class files: do I really want a conditional for each and every class my class depends on? whan impact will this have on performance (class_exists() tests are slower than a single require_once as instead of being a language construct, you now have a conditional, a function call *and* the include statement).
Now the "ugly" part in all of this, is something we have been debating on this list before without a proper agreement. The first one is the fact that in order to prevent those warnings, you would have to suppress the include with an @ or you would need to attempt to find the file in the file system first, which is harder than one would hope for when relying on the include_path, since internals does not think we need an include_path search parameter in file_exists() like is available in fopen().
In Zend Framework, we actually created an isReadable() implementation that searches the include path for this very reason -- something that feels like a needless hack.
The work arounds to this are either not truely reliable or are inefficient and of course this is all open to race conditions. The other issue are potential syntax errors in the file to be included. The good news is that the user is now in control over how this should be dealt with inside the __autoload(). The bad news is that the user would need to implement all of this special handling for factory loaders in the generic implementation that gets call in all other cases as well, and there is no way to pass in any parameter to __autoload() to differentiate explicit loading attempts compared to implicit ones caused by making an instance of a not yet defined class. In the end I again prefer the added flexibility, since instead of all the back and forth in the past, where the PEAR developers were essentially the ones deciding what hack to do, its now in the application developers hands (though we can still provide him with reliable implementations for this choice).
I guess I'm left scratching my head at why require_once + autoload is such a big deal, then; it keeps the flexibility of allowing you to explicitly load a class file, and having that class file explicitly load its dependencies (require_once), as well as using implicit loading when desired (autoload). Sure, having the require_once calls in the class files when autoloading is present may seem redundant, but I still haven't seen conclusive test results that show any significant performance benefit to removing those calls (other than perhaps on FreeBSD -- but Greg doesn't indicate whether or not those tests were done before or after the realpath cache was added in the 5.2 series). -- Matthew Weier O'Phinney mweierophinney@gmail.com http://weierophinney.net/matthew/

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