Originally published at OpenCraft.
What happens if you need to open a file with fopen
, but find that your function can exit at multiple points? It is tedious and error-prone to call fclose
at each exit point. Alternatively, you could re-structure your code to only exit at the bottom, thereby calling fclose
only once, but you would end up with many nested blocks that hamper readibility and are generally considered bad programming style.
A technique I like to use in those cases is the Automatic Resource Destructor. The idea is to create an object instance that destroys the used resource, in our case the file handle, when its own destructor is called. Here's how it works:
<?php
class FileDestructor {
function __construct($resource) {
$this->resource = $resource;
}
function __destruct() {
fclose($this->resource);
}
function get() {
return $this->resource;
}
private $resource;
}
?>
The way to use this in your code is the following:
<?php
function foo() {
$file = new FileDestructor(fopen('file.txt', 'r'));
$text = fread($file->get(), 1024);
if ($text[0] != 'X') return FALSE;
// Some more processing...
return TRUE;
}
?>
Notice that you don't need to call fclose
because the destructor of $file
will be called automatically upon exiting the function. This is the benefit of using this file destructor.
You can generalize the above code to work with any kind of resource that has a single-valued destructor function:
<?php
class ResourceDestructor {
function __construct($resource, $destroy) {
$this->resource = $resource;
$this->destroy = $destroy;
}
function __destruct() {
call_user_func($this->destroy, $this->resource);
}
function get() {
return $this->resource;
}
private $resource;
private $destroy;
}
?>
In this case, you would instantiate the object like the following:
<?php
$file = new ResourceDestructor(fopen('file.txt', 'r'), 'fclose');
$db = new ResourceDestructor(mysql_connect(...), 'mysql_close');
?>
Finally, instead of repeating the close function every time, you can create subclasses for each resource type:
<?php
class FileDestructor extends ResourceDestructor {
function __construct($resource) {
parent::__construct($resource, 'fclose');
}
}
class MySqlDestructor extends ResourceDestructor {
function __construct($resource) {
parent::__construct($resource, 'mysql_close');
}
}
?>
Happy coding!