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

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to