Give your projects a Gaufrette

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 GaufretteAdapter 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 GaufretteAdapterLocal('/path/to/a/directory');
$filesystem = new GaufretteFilesystem($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 GaufretteAdapterAmazonS3($amazon, 'my_bucket');
$filesystem = new GaufretteFilesystem($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(GaufretteFilesystem $filesystem)
    {
        $this->filesystem = $filesystem;
    }

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

We want to test it.

As the ArticleManager uses a GaufretteFilesystem 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('GaufretteFilesystem', 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 GaufretteFilesystem(new GaufretteAdapterInMemory());
        $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.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>