Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way of handling errors and warnings on filesystem functions in PHP?

When using filesystem functions, what would be the correct way of handling errors such as:

Warning: symlink(): No such file or directory in /path-to-script/symlink.php on line XXX

My usual approach is to check for any condition that could produce an error before calling the filesystem function. But if the command fails for a reason I haven't foreseen, how can I catch the error to show the user a more helpful message?

This is a simplification of the code that creates the symbolic link:

$filename = 'some-file.ext';
$source_path = '/full/path/to/source/dir/';
$dest_path = '/full/path/to/destination/dir/';

if(file_exists($source_path . $filename)) {
    if(is_dir($dest_path)) {
        if( ! file_exists($dest_path . $filename)) {
            if (symlink($source_path . $filename, $dest_path . $filename)) {
                echo 'Success';
            } else {
                echo 'Error';
            }
        }
        else {
            if (is_link($dest_path . $filename)) {
                $current_source_path = readlink($dest_path . $filename);
                if ( $current_source_path == $source_path . $filename) {
                    echo 'Link exists';
                } else {
                    echo "Link exists but points to: {$current_source_path}";
                }
            } else {
                echo "{$source_path}{$filename} exists but it is not a link";
            }
        }
    } else {
        echo "{$source_path} is not a dir or doesn't exist";
    }
} else {
    echo "{$source_path}{$filename} doesn't exist";
}  

Followup / Solutions

As sugested by Sander, using set_error_handler() to turn errors and warnings into exceptions.

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}

set_error_handler("exception_error_handler");

try {
    symlink($source_path . $filename, $dest_path . $filename);
    echo 'Success';
}
catch (ErrorException $ex) {
    echo "There was an error linking {$source_path}{$filename} to {$dest_path}{$filename}: {$ex->getMessage()}";
}

restore_error_handler();

Using the @ operator is another solution (although some suggest avoiding it whenever possible):

if (@symlink($source_path . $filename, $dest_path . $filename)) {
    echo 'Success';
} else {
    $symlink_error = error_get_last();        
    echo "There was an error linking {$source_path}{$filename} to {$dest_path}{$filename}: {$symlink_error['message']}";
}
like image 930
Lando Avatar asked Nov 13 '12 18:11

Lando


2 Answers

I think you want to want to set an errorhandler which throws exceptions:

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    // see http://php.net/manual/en/class.errorexception.php
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}

set_error_handler("exception_error_handler");

Then your code would be:

try {
    symlink(); // when an error occured, it jumps to the catch
} catch (ErrorException $ex) {
    // do stuff with $ex
}
like image 79
sroes Avatar answered Oct 20 '22 15:10

sroes


I suggest using Fileysystem component that is widely used and tested solution for filesystem operations https://github.com/symfony/Filesystem/blob/master/Filesystem.php#L248

like image 33
Ziumin Avatar answered Oct 20 '22 16:10

Ziumin