DekGenius.com
Previous Section  < Day Day Up >  Next Section

10.6 Checking for Errors

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 somewhat 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 permissions 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 10-19 shows how to check whether fopen( ) or fclose( ) encounters an error.

Example 10-19. Checking for an error from fopen( ) or fclose( )
require 'DB.php';
$db = DB::connect('mysql://hunter:w)mp3s@db.example.com/restaurant');

// 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->fetchRow( )) {
        // 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 10-19 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

Section 12.1 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 (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 each return when an error happens from the data they each 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 Section 3.1 though, that such a string is considered false.

To get around this, you need to use the identical operator: = = = (three equals signs). This compares two values and says they're equal only if they have the same value and are the same type. That way, you can compare the return value of a file function with false and know that an error has happened only if the function returns false, not a string that evaluates to false.

Example 10-20 shows how to use the identical operator to check for an error from file_get_contents( ).

Example 10-20. Checking for an error from file_get_contents( )
$page = file_get_contents('page-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 10-21 correctly checks for errors from fopen( ), fgets( ), and fclose( ).

Example 10-21. Checking for an error from fopen( ), fgets( ), or fclose( )
$fh = fopen('people.txt','rb');
if (! $fh) {
    print "Error opening people.txt: $php_errormsg";
} else {
    for ($line = fgets($fh); ! feof($fh); $line = fgets($fh)) {
        if ($line =  =  = false) {
            print "Error reading line: $php_errormsg";
        } else {
            $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( ) and file_put_contents( ) succeed, they return the number of bytes they've written. When fwrite( ) fails, it returns false, so you can use the identical 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 10-22 shows how to check for errors from file_put_contents( ).

Example 10-22. Checking for an error from file_put_contents( )
$zip = 10040;
$weather_page = file_get_contents('http://www.srh.noaa.gov/zipcity.php?inputstring=' . 
$zip);

if ($weather_page =  =  = false) {
    print "Couldn't get weather for $zip";
} else {
    // Just keep everything after the "Detailed Forecast" image alt text
    $page = strstr($weather_page,'Detailed Forecast');
    // Find where the forecast <table> starts
    $table_start = strpos($page, '<table');
    // Find where the <table> ends
    // Need to add 8 to advance past the </table> tag
    $table_end  = strpos($page, '</table>') + 8;
    // And get the slice of $page that holds the table
    $forecast = substr($page, $table_start, $table_end - $table_start);
    // Print the forecast;
    print $forecast;
    $saved_file = file_put_contents("weather-$zip.txt", $matches[1]);
    // Need to check if file_put_contents( ) returns false or -1
    if (($saved_file =  =  = false) || ($saved_file =  = -1)) {
        print "Couldn't save weather to weather-$zip.txt";
    }
}

    Previous Section  < Day Day Up >  Next Section