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;
}
}