This blog post is all about how to handle errors from the PHP file_get_contents
function, and others which work like it.
The file_get_contents
function will read the contents of a file into a string
. For example:
|
|
You can try this out on the command-line like so:
|
|
This function is widely used, but I’ve observed that error handling around it is often not quite right. I’ve fixed a few bugs involving incorrect I/O error handling recently, so here are my thoughts on how it should be done.
How file_get_contents fails
For legacy reasons, this function does not throw an exception when something goes wrong. Instead, it will both log a warning, and return false
.
|
|
Which looks like this when you run it:
|
|
Warnings are not very useful on their own, because the code will continue on without the correct data.
Error handling in four steps
If anything goes wrong when you are reading a file, your code should be throwing some type of Exception
which describes the problem. This allows developers to put a try {} catch {}
around it, and avoids nasty surprises where invalid data is used later.
Step 1: Detect that the file was not read
Any call to file_get_contents
should be immediately followed by a check for that false
return value. This is how you know that there is a problem.
|
|
This now gives both a warning and an uncaught exception:
|
|
Step 2: Suppress the warning
Warnings are usually harmless, but there are several good reasons to suppress them:
- It ensures that you are not depending on a global error handler (or the absence of one) for correct behaviour.
- The warning might appear in the middle of the output, depending on
php.ini
. - Warnings can produce a lot of noise in the logs
Use @
to silence any warnings from a function call.
|
|
The output is now only the uncaught Exception
:
|
|
Step 3: Get the reason for the failure
Unfortunately, we lost the “No such file or directory” message, which is pretty important information, which should go in the Exception
. This information is retrieved from the old-style error_get_last
method.
This function might just return empty data, so you should check that everything is set and non-empty before you try to use it.
|
|
This now embeds the failure reason directly in the message.
|
|
Step 4: Add a fallback
The last time I introduced error_clear_last()
/get_last_error()
into a code-base, I learned that HHVM does not have these functions.
|
|
The fix for this is to write some wrapper code, to verify that each function exists.
|
|
This does the same thing as before, but without breaking other PHP runtimes.
|
|
Since HHVM is dropping support for PHP, I expect that this last step will soon become unnecessary.
How not to handle errors
Some applications put a series of checks before each I/O operation, and then simply perform the operation with no error handling. An example of this would be:
|
|
You could probably make a reasonable-sounding argument that pre-emptive checks are a good idea, but I consider them to be misguided:
- If you skip any actual error handling, then your code is going to fail in more surprising ways when you encounter an I/O problem that could not be detected.
- If you do perform correct error handling as well, then the extra checks add nothing other than more branches to test.
Lastly, beware of false positives. For example, the above snippet will reject HTTP URL’s, which are perfectly valid for file_get_contents
.
Conclusion
Most PHP code now uses try
/catch
/finally
blocks to handle problems, but the ecosystem really values backwards compatibility, so existing functions are rarely changed.
The style of error reporting used in these I/O functions is by now a legacy quirk, and should be wrapped to consistently throw a useful Exception
.