The idiom RAII offers a code level for the finally block. Create a class that contains called calls. In the destuctor, invoke the invoked calls.
class Finally { # could instead hold a single block public $blocks = array(); function __construct($block) { if (is_callable($block)) { $this->blocks = func_get_args(); } elseif (is_array($block)) { $this->blocks = $block; } else { # TODO: handle type error } } function __destruct() { foreach ($this->blocks as $block) { if (is_callable($block)) { call_user_func($block); } else { # TODO: handle type error. } } } }
Coordination
Note that PHP does not have a block area for variables, so finally will not start until the function exits or (in the global area) the shutdown sequence. For example, the following:
try { echo "Creating global Finally.\n"; $finally = new Finally(function () { echo "Global Finally finally run.\n"; }); throw new Exception; } catch (Exception $exc) {} class Foo { function useTry() { try { $finally = new Finally(function () { echo "Finally for method run.\n"; }); throw new Exception; } catch (Exception $exc) {} echo __METHOD__, " done.\n"; } } $foo = new Foo; $foo->useTry(); echo "A whole bunch more work done by the script.\n";
will lead to the conclusion:
Creating global Finally.
Foo :: useTry done.
Finally for method run.
A whole bunch more work done by the script.
Global Finally finally run.
$ this
Closing PHP 5.3 cannot access $this (fixed in 5.4), so you will need an extra variable to access instance instances in some final blocks.
class Foo { function useThis() { $self = $this; $finally = new Finally(
Private and protected fields
Probably the biggest problem with this approach in PHP 5.3 is, finally, closing cannot access the private and protected fields of the object. Like accessing $this , this problem is resolved in PHP 5.4. So far, private and protected properties can be obtained using links, since Artefacto shows in its answer a question on this topic elsewhere on this site.
class Foo { private $_property='valid'; public function method() { $this->_property = 'invalid'; $_property =& $this->_property; $finally = new Finally(function () use (&$_property) { $_property = 'valid'; }); } public function reportState() { return $this->_property; } } $f = new Foo; $f->method(); echo $f->reportState(), "\n";
Private and protected methods can be obtained using reflection. You can use the same method to access non-public properties, but the links are simpler and easier. In a comment on the PHP manual page for anonymous functions , Martin Partel gives an example of the FullAccessWrapper class that opens non-public fields for public access. I will not reproduce it here (see the two previous links for this), but here is how you use it:
class Foo { private $_property='valid'; public function method() { $this->_property = 'invalid'; $self = new FullAccessWrapper($this); $finally = new Finally(function () use (&$self) { $self->_fixState(); }); } public function reportState() { return $this->_property; } protected function _fixState() { $this->_property = 'valid'; } } $f = new Foo; $f->method(); echo $f->reportState(), "\n";
try/finally
For blocks
try requires at least one catch . If you only want to try/finally , add a catch that will catch non- Exception (PHP code cannot throw anything that is not received from Exception ), or rethrow the caught exception. In the first case, I propose to catch StdClass as an idiom meaning "do not catch anything." In methods, catching the current class can also be used to mean “catch nothing,” but using StdClass simpler and easier to find when searching for files.
try { $finally = new Finally(); } catch (StdClass $exc) {} try { $finally = new Finally(); } catch (RuntimeError $exc) { throw $exc }