Menu

Cleaning up after yourself

This is an old post from 2015. As such, it might not be relevant anymore.

Everyone likes a nice clean interface; some of the required scaffolding simply gets in the way of what we want to do. A classic example of this is closing things automatically; there are several ways to do this (using tweaked code examples from the PHP manual):

Finally

You can see that, in the code below, we do not call fclose when we throw an exception because the finally will kick in and do it for us automatically.

function example($url) {
    try {
        $handle   = fopen($url, "rb");
        $contents = '';

        if (false === $handle) {
            throw Exception('Failed to open stream to URL');
        }

        while (! feof($handle)) {
            $contents .= fread($handle, 8192);
        }

        return $contents;
    } finally {
        fclose($handle);
    }
}

Deferred

“finally” is an OK method, but then one ends up having try-catch blocks all over the place. It’s much better to have an interface to do it for us. In the programming language Go there is a feature called defer which prevents things from executing until a function has complete. It would be nice to do this in PHP. Consider the following using a Defer class and a closure:

class Defer
{
    private $closure;

    public function __construct($closure) {
        $this->closure = $closure;
    }

    public function __destruct() {
        call_user_func($this->closure);
    }
}

function example($url) {
    $handle   = fopen($url, "rb");
    $contents = '';

    // Magic happens here
    // Pass the $handle into the closure so we have access to it
    // When the function ends the variables will be out of scope and the $defer deconstructor
    // .. will be automatically called, closing the connection.
    $defer = new Defer(function () use ($handle) {
        fclose($handle);
    });

    if (false === $handle) {
        throw Exception('Failed to open stream to URL');
    }

    while (! feof($handle)) {
        $contents .= fread($handle, 8192);
    }

    return $contents;
}

The benefits of this are that you can pass anything into the Defer class to have it destroyed (or any other kind of functionality, come to that) when it goes out of scope: opening files, curl requests, etc.).