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
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
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
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 anE_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 forinterface_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
byclass_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 anE_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
Marcus Boerger wrote:
I'd like to avoid my little fatal error trick if the user simply called
class_exists('Classname', true) and same forinterface_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
byclass_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
Greg,
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
Greg,
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()
;
;-)
if ($bt[1]["function"] == "class_exists") {
echo "in get_class";
}
}should work quite well without obscure engine hacks - or am I missing
something?johannes