link to exceptions in autoload thread?

php.internals

Greg Beaver

18 years ago
Hi all, Could someone point me to the message(s) explaining why the executor is unstable when an exception is thrown in __autoload()? Thanks, Greg

Marcus Börger

18 years ago
Hello Gregory, I don't recall any particular thread but Andi once explained why a few things are instable when exceptions are pending. Some of that is no longer the case so maybe it is time to reinvestigate the whole issue of pending exceptions. But maybe that would result in an exception stack like in Java and I am not sure whether I would not simply favor our current bahavior. marcus Tuesday, October 2, 2007, 7:24:23 AM, you wrote:
> Hi all,
> Could someone point me to the message(s) explaining why the executor is > unstable when an exception is thrown in __autoload()?
> Thanks, > Greg
Best regards, Marcus

Greg Beaver

18 years ago
Marcus Boerger wrote:
> Hello Gregory, > > I don't recall any particular thread but Andi once explained why a few > things are instable when exceptions are pending. Some of that is no longer > the case so maybe it is time to reinvestigate the whole issue of pending > exceptions. But maybe that would result in an exception stack like in Java > and I am not sure whether I would not simply favor our current bahavior.
Hi, I actually don't mind the current behavior, although it would be nice if instead of an E_ERROR it simply displayed the exception as if it were uncaught (ignoring the exception handler - just shunt and display as an Exception would). Even this is not a big issue, I've been using die(new Exception(...)) and it does the same thing. What I was wondering is to understand __autoload()s internals a little better. Your message plus my investigation of the source leads me to ask a pre-patch question: I'd like to avoid my little fatal error trick if the user simply called class_exists('Classname', true) and same for interface_exists(). If I provide a simple patch that would introduce a new HashTable of classnames into EG() called EG(in_class_exists), and a new userspace function in_class_exists() that returns true if __autoload() was called by class_exists(), would that be an acceptable addition into PHP 5.3? This way, my autoload handler could simply return if the user is querying the existence of a class, and die pre-emptively with the exception I want in other cases where an E_ERROR would result. This feature would be extremely useful for PEAR2's implementation of autoload. Currently, I check the stack frame of the exception for class_exists() prior to die()ing with it, which is risky at best, and a performance hog. Thanks, Greg

Marcus Börger

18 years ago
Hello Greg, Tuesday, October 2, 2007, 9:25:22 PM, you wrote:
> Marcus Boerger wrote: >> Hello Gregory, >> >> I don't recall any particular thread but Andi once explained why a few >> things are instable when exceptions are pending. Some of that is no longer >> the case so maybe it is time to reinvestigate the whole issue of pending >> exceptions. But maybe that would result in an exception stack like in Java >> and I am not sure whether I would not simply favor our current bahavior.
> Hi,
> I actually don't mind the current behavior, although it would be nice if > instead of an E_ERROR it simply displayed the exception as if it were > uncaught (ignoring the exception handler - just shunt and display as an > Exception would). Even this is not a big issue, I've been using die(new > Exception(...)) and it does the same thing.
> What I was wondering is to understand __autoload()s internals a little > better. Your message plus my investigation of the source leads me to > ask a pre-patch question:
> I'd like to avoid my little fatal error trick if the user simply called > class_exists('Classname', true) and same for interface_exists(). If I > provide a simple patch that would introduce a new HashTable of > classnames into EG() called EG(in_class_exists), and a new userspace > function in_class_exists() that returns true if __autoload() was called > by class_exists(), would that be an acceptable addition into PHP 5.3?
In theory class_exists() should simply return false if __autoload() cannot find it. Why make that more complex? Is it avoiding the time to load and compile the class? If so wouldn't normal code that checks for a class being avaiable later use that class? Check: php -r 'function __autoload($c) { echo "Autoload: $c\n";} var_dump(class_exists("bla");' You can even do class_exists($class_name, false) to avoid the call to to __autoload().
> This way, my autoload handler could simply return if the user is > querying the existence of a class, and die pre-emptively with the > exception I want in other cases where an E_ERROR would result.
> This feature would be extremely useful for PEAR2's implementation of > autoload. Currently, I check the stack frame of the exception for > class_exists() prior to die()ing with it, which is risky at best, and a > performance hog.
> Thanks, > Greg
Best regards, Marcus

LAUPRETRE François (P)

18 years ago
> > This way, my autoload handler could simply return if the user is > > querying the existence of a class, and die pre-emptively with the > > exception I want in other cases where an E_ERROR would result. > > > This feature would be extremely useful for PEAR2's > implementation of > > autoload. Currently, I check the stack frame of the exception for > > class_exists() prior to die()ing with it, which is risky at > best, and > > a performance hog. > > > Thanks, > > Greg
I don't understand why you throw exceptions from an autoload handler. IMO, an autoload handler should just define the class if it can access it, and that's all. I even think that exceptions should be disabled when calling autoload handlers. IMHO, if you die() from your handler, you become incompatible with spl_autoload(), as you cannot know if another handler can resolve the requested symbol. Reagards Francois

Greg Beaver

18 years ago
LAUPRETRE François (P) wrote:
>>> This way, my autoload handler could simply return if the user is >>> querying the existence of a class, and die pre-emptively with the >>> exception I want in other cases where an E_ERROR would result. >>> This feature would be extremely useful for PEAR2's >> implementation of >>> autoload. Currently, I check the stack frame of the exception for >>> class_exists() prior to die()ing with it, which is risky at >> best, and >>> a performance hog. >>> Thanks, >>> Greg > > I don't understand why you throw exceptions from an autoload handler. IMO, > an autoload handler should just define the class if it can access it, and > that's all. I even think that exceptions should be disabled when calling > autoload handlers.
Exceptions thrown cause an E_ERROR. I'm not throwing an exception
> IMHO, if you die() from your handler, you become incompatible with > spl_autoload(), as you cannot know if another handler can resolve the > requested symbol.
Your opinion is wrong. PEAR2's autoload returns false for any classes not prefixed by "PEAR2" Greg

Greg Beaver

18 years ago
Marcus Boerger wrote:
>> I'd like to avoid my little fatal error trick if the user simply called >> class_exists('Classname', true) and same for interface_exists(). If I >> provide a simple patch that would introduce a new HashTable of >> classnames into EG() called EG(in_class_exists), and a new userspace >> function in_class_exists() that returns true if __autoload() was called >> by class_exists(), would that be an acceptable addition into PHP 5.3? > > In theory class_exists() should simply return false if __autoload() cannot > find it. Why make that more complex? Is it avoiding the time to load and > compile the class? If so wouldn't normal code that checks for a class being > avaiable later use that class?
Here is the use case I am dealing with: new PEAR2 user Joe User downloads PEAR2 package Blah, which depends on package Foo, but does not download Foo/something happens and Foo is erased accidentally/whatever. Joe, not knowing anything about PEAR2 package Blah, is just trying it out to see how it works, and so does the drill of: <?php include '/path/to/PEAR2/Autoload.php'; $a = new Blah; $a->doSomething(); $a->doSomethingElse(); ?> This script results in: Fatal Error: class PEAR2::Foo not found in /path/to/PEAR2/Blah/SomeinternalClass.php on Line XX Now what? The error message doesn't tell Joe where to find PEAR2::Foo, or any other useful information on how to find it, just that it can't be found. PEAR2, however, knows exactly where it should be found, and has some idea of why it isn't there (package not installed). In addition, there is no stack trace that can be used to debug the call chain that led to the problem if PEAR2::Foo *is* installed, and Joe reports the problem to the maintainers of PEAR2::Blah. Without my die(new Exception()), there is no way to pass this known information back out of PEAR2_Autoload to Joe, and it makes both development and debugging much more difficult, and unnecessarily so. However, PEAR2 packages can also use class_exists() on drivers/optional components to give a better error message. For instance, if hypothetical package PEAR2::Database is asked to instantiate the Firebird driver, it could do: if (!class_exists('PEAR2::Database::Firebird')) { throw new Exception('Could not locate Firebird database driver, please install PEAR2::Database::Firebird package'); } This error message is infinitely more useful than Fatal Error: class PEAR2::Database::Firebird not found in ... The fact is that there is no way to safely pass error information out of an unsuccessful __autoload() if the script expects the class to simply exist on the next line. Perhaps you have a better idea, but the truth is that I expect the default __autoload for PEAR2 to provide actual useful debugging information. I have already used this model to successfully debug conversion of an existing PEAR package to the PEAR2 model as an exercise, and the stack trace was REALLY helpful for locating what would have been a particularly thorny problem - a class name with the incorrect CasING that could not locate the file on disk. The problem is that PEAR distributes code that is partially a black box - the user is not expected to understand all of the intricate workings, but just to use it to speed up their own development. I use die() inside PEAR2's autoload because it would be a fatal error anyways - if PEAR2's autoload didn't work, the class will not be found by the definition of how PEAR2's directory structure and class naming conventions work. Perhaps you have a better idea of how to pass out a customized error message and stack trace, but as it stands now, __autoload() is extremely debugging-unfriendly. Adding the ability to determine whether class_exists() is the source of the autoload would increase its debugging-friendliness exponentially for my purposes, and would not require major changes to the engine.
> Check: > php -r 'function __autoload($c) { echo "Autoload: $c\n";} var_dump(class_exists("bla");' > > You can even do class_exists($class_name, false) to avoid the call to to > __autoload().
This, as you say, would mean that autoload would not be called. Thus this code: if (!class_exists('PEAR2::Database::Firebird', false)) { throw new Exception('Could not locate Firebird database driver, please install PEAR2::Database::Firebird package'); } $driver = new PEAR2::Database::Firebird(...); would always throw the exception, which defeats the purpose of using class_exists to get a friendlier error message in the first place. Greg

Johannes Schlueter

18 years ago
Greg, On Wed, 2007-10-03 at 15:07 -0500, Greg Beaver wrote:
> Now what? The error message doesn't tell Joe where to find PEAR2::Foo, > or any other useful information on how to find it, just that it can't be > found. PEAR2, however, knows exactly where it should be found, and has > some idea of why it isn't there (package not installed). In addition, > there is no stack trace that can be used to debug the call chain that > led to the problem if PEAR2::Foo *is* installed, and Joe reports the > problem to the maintainers of PEAR2::Blah. > > Without my die(new Exception()), there is no way to pass this known > information back out of PEAR2_Autoload to Joe, and it makes both > development and debugging much more difficult, and unnecessarily so.
function __autoload($a) { $b = debug_backtrace(); if ($bt[1]["function"] == "class_exists") { echo "in get_class"; } } should work quite well without obscure engine hacks - or am I missing something? johannes

Johannes Schlueter

18 years ago
On Wed, 2007-10-03 at 22:23 +0200, Johannes Schlüter wrote:
> Greg, > > On Wed, 2007-10-03 at 15:07 -0500, Greg Beaver wrote: > > Now what? The error message doesn't tell Joe where to find PEAR2::Foo, > > or any other useful information on how to find it, just that it can't be > > found. PEAR2, however, knows exactly where it should be found, and has > > some idea of why it isn't there (package not installed). In addition, > > there is no stack trace that can be used to debug the call chain that > > led to the problem if PEAR2::Foo *is* installed, and Joe reports the > > problem to the maintainers of PEAR2::Blah. > > > > Without my die(new Exception()), there is no way to pass this known > > information back out of PEAR2_Autoload to Joe, and it makes both > > development and debugging much more difficult, and unnecessarily so. > > function __autoload($a) { > $b = debug_backtrace();
This, of course, should have been $bt = debug_backtrace(); ;-)

Greg Beaver

18 years ago
Johannes Schlüter wrote:
> Greg, > > On Wed, 2007-10-03 at 15:07 -0500, Greg Beaver wrote: >> Now what? The error message doesn't tell Joe where to find PEAR2::Foo, >> or any other useful information on how to find it, just that it can't be >> found. PEAR2, however, knows exactly where it should be found, and has >> some idea of why it isn't there (package not installed). In addition, >> there is no stack trace that can be used to debug the call chain that >> led to the problem if PEAR2::Foo *is* installed, and Joe reports the >> problem to the maintainers of PEAR2::Blah. >> >> Without my die(new Exception()), there is no way to pass this known >> information back out of PEAR2_Autoload to Joe, and it makes both >> development and debugging much more difficult, and unnecessarily so. > > function __autoload($a) { > $b = debug_backtrace(); > if ($bt[1]["function"] == "class_exists") { > echo "in get_class"; > } > } > > should work quite well without obscure engine hacks - or am I missing > something?
I do that now, but it is a severe performance hog as well as being fugly. Also, if you use spl_autoload_register, the frame is $bt[2]. In other words, it's possible, but still an ugly, unnecessary hack with lots of potential pitfalls caused by the inability to customize an error message when a class doesn't exist. Greg

Johannes Schlueter

18 years ago
On Wed, 2007-10-03 at 15:27 -0500, Greg Beaver wrote:
> I do that now, but it is a severe performance hog as well as being > fugly. Also, if you use spl_autoload_register, the frame is $bt[2]. In > other words, it's possible, but still an ugly, unnecessary hack with > lots of potential pitfalls caused by the inability to customize an error > message when a class doesn't exist.
Well, the real performance issue there is searching through the file system - and I doubt performance is important for an error handler. If you need performance write proper code without errors which need handling. johannes

Marcus Börger

18 years ago
Hello Greg, I think Johannes has a point here. It is doable in PHP already and you don't hit a performance issue here as you are showing the message only when the class is not present. That is just add Johanne' code after you know that your autoload was responsible but cannot find the class. marcus Wednesday, October 3, 2007, 10:27:45 PM, you wrote:
> Johannes Schlüter wrote: >> Greg, >> >> On Wed, 2007-10-03 at 15:07 -0500, Greg Beaver wrote: >>> Now what? The error message doesn't tell Joe where to find PEAR2::Foo, >>> or any other useful information on how to find it, just that it can't be >>> found. PEAR2, however, knows exactly where it should be found, and has >>> some idea of why it isn't there (package not installed). In addition, >>> there is no stack trace that can be used to debug the call chain that >>> led to the problem if PEAR2::Foo *is* installed, and Joe reports the >>> problem to the maintainers of PEAR2::Blah. >>> >>> Without my die(new Exception()), there is no way to pass this known >>> information back out of PEAR2_Autoload to Joe, and it makes both >>> development and debugging much more difficult, and unnecessarily so. >> >> function __autoload($a) { >> $b = debug_backtrace(); >> if ($bt[1]["function"] == "class_exists") { >> echo "in get_class"; >> } >> } >> >> should work quite well without obscure engine hacks - or am I missing >> something?
> I do that now, but it is a severe performance hog as well as being > fugly. Also, if you use spl_autoload_register, the frame is $bt[2]. In > other words, it's possible, but still an ugly, unnecessary hack with > lots of potential pitfalls caused by the inability to customize an error > message when a class doesn't exist.
> Greg
Best regards, Marcus

Greg Beaver

18 years ago
Marcus Boerger wrote:
> Hello Greg, > > I think Johannes has a point here. It is doable in PHP already and you > don't hit a performance issue here as you are showing the message only > when the class is not present. That is just add Johanne' code after you > know that your autoload was responsible but cannot find the class.
Hi, Just to help with the volume of mail - I know when an idea will not be implemented, and I've got the hacky code in my implementation (and it works even if the smell of the code repulses me :), so this thread can die now for the more important issues. Thanks, Greg