This is somewhat related to #611.
If I try to write a file on S3 using Flysystem, and a connection problem occurs, I get an S3 exception:
Aws\S3\Exception\S3Exception: Error executing "PutObject" on ...
For Dropbox, I get a (subclass of) Dropbox\Exception
.
First of all, these adapters do not respect the AdapterInterface
contract by allowing an exception to be thrown, instead of returning false on failure.
Secondly, I would suggest throwing an exception in all cases when something goes wrong on the underlying storage. And to keep code portable (i.e. allowing to change the storage adapter at the flip of a switch), this exception should belong to the Flysystem package.
At the moment, I have to do:
try {
$filesystem->write($path, $contents);
} catch (S3Exception $e) {
// ...
}
If I decide to switch to Dropbox, I have to search through my entire codebase for Flysystem calls, and update them to something like:
try {
$filesystem->write($path, $contents);
} catch (Dropbox\Exception $e) {
// ...
}
And if I'm writing an app that can accept any adapter, using information from a config file, I have to take all possible errors into account:
try {
$filesystem->write($path, $contents);
} catch (S3Exception $e) {
// ...
} catch (Dropbox\Exception $e) {
// ...
}
And so on, without forgetting any adapter. As you can see, this is not a very good practice.
Introducing a common interface for these errors would allow us to call:
try {
$filesystem->write($path, $contents);
} catch (AdapterException $e) {
// ...
}
This way, there is no need to rewrite any code when switching to another adapter.
As I understand the project in its current status, you decided to return false
in some methods, when something goes wrong. The fact is, the few adapters I had a chance to look at do not respect this contract properly (S3 and Dropbox thrown their own exceptions, Ftp and Local raise PHP errors).
There are two solutions to this problem:
- Fix all the adapters to catch any exception or error (including PHP warnings for adapters relying on native functions) and return
false
where appropriate
- Change the contract to never return
false
on methods such as write()
, and throw an AdapterException
on failure instead
I would strongly suggest the second option, because it breaks the application flow when something goes wrong; otherwise an error could go unnoticed if the developer forgets to check the return value using something like:
if (! $fs->write($path, $contents)) {
...
}
Plus, although returning false
gives an opportunity to catch failures on operations such as write()
, there is no such opportunity on methods that return boolean values, such as has()
.
Having all of them throw an exception in case of a failure harmonizes the whole thing.
Happily waiting for your feedback on this one. I could help rewrite the code if you agree with the idea!