On the 13th of January 2022, version 3.0.0 of Flysystem was released. The library ships a couple new methods on the main filesystem and adapter interfaces, making it a breaking change that cause a major version increase.
During the design phase of Flysystem 2, I took a good look at the API to see if there was room for improvement. I spotted a couple cases where there was overlap, even duplication, in the API. The
write functions were essentially the same. Some other methods, such as
has, had inconsistent behaviour.
Checking for file and directory existence, using the
has method, wasn't particularly well abstracted. It sometimes behaved differently depending on the underlying filesystem. Sometimes it responded different depending on what kind of item was checked, a file or a directory. To fix this,
write methods were merged into a single method, and the
has function was removed. The last removal is reversed in version 3, so what happened there?
Directories in Flysystem
To know why the
has call was removed, it helps to understand a part of the larger context of design decisions of Flysystem, in particular those around the handling of directories. The library abstracts away a bunch of differences between different filesystem implementations. One of the larger differences is the need for directories, which some filesystem types can happily function without.
To deal with these differences, some design decisions were taken:
- Directories do not have to be explicitly created
For the ease of use, directories are implicitly created when writing files. If the underlying filesystem needs directories (such as the local filesystem and FTP servers) Flysystem will ensure the directories automatically when writing a file. Concretely, this means you don't have to call
- Directories can be explicitly created
To support "file manager" use-cases, directories can be explicitly created. There is no need for this when wanting to write files. Filesystem such as S3 don't even create directories even when writing to a path like
/some/nested/path.txt. But, if needed, you can force the creation of a directory.
- Directories (virtual or real) are deleted in full
The deletion of files is modelled in a
rm -rffashion. When deleting a directory, all the contents of the directory (at any level) is deleted.
- Directories can be listed on all filesystem types
Even when the underlying filesystem does not support directories, the directories are are listed. For filesystem that don't require directories common prefixes are used and returned as directories, since they represent the same concept.
With these decisions in mind, in particular the implicit directory creation, there wasn't really concrete need to check if a directory exists or not. In my pursuit of simplification, this is where I've gone A LITTLE BIT over board. While I personally do not ever check for directory existence, I can imagine that other people do check for it even when it's not strictly needed.
Software is for humans, not computers
The thing is, while I was technically right (the best kind of right) there is a human aspect to the usability of a library, which I failed to cater for. When you're crafting a solution in your head, you use different ways of solving a problem. For some, the existence check was a way to check "something else". For example, a well-known media manager written for Laravel uses the directory check to see if thumbnails need to be generated or not. Of course, this could be worked around by checking if any of the thumbnails files were existed, but that's not how the thought process went.
When it comes to creative work, which I believe software design falls under, it's good to be uninterrupted to produce the best output. For brainstorming, this means not blocking bad ideas to keep the creative juices flowing. For coding this means being able to use functions that people expect to be there even if they are strictly not needed.
In the end, it's good to keep in mind that software is for humans, not for computers.
New functionality for directory checks
Now, back to the original topic, the new functionality in Flysystem 3. The new method are:
/** @var boolean $directoryExists */ $directoryExists = $filesystem->directoryExists($path); /** @var boolean $anythingExists */ $anythingExists = $filesystem->has($path);
These new methods are implemented in all of the supported adapters and you can now upgrade your dependencies to make use of them. Hope you like the new version 😊👍
Checkout league/flysystem on packagist.