5

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']}";
}
Community
  • 1
  • 1
Lando
  • 715
  • 6
  • 29

2 Answers2

4

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
}
sroes
  • 14,663
  • 1
  • 53
  • 72
  • This would get called for every error? What if i wanted to use is only on certain functions? – Lando Nov 13 '12 at 19:58
  • The catch will only be called on the first error that occurred. You can read up on the subject at http://php.net/manual/en/language.exceptions.php – sroes Nov 13 '12 at 20:30
  • What I meant was how could I target this to a specific function, later I found `restore_error_handler()` [here](http://stackoverflow.com/a/1241751/1132838) :). – Lando Nov 20 '12 at 15:04
1

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

Ziumin
  • 4,800
  • 1
  • 27
  • 34
  • I am not currently using any framework as this is a simple script. It does give me some ideas on how to solve the problem, thanks for the link. – Lando Nov 13 '12 at 19:33
  • You can use it as standalone component without using a framework – Ziumin Nov 13 '12 at 21:02