A first approach to the Filesystem API

Since I’ve seen little examples of proper and clear Filesystem API usage in the web I’ve put together a little utility class to deal with my file-writing needs.
The class is not a colossus of logic and implementation and it’s baked into my Adapter Classes for WordPress plugin but can be conveniently dropped into any kind of code.
I can include its file in my code

include_once "/path/to/libs/FileWriter.php";

get hold of one instance of it

$file_writer = new FileWriter();

and then just call it with parameters

$fileWriter->put_contents(
    'some/url',
    'some string';
    '/path/to/someFileName.txt' 
    );

The URL

Aside for $contents and $filename which can be very well guessed, the first parameter, $url is not so clear if not with a little understanding of the Filesystem API. While trying to write the file the writer will prompt the user to enter the credentials to write, in the class defaulted to the FTP ones, and will echo a form requesting them to the user. The form needs to tie to the caller, the page the file writing attempt was made in, to return to it. Convoluted but it means the file writer needs to know who called it.

Catch because it throws

The writer takes care of asking for user credentials if needed and returns proper bool values on success and failures; it will raise Exceptions on bad parameters or runtime errors so try-catch

try{
    $wrote = $fileWriter->put_contents(
        'some/url',
        'some string';
        '/path/to/someFileName.txt' 
        );

    $wrote ? echo 'success!' : echo 'could not write. need credentials?';
}
catch( Exception $e ){
    // deal with the drama
    echo 'something happened';
}

It’s not safe per se

The file writer, wrapping the request_filesystem_credentials function will not make any attempt to check against nonced URLs and will simply require the credentials to write. So the security is on the developer using it.

The code

Here it is

<?php
class FileWriter
{
    private $method = 'ftp';
    public function __get($property)
    {
        if (isset($this->{$property})) {

            return $this->{$property};
        }
    }
    public function __set($property, $value) {
        if (property_exists(get_class($this) , $property)) {
            $this->{$property} = $value;
        }
    }
    /**
     * Writes strings to file
     * @param  string $url      The URL the function is called from.
     * @param  string $contents The contents to write to file
     * @param  string $filename The filename absdolute path
     * @return bool           False if filesystem credentials are required, true if the file has been written to file   or thrown exceptions if parameters are wrong or the underlying WordPress function encountered some problems.
     */
    public function put_contents($url, $contents, $filename)
    {
        // check the parameters
        if (!is_string($url) or $url == '') {

            throw new InvalidArgumentException('URL must be a non empty string.');
        }
        if (!is_string($action) or $action == '') {

            throw new InvalidArgumentException('action must be a non empty string.');
        }
        if (!is_string($contents)) {

            throw new InvalidArgumentException('Contents must be a string.');
        }
        if (!is_string($filename) or $filename == '') {

            throw new InvalidArgumentException('File filename must be a non empty string.');
        }
        // request the filesystem credentials
        if (false === ($creds = request_filesystem_credentials($url, '', $this->method, false, null))) {
            // here the user is presented with a form to fill with FTP username and password

            return false;
        }
        // check to see if the credentials work or ask for them again
        if (!WP_Filesystem($creds)) {
            $creds = request_filesystem_credentials($url, '', $this->method, false, null);

            return false;
        }
        global $wp_filesystem;
        if (!$wp_filesystem->put_contents($filename, $contents, FS_CHMOD_FILE)) {
            // if there was an error while trying to write the file throw an exception

            throw new RuntimeException('Could not write contents to file.');
        }
        // return true if everything was ok

        return true;
    }
}