我怎样才能解决PHP中缺less一个finally块的问题?

在5.5之前的PHP没有最后的阻止 – 也就是说,在大多数合理的语言中,你可以这样做:

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

PHP没有一个finally块的概念。

任何人都有解决这个相当刺激的语言孔的经验?

解决scheme,没有。 刺激繁琐的解决方法,是的:

 $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,但应该工作。

请注意 :PHP5.5终于(ahem,对不起)添加了一个终止块: https : //wiki.php.net/rfc/finally (只需要几年…在5.5 RC中可用近四年的时间自从我发布这个答案的date…)

RAII成语为finally一个块提供了一个代码级的替身。 创build一个保存可调用的类。 在解列器中,调用可调用的函数。

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

协调

请注意,PHP没有variables的块范围,所以在函数退出或(在全局范围内)closures序列之前, Finally才会启动。 例如,以下内容:

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

将导致输出:

最后创build全局。
 Foo :: useTry完成。
最后用于方法运行。
一大堆工作由脚本完成。
全球终于终于跑了。

$这

PHP 5.3中的闭包无法访问$this (在5.4中修复),因此您需要一个额外的variables来访问某些finally块中的实例成员。

 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() {/*...*/} } 

私人和受保护的字段

这个方法在PHP 5.3中最大的问题就是finally-closure不能访问对象的私有和受保护的字段。 就像访问$this ,这个问题在PHP 5.4中解决。 现在, 私人和受保护的属性可以通过引用来访问,因为Artefacto在他对这个网站上其他地方的这个话题的回答中显示。

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

可以使用reflection访问私有和受保护的方法 。 您实际上可以使用相同的技术访问非公共属性,但引用更简单,更轻量。 在关于匿名函数的PHP手册页的评论中,Martin Partel给出了一个FullAccessWrapper类的例子,该类打开了公共访问的非公共字段。 我不会在这里重现它(请参阅前面的两个链接),但是这里是你如何使用它:

 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

try块至less需要一个catch 。 如果你只想try/finally ,添加一个catchException (PHP代码不能抛出任何不是从Exception派生的东西)的catch块或重新抛出捕获的exception。 在前一种情况下,我build议把StdClass作为一个成语意思是“不要捉到任何东西”。 在方法中,捕获当前类也可以用来表示“不捕捉任何东西”,但是在search文件时使用StdClass更简单,更容易find。

 try { $finally = new Finally(/*...*/); /* ... */ } catch (StdClass $exc) {} try { $finally = new Finally(/*...*/); /* ... */ } catch (RuntimeError $exc) { throw $exc } 

这是我的解决scheme,以最终阻止的缺乏。 它不仅为finally块提供了解决方法,还扩展了try / catch来捕获PHP错误(以及致命错误)。 我的解决scheme看起来像这样(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 } ); 

你可以下载解决scheme的文档和例子从git hub – https://github.com/Perennials/travelsdk-core-php/tree/master/src/sys

由于这是一种语言结构,因此您不会find一个简单的解决scheme。 你可以编写一个函数,并把它作为try块的最后一行和最后一行,然后重新导出try块的excepion。

好书认为不要使用finally块来释放资源,因为如果发生了一些不愉快的事情,你不能确定它会被执行。 称它为一个刺激性的漏洞是相当夸张的。 相信我,很多非常好的代码是用语言编写的,没有最终阻止。 🙂

无论try块是否成功,最后都要执行。

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

使用闭包呼叫。 第二个参数$catch是可选的。 例子:

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

正确处理各处的exception情况:

  • $try :Exception将被传递给$catch$catch将先运行,然后$finally 。 如果没有$catch ,那么运行$finally之后将重新抛出exception。
  • $catch$finally会立即执行。 $finally完成后,exception将被重新抛出。
  • $finally :exception会阻止调用堆栈的畅通无阻。 计划重新抛出的任何其他exception都将被丢弃。
  • :返回$try返回值。

如果有人仍然在跟踪这个问题,那么您可能有兴趣检查PHP wiki中最终语言特性的(全新的) RFC 。 作者似乎已经有了补丁,我相信这个提议会从其他开发者的反馈中受益。

我刚写完一个更优雅的Try Catch Finally类,可能对你有用。 有一些缺点,但他们可以解决。

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