The short answers to your questions:
A1: Is A flimsy and a bit unprofessional?
No. It's not at all "flimsy" and not unprofessional. The difference between an
include and a
require is just that: If you
require something, your code needs that contents to be there, or it can't work. If
require doesn't work, that represents a fatal error, which is just what
A2: Is A acceptable just allowing a fatal error?
For reasons explained above: yes, yes it is. Of course fatal errors due to missing
require files are not acceptable in production, but for code to make it to the production stage, the requires should be absolutely bug-free.
A3: Is B over the top, too much code FWIW, and not really necessary?
Yes and No. B is over the top, it is too much code, but at the same time it doesn't check enough. your
is_readable will check for a readable file in the same directory as the script that calls
require on the other hand will look for files in the current directory and all of the paths in the
include_path. Check out which directories this includes by using
Not that the include path isn't a constant: you can add directories to it at runtime:
So to check if the
require calls will fail, you'd have to check all of the directories in the include path, for readable files, and only then will an
include or a
require fail. So your code is more verbose, but doesn't check enough.
A4: Is B much better because it removes a stinky fatal error, and because it controls how the application degrades rather than relying on a PHP fatal error to halt?
Absolutely NOT: a "stinky" fatal error or a
exit call... I don't really see much difference (other than the one you seem to consider cleaner generating a less clear error message). It doesn't really control how the application degrades at all: it merely halts it. There's no predefined sequence by which the runtime is unloaded, connections are being closed and resources are freed. Internally speaking, the differences between your approach and the fatal error are nil.
In addition to all of this, I'd like to rant a bit about some of your statements:
"Such as B has too much overhead (likely negligible but just an example)..."
Well overhead is definitely one reason not to use B. As I've explained: to check if the file is readable or not, you'll have to iterate over all directories that are in the
include_path. Each time calling
is_readable for each file that you're trying to include. Given that most frameworks const of hundreds or even thousands of files, the overhead this causes is in no way negligible. It'll be quite substantial actually. The one thing that you'll want to avoid if you have to maintain a high-traffic application is pointless disk IO, because Disk IO has a direct impact on your performance. And in this case, it can be avoided easily by just spending a bit more time debugging, and making sure the
requires all work.
"B seems more frameworky and robust, controlling what happens via the application rather than just relying upon PHP which falls back on a fatal error"
Now it is true that frameworks handle nearly all errors/exceptions that internal components throw at some point or another. An uncaught exception is generally handled by an exception handler that is registered by the framework itself, which then churns out a nice render of this exception, complete with stack-trace and all that. So I can understand why you think that, instead of letting a fatal error propagate, preventing it and displaying a more "tidy" error message is what frameworks could/should do. But there are 2 things you're not taking into account:
- Frameworks consist of hundreds or thousands of files. Classes that are autoloaded are, at some point,
required. If a framework were to loop over the include paths, using
is_readable over and over again, even for components of the framework itself, that would become a significant bottleneck. Especially considering the fact that stable frameworks rarely (I haven't seen it happen) encounter missing includes/requires. A framework worth using contains no broken
requires, and that's an end of it. That's why they'll never check the files they require.
- When frameworks handle things like uncaught exceptions, they often do a couple of things more than just presenting a human-readable error message. They allow for user code to hook into that event, to log any problems that are encountered. They could use the exception to prevent a fatal crash (DB connection goes away, reconnect and retry). But that's more of an "in-theory" use-case. What you do is actually handling a real FATAL, possibly not even code-related error. That's not what frameworks do. They don't catch syntax errors, they don't handle missing files or wrong permission settings. And they certainly don't brute-force attack the DB when the connection params (username, password) are incorrect.
So if, after all this, you still feel that apprach B is more frameworky, then why not go one step further, and also check syntax errors prior to the
require? No? Because it would be silly? Yes, it would be. So just
include the files: a fatal error is an error that needs to be fixed ASAP. The best way to communicate the urgency to devs is by displaying them the raw fatal error. It stresses the fact that there's a serious issue that needs to be fixed. Don't pamper them too much, because you'll end up with code that just looks silly:
* This is just an example of how far you can take things
* If you ever see someone using code like this, shout at that person
* NEVER USE THIS AWFUL FUNCTION
$paths = explode(PATH_SEPARATOR, get_include_path());
$exists = false;
foreach ($paths as $path)
$exists = true;
$exists = realpath($path.'/'.$file);
if ($exists === false)
exit($file.' Could not be found, please check the name or create the file');
if ($exists === true)
exit($file.' exists, but is not readable. Check permissions, file corruption or...');
if (!php_check_syntax(realpath($path.'/'.$file, $msg))
exit($file.' exists, and is readable, but contains syntax errors: '.$msg);
Not only is it more verbose, it's also slower, and makes debugging just an awful lot harder than a good 'ol: