PHP autoloading: Preventing 'cannot redeclare <class>' in all constellations?

pinkgothic picture pinkgothic · Jul 14, 2010 · Viewed 8.3k times · Source

Question

Is there a way I can make PHP ignore re-declarations of classes rather than barf up a FATAL ERROR? Or at least throw an exception? (I could easily catch it then and proceed (as well a log the attempted autoloading).)

I'm guessing no and a fatal error is a fatal error - after all, in ninety-nine out of a hundred cases, that's reasonably sensible behaviour - and I'll probably just have to fix instances of it being triggered on a case-by-case basis. But maybe someone smarter than me has this figured out.


If you're asking yourself "Why on earth would you want to do that?", read on.

Background

I'm working on a tool that uses Reflection to aggregate specific information about used functions and classes. One of the script's arguments is an optional bootstrap file to make Reflection more reliable with autoloading (less ReflectionExceptions that end up caught and triggering a fallback heuristic, because classes are unknown in a specific file).

Now, the bootstrapping loads the autoloader(s) fine, and the script runs as intended, moves through several hundred files without a complaint, until I hit a snag:

PHP Fatal error: Cannot redeclare class PHPUnit_Framework_Constraint in /usr/share/php/PHPUnit/Framework/Constraint.php on line 62

I have two issues:

One, I have no idea what is triggering this. I've adjusted the bootstrap that is used, but am only alternating between 'Cannot redeclare' and 'Could not open file', depending on the include path used. There is no middle ground, i.e. no point where no error occurs. Still, I'm still investigating. (This issue is not what the question is about, though.)

Two, more importantly, and leading to the subject of this question, I need a way to catch it. I've tried writing a custom error handler, but it doesn't seem to want to work for Fatal errors (somewhat sensibly, one might argue).

I intend to release this tool into the open source world at some point, and this strikes me as a quite unacceptable behaviour. I have a fallback heuristic for classes that don't exist - I'd rather they're declared once too seldom than once too often, but without over-using the heuristic, either, which is to say, I do want to offer the capability of using a bootstrapper. Without breaking the script. Ever. Even if it's the worst autoloader in the history of autoloaders.

(To stress: I don't want help with my autoloaders. That is not what this question is about.)

Answer

Narcis Radu picture Narcis Radu · Jul 14, 2010

One of the best options to avoid Cannot redeclare is the class_exists function. You may use it in the autoloader to prevent class redeclaration. With class_exists you don't have to catch the error, you just prevent it.

There are actually two kinds of fatal errors: catchable and not catchable. Class redeclaration doesn't fire up an E_RECOVERABLE_ERROR (a catchable one) and you can't handle it.

So, the answer to your question is: "You cannot."