RSS

Blog

Give your projects a Gaufrette

By Antoine Hérault
10 October 2011
In the Development category

Gaufrette is a project I started last year in my R&D time and that lives in a public GitHub repository.

For years now, I've enjoyed working with objects in my scripts. So naturally, the built-in filesystem manipulation functions of PHP drive me crazy! Gaufrette to the rescue.

Gaufrette offers a simple object-oriented API to abstract both filesystems and files. This means that you get the same fluid API, whether your filesystem is local, on Amazon S3, or anywhere else. Cool, right!

Storage Agnostic

Web applications often use a lot of media. Should you store them in the cloud? What about in a local directory? At the beginning of development, you don't care! The only thing you know is that they need to be stored somewhere.

One of Gaufrette's main goals is to provide a unified API to deal with files, so you can switch a filesystem's adapter without changing the code of your application.

If you don't find a suitable match in the existing adapters, you can create your own. It's as simple as implementing the Gaufrette\Adapter interface. And if you think it can interest other people, your pull request will be welcome.

Take a look at the following code that creates a local file if it doesn't exist and then reads its content:

<?php

$adapter = new Gaufrette\Adapter\Local('/path/to/a/directory');
$filesystem = new Gaufrette\Filesystem($adapter);

if ( ! $filesystem->has('foo')) {
    $filesystem->write('foo', 'Some content');
}

echo $filesystem->read('foo');

Ok, there is nothing spectacular here. But now we want the file to be stored in an Amazon S3 bucket:

<?php

$amazon = new AmazonS3('myKey', 'mySecretKey', 'myToken');
$adapter = new Gaufrette\Adapter\AmazonS3($amazon, 'my_bucket');
$filesystem = new Gaufrette\Filesystem($adapter);

if ( ! $filesystem->has('foo')) {
    $filesystem->write('foo', 'Some content');
}

echo $filesystem->read('foo');

As you can see, the only part that changes is the adapter.

Testable

If you've every tried to test any file manipulation, you know it's a difficult thing, especially using the built-in PHP functions.

As exemple, let's create a simple ArticleManager class that reads articles stored in markdown files:

<?php

class ArticleManager
{
    private $filesystem;

    public function __construct(Gaufrette\Filesystem $filesystem)
    {
        $this->filesystem = $filesystem;
    }

    public function getArticle($name)
    {
        return $this->filesystem->read($name . '.markdown');
    }
}

We want to test it.

As the ArticleManager uses a Gaufrette\Filesystem instance to interact with the articles filesystem, you can easily mock it:

<?php

class ArticleManagerTest extends \PHPUnit_Framework_TestCase
{
    public function testGetArticle()
    {
        $filesystem = $this->getMock('Gaufrette\Filesystem', array(), array(), '', false);
        $filesystem
            ->expects($this->once())
            ->method('read')
            ->with($this->equalTo('foo.markdown'))
            ->will($this->returnValue('The article\'s content'))
        ;

        $manager = new ArticleManager($filesystem);

        $this->assertEquals('The article\'s content', $manager->getArticle('foo'));
    }
}

But it really depends on the implementation... In exemple, replace return $this->filesystem->read($filename); with return $this->filesystem->get($filename)->getContent();. The tests fails, even if the method still behaves correctly.

This is why we can do better using a test dedicated adapter to simulate a filesystem in memory:

<?php

class ArticleReaderTest extends \PHPUnit_Framework_TestCase
{
    public function testGetArticle()
    {
        $filesystem = new Gaufrette\Filesystem(new Gaufrette\Adapter\InMemory());
        $filesystem->write('foo.markdown', 'The article\'s content');

        $manager = new ArticleManager($filesystem);

        $this->assertEquals('The article\'s content', $manager->getArticle('foo'));
    }
}

Simple isn't it?

Join the adventure

Even if the Gaufrette's API is still not stable, here at KNP Labs, we already used it in production. Ok, maybe you'll say we are a bit crazy, and maybe you're right. But the fact remains that a good library is primarily a library that solves concrete problems.

So if you are interested in helping making Gaufrette a good library, then try it out and join us.

  • 2012-11-25 Tawfekov

    I Like it , really 

  • 2012-10-24 Casey Flynn

    I really like the bundle, but when I tried to implement it I couldn't get the Amazon S3 stream wrapper to dump the projects filtered assets via assetic:dump to the s3. it kept getting stuck on assetic dumps call to is_dir. The amazon sdk however did not have the same problem. has anyone else had this issue?

  • 2012-08-22 damienalexandre

    The way the Bundle is done does not allow to move a file without reading it, even with the "Local" adapter because you are supposed to be able to switch adapters without changing your code.

    After a file upload, you have to read the uploaded file if you want to send it to S3, FTP or whatever. If you are only working with Local adapter, you can cheat by using something like this :

    move($uploaded_file_path, $filesystem->getAdapter()->computePath($dest_key));

    But that's kind of dirty.

    Gaufrette come with great features and flexibility, but there is also downsides: limited API, mandatory file reading, no way to copy from one filesystem to another easily...

  • 2012-08-22 Nico

     have you found a solution or an answer yet ? I would be very interested in an example for managing file uploads with Gaufrette aswell :)

  • 2012-03-03 nasragiel

    Hey, could you please come up with an example of how you deal with uploaded files and Gaufrette? Moving is not an option as the adaperts just work in "their" filesystem and the uploaded file is out of that. So do you always have to read that file first using file_get_contents() and write it using the adapter and then delete the uploaded file using unlink()? How does this effect the memory if I have large file uploads of ~120mb like for videos?

  • 2011-11-02 orbi

    Bravo! That's a nicely designed Virtual File System. And with a permissive license, it's definitely going in my toolkit. :-)

  • 2011-10-13 Johannes S

    You should try the AclAwareAmazonS3 adapter.

  • 2011-10-11 Antoine Hérault

    The initial implementation of Gaufrette was quite simple. But with the help of some contributors, we are working on the metadata support.

  • 2011-10-11 Ruud Kamphuis

    I like Gaufrette but with Amazon S3 it's pretty useless because you cannot set additional parameters like permissions etc..

  • 2011-10-10 Cedric Delalande

    Excellent ! I'll look into it.

  • 2011-10-10 Antoine Hérault

    It's because if you use the `file_exists` function against a directory, it'll return TRUE.

  • 2011-10-10 damienalexandre

    Nice project, already used with the SF2 Bundle :-)

    But I still don't get why you are using "is_file" instead of "file_exists" - is there a reason? (https://github.com/knplabs/Gau....

    Gaufrette FTW!

  • 2011-10-10 vh

    Cool, thanks guys