Give your projects a Gaufrette

Published on

Oct 9, 2011

technical

Oct 10, 2011 − If you're tired of the way you deal with files in your PHP projects. If you think dealing with files in PHP is a nightmare. If you already tried to write unit tests for filesystem manipulations. Then you should try Gaufrette, it changes the games.

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.

Written by

KNP Labs
KNP Labs

Comments