KnpLabs / Gaufrette

PHP library that provides a filesystem abstraction layer − will be a feast for your files!
http://knplabs.github.io/Gaufrette
MIT License
2.47k stars 355 forks source link

[Feature request] Prefixed/chrooted filesystems #474

Open OJezu opened 7 years ago

OJezu commented 7 years ago

Proposal:

I propose adding a class, that allows to transparently expose a prefixed part of Gaufrette Filesystem. That is, all reads and writes from instance of it, will have a prefix added to the file key.

I think the best way to do it is to create FilesystemInterface specifying public methods of Filesystem, make Filesystem implement it, and add class named PrefixedFilesystem which constructor will take two arguments, the wrapped Filesystem and prefix which should be transparently used with all the file keys.

interface FilesystemIterface;
class Filesystem implements FilesystemInterface;
class PrefixedFilesystem implements FilesystemInterface 
{
    public function __construct(FilesystemInterface $filesystem, string $keyPrefix);
}

I thought about adding a adapter which would wrap another adapters, the problem here is that wrapped adapters may or may not implement additional feature interfaces, which will result in either loss of performance, or repeating of the fallback code from Filesystem class.

Another option is an Adapter wrapping around Filesystem adding a prefix and then plugging that into a new instance of Filesystem. This has advantage of not modifying existing class hierarchy, at the cost of somewhat superfluous wrapping Filesystem(Adapter(Filesystem(Adapter))))

Rationale:

If a hierarchical key structure is used, PrefixedFilesystem would allow to expose consistent part of Filesystem as needed in a way abstract from underlying storage.

We are keeping groups of files connected to class of entities on a single Gaufrette Filesystem (in this case used adapter is Local) and use a service to manage those files. One of the features required from that service is to upload and download files to and from the filesystem. Our upload method takes the entity for which the upload is to be made and an instance of another Gaufrette Filesystem, it then determines prefix under which the files should be stored in the main Filesystem.

Usually adapter behind the upload is Zip, but using a Filesystem provides a nice abstraction over the way the files are delivered, they could be as well downloaded from s3 etc. I would like to provide similar method to download files connected to an entity, by exposing their part of the storage as Filesystem too, which in this case would be returning an instance of PrefixedFilesystem wrapping the main Filesystem with appropriate prefix.

akerouanton commented 7 years ago

You're right: decorating the filesystem is the best way to achieve this since you can't predict which interfaces will be implemented by the decorated adapters if you go the other way (the filesystem would have to mock behaviors provided by decorated adapters). I'm not sure to understand your rationale by the way. I think an exemple is worth a thousand words :)

Sorry I forgot to ping you when I added the FilesystemInterface, you can now use it. I created a new repository named gaufrette/extras holding extras features like filesystem decorators (we don't want to merge non-essential features here). You can make a PR over there (or an issue if you want to continue this discussion).

Thank you for your interest for Gaufrette :)

OJezu commented 7 years ago

Example could be, a Gaufrette filesystem used to keep files uploaded by users, where each user's file is stored with separate prefix. Exposing single user's files could be easily and securely done with PrefixedFilesystem.