Sometimes we work with instances that needs to released even when exceptions happens. Something typical when we work with resources (Files, Database connections, …) Let me show you with an example:
Imagine this class for handling file resources:
class File { private $resource; private $logger; public function __construct($filename, Loger $logger) { $this->logger = $logger; $this->resource = fopen($filename); } public function write($string) { fwrite($this->resource, $string); } public function close() { $this->logger->log("file closed") fclose($this->resource); } }
We can use this class:
$file = new File(__DIR__ . "/file.txt", 'w'); $file->write("Hello\n"); // ... // some other things // ... $file->write("Hello\n"); $file->close();
OK. What happens if one exception happens inside “some other things”? Simple answer: close() function isn’t called. This can be a problem. We can face this problem with a try/catch block like this:
try { $file->write("Hello\n"); // ... // some other things // ... $file->write("Hello\n"); $file->close(); } catch (\Exception $e) { $file->close(); }
Or even if we’re using PHP5.5 we can use “finally” keyword
try { $file->write("Hello\n"); // ... // some other things // ... $file->write("Hello\n"); } catch (\Exception $e) { } finally { $file->close(); }
Sometimes I need collaborate with C# projects. C# is a great language. I really like it. It has a really cool feature to solve this problem: the “using” statement. Because of that we are going to build today one small library to implement something similar in PHP.
First we will add G\IDisposable interface to our File class
namespace G; interface IDisposable { public function dispose(); }
Now our File class looks like this:
use G\IDisposable; class File implements IDisposable { private $resource; public function __construct($filename, $mode) { $this->resource = fopen($filename, $mode); } public function write($string) { fwrite($this->resource, $string); } public function close() { fclose($this->resource); } public function dispose() { $this->close(); } }
And we can use our “using” function in PHP:
using(new File(__DIR__ . "/file.txt", 'w'), function (File $file) { $file->write("Hello\n"); $file->write("Hello\n"); $file->write("Hello\n"); });
As we can see we can forget to close() our file instance. “using” will do it for us, even if one exception is triggered inside. We need to take into account that “using” is not a way to handle exceptions. If you want to handle them you need to do it. The only responsibility of “using” is to ensure that dispose() is been called.
We also can use an array of instances (implementing the IDisposable interface of course)
using([new Bar, new Foo], function (Bar $bar, Foo $foo) { echo $bar->hello("Gonzalo"); echo $foo->hello("Gonzalo"); });
And that’s all. Library is available in github and also you can use it with composer.
Update!
I’ve changed the Example. The first example wasn’t a good one. Now File::close() closes the resource and also write a log. With the first example (without the logger) it wasn’t important to close the resorce (PHP closes it).
Why not just use __destruct() in the wrapper class?
__destruct() isn’t called if one exception is thrown. “using” ensures that disponse() is called (even with exceptions)
Actually it does, unless you are using the global namespace.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
destruct_example.php
hosted with ❤ by GitHub
I realized this within this post. That’s because $test goes out (or not) of scope. My first impression was it was a bug but it isn’t (php facts :)). We can do the same with __desctruct (if we wrap the code within a block) but anyway I still prefer the usage of dispose() instead of __destruct
I don’t want to sound pedantic, but what’s the gain versus “finally”?
I see the same amount of code, and it’s a little less readable and explicit.
In fact the “using” function creates a try/finally block (I not use finally keyword because of compatibility). IMHO is more readable with the interface. If one class implements IDisposable it must implements dispose() function (where we release resources). It’s very common in C# and Python (“with” keyword).
“using” also allows you to use an array of IDisposable instances. You can do it of course with try/finally blocks but the code will be more comples
You don’t need this complication. PHP will automatically clean up resources (close them) when the variable goes out of scope and is garbage collected. So when an exception is thrown, and the stack frame is popped, the variables will all be collected (assuming there are no other references).
When that happens, the resource is no longer reachable and will be closed. Take this code for example:
function readFile($fileName) {
$f = fopen($fileName, ‘r’);
return fread($f, 1024);
}
The file handle is not leaked. Since `$f` goes out of scope at the end of the return, it’s destructed. And since PHP resources are closed on destruction, the resource will be closed as well.
In short, there’s no need to go through all of this complication **for this specific reason**. You may want it for other reasons (such as closing of transactions, etc), but not for resource cleaning.
touche! I’ve use the File example because is simple to understand (and it’s also the example used in almost all c# documentation about “using” 🙂 ). But PHP handles resources as you said. So maybe my example is not the best one.
I’m using something similar with PDO database transactions (in fact DBAL implements the same idea with its transactional() function)
Also it can be used when dispose() action is more complex (not only close a resource). For example, unlink temporally files, register logs or things like that.
many thanks for the clarification.
Do you think it would be a good idea to implement this functionality in the PHP core:
For example:
using($bar = new Bar(), $foo = new Foo()) {
echo $bar->hello("Gonzalo");
echo $foo->hello("Gonzalo");
});
I answer myself. Maybe is not a good thing to include “using” in the core (as in C# or “with” in python) due to it’s deterministic destruction.
Maybe if we want to use we can use it as a library or maybe using __destruct() (I’m not a big fan of __destruct() but is more or less the same)
Hi, I think that the functionality “using” is very interesting, in a specific scope It could be a good solution, is a good tool, thanks. By the way, the code of your first example, at line 19, there is a bug, the $logger variable does not exists, it may be $this->logger.
changed!. Thanks