Working with Files: Checking for Errors in PHP

So far, the examples in this chapter have been shown without any error checking in them. This keeps them shorter, so you can focus on the file manipulation functions such as file_get_contents(), fopen(), and fgetcsv(). It also makes them some­what incomplete. Just like talking to a database program, working with files means interacting with resources external to your program. This means you have to worry about all sorts of things that can cause problems, such as operating system file per­missions or a disk running out of free space.

In practice, to write robust file-handling code, you should check the return value of each file-related function. They each generate a warning message and return false if there is a problem. If the configuration directive track_errors is on, the text of the error message is available in the global variable $php_errormsg.

Example 9-17 shows how to check whether fopen() or fclose() encounters an error.

Example 9-17. Checking for an error from fopen() or fclose()

try {

$db = new PDO(‘sqlite:/tmp/restaurant.db’);

} catch (Exception $e) {

print “Couldn’t connect to database: ” . $e->getMessage();

exit();

}

// Open dishes.txt for writing

$fh = fopen(‘/usr/local/dishes.txt’,’wb’);

if (! $fh) {

print “Error opening dishes.txt: $php_errormsg”;

} else {

$q = $db->query(“SELECT dish_name, price FROM dishes”);

while($row = $q->fetch()) {

// Write each line (with a newline on the end) to

// dishes.txt

fwrite($fh, “The price of $row[0] is $row[1] \n”);

}

if (! fclose($fh)) {

print “Error closing dishes.txt: $php_errormsg”;

}

}

If your program doesn’t have permission to write into the /usr/local directory, then fopen() returns false, and Example 9-17 prints:

Error opening dishes.txt: failed to open stream: Permission denied

It also generates a warning message that looks like this:

Warning: fopen(/usr/local/dishes.txt): failed to open stream: Permission denied in dishes.php on line 5

“Controlling Where Errors Appear” on page 249 talks about how to control where the warning message is shown.

The same thing happens with fclose(). If it returns false, then the Error closing dishes.txt message is printed. Sometimes operating systems buffer data written with fwrite() and don’t actually save the data to the file until you call fclose(). If there’s no space on the disk for the data you’re writing, the error might show up when you call fclose(), not when you call fwrite().

Checking for errors from the other file-handling functions (such as fgets(), fwrite(), fgetcsv(), file_get_contents(), and file_put_contents()) is a little trickier. This is because you have to do something special to distinguish the value they return when an error happens from the data they return when everything goes OK.

If something goes wrong with fgets(), file_get_contents(), or fgetcsv(), they each return false. However, it’s possible that these functions could succeed and still return a value that evaluates to false in a comparison. If file_get_contents() reads a file that just consists of the one character 0, then it returns a one-character string, 0. Remember from “Understanding true and false” on page 40, though, that such a string is considered false.

To get around this, be sure to use the identity operator to check the function’s return value. That way, you can compare the value with false and know that an error has happened only if the function actually returns false, not a string that evaluates to false.

Example 9-18 shows how to use the identity operator to check for an error from file_get_contents().

Example 9-18. Checking for an error from file_get_contents()

$page = file_get_contentsCpage-template.html’);

// Note the three equals signs in the test expression

if ($page === false) {

print “Couldn’t load template: $php_errormsg”;

} else {

// … process template here

}

Use the same technique with fgets() or fgetcsv(). Example 9-19 correctly checks for errors from fopen(), fgets(), and fclose().

Example 9-19. Checking for an error from fopen(), fgets(), or fclose()

$fh = fopen(‘people.txt’,’rb’); if (! $fh) {

print “Error opening people.txt: $php_errormsg”;

} else {

while (! feof($fh)) {

$line = fgets($fh);

if ($line !== false) {

$line = trim($line);

$info = explode(‘|’, $line);

print ‘<li><a href=”mailto:’ . $info[0] . ‘”>’ . $info[1] .”</li>\n”;

}

}

if (! fclose($fh)) {

print “Error closing people.txt: $php_errormsg”;

}

}

When fwrite(), fputcsv(), and file_put_contents() succeed, they return the number of bytes they’ve written. When fwrite() or fputcsv() fails, it returns false, so you can use the identity operator with it just like with fgets(). The file_put_contents() function is a little different. Depending on what goes wrong, it either returns false or -1, so you need to check for both possibilities. Example 9-20 shows how to check for errors from file_put_contents().

// Load the file from Example 9-1

$page = file_get_contentsCpage-template.html’);

// Insert the title of the page

$page = str_replace(‘{page_title}’, ‘Welcome’, $page);

// Make the page blue in the afternoon and

// green in the morning if (date(‘H’ >= 12))

{

$page = str_replace(‘{color}’, ‘blue’, $page);

} else {

$page = str_replace(‘{color}’, ‘green’, $page);

}

// Take the username from a previously saved session

// variable

$page = str_replace(‘{name}’, $_SESSION[‘username’], $page);

$result = file_put_contents(‘page.html’, $page);

// Need to check if file_put_contents() returns false or -1

if (($result === false) || ($result == -1)) {

print “Couldn’t save HTML to page.html”;

}

Source: Sklar David (2016), Learning PHP: A Gentle Introduction to the Web’s Most Popular Language, O’Reilly Media; 1st edition.

Leave a Reply

Your email address will not be published. Required fields are marked *