How can I get around the lack of a finally block in PHP?

PHP prior to version 5.5 does not have a final block, i.e. while in the most reasonable languages ​​you can:

try { //do something } catch(Exception ex) { //handle an error } finally { //clean up after yourself } 

PHP has no idea about the final block.

Does anyone have experience solving this rather annoying hole in the tongue?

+54
php exception
May 29 '09 at 17:31
source share
7 answers

Decision No. Annoying cumbersome workaround, yes:

 $stored_exc = null; try { // Do stuff } catch (Exception $exc) { $stored_exc = $exc; // Handle an error } // "Finally" here, clean up after yourself if ($stored_exc) { throw($stored_exc); } 

Yucky, but should work.

Please note : PHP 5.5 finally (um, sorry) added a finally block: https://wiki.php.net/rfc/finally (and it only took a few years ... available in 5.5 RC almost four years before the date I posted this answer ...)

+58
May 29 '09 at 17:36
source share

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( # if $self is used by reference, it can be set after creating the closure function () use ($self) { $self->frob(); }, # $this not used in a closure, so no need for $self array($this, 'wibble') ); /*...*/ } function frob() {/*...*/} function wibble() {/*...*/} } 

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 } 
+9
Feb 11 2018-12-12T00:
source share

Here is my solution for missing a finally block. It not only provides work for the finally block, but also extends the capabilities of try / catch to catch PHP errors (and fatal errors too). My solution looks like this (PHP 5.3):

 _try( //some piece of code that will be our try block function() { //this code is expected to throw exception or produce php error }, //some (optional) piece of code that will be our catch block function($exception) { //the exception will be caught here //php errors too will come here as ErrorException }, //some (optional) piece of code that will be our finally block function() { //this code will execute after the catch block and even after fatal errors } ); 

You can download the solution with documentation and examples from git hub - https://github.com/Perennials/travelsdk-core-php/tree/master/src/sys

+2
May 30 '12 at 18:43
source share

Since this is a language construct, you will not find a simple solution for this. You can write a function and call it the last line of the try block and the last line before rebuilding excepion in the try block.

Good books do not allow finally blocks to be used for anything other than freeing up a resource, because you cannot be sure that it will execute if something unpleasant happens. Calling it an annoying hole is quite an exaggeration. Believe me, damn it, very good code is written in languages ​​without a final block. :)

The point must finally be executed regardless of whether the try block was successful or not.

+1
May 29 '09 at 19:08
source share
 function _try(callable $try, callable $catch, callable $finally = null) { if (is_null($finally)) { $finally = $catch; $catch = null; } try { $return = $try(); } catch (Exception $rethrow) { if (isset($catch)) { try { $catch($rethrow); $rethrow = null; } catch (Exception $rethrow) { } } } $finally(); if (isset($rethrow)) { throw $rethrow; } return $return; } 

Call using closures. The second parameter $catch is optional. Examples:

 _try(function () { // try }, function ($ex) { // catch ($ex) }, function () { // finally }); _try(function () { // try }, function () { // finally }); 

Handles exceptions correctly everywhere:

  • $try : An exception will be thrown $catch . $catch will be executed first, then $finally . If there is no $catch , the exception will be thrown after $finally launched.
  • $catch : $finally will execute immediately. The exception will be thrown after the completion of $finally .
  • $finally : An exception will seamlessly destroy the call stack. Any other exceptions planned for retransmission will be discarded.
  • No : returns the return value from $try .
+1
Jul 14 '13 at 10:43
source share

If someone is still tracking this issue, you might be interested in checking out the (new) RFC for the final language feature in the PHP wiki. The author already has working corrections, and I am sure that this proposal will benefit from feedback from other developers.

0
Jul 31 '12 at 8:05
source share

I just finished writing a more elegant Try Catch finally class that might come in handy. There are some disadvantages, but they can be circumvented.

https://gist.github.com/Zeronights/5518445

0
May 04 '13 at 19:23
source share



All Articles