RAII 成语为finally 块提供代码级替代。创建一个包含可调用对象的类。在析构函数中,调用可调用对象。
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 没有变量的块作用域,所以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";
将导致输出:
最后创建全局。
Foo::use 尝试完成。
最后用于方法运行。
脚本完成了一大堆工作。
Global 终于运行了。
$这个
PHP 5.3 闭包无法访问$this(在 5.4 中已修复),因此您需要一个额外的变量来访问某些 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 中得到了解决。目前,private and protected properties 可以通过引用访问,正如 Artefacto 在他的 answer 中针对本网站其他地方的这个主题的问题中所展示的那样。
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 可以使用反射访问。您实际上可以使用相同的技术来访问非公共属性,但引用更简单、更轻量级。在anonymous functions 的 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 块至少需要一个 catch。如果你只想要try/finally,添加一个catch 块来捕获非Exception(PHP 代码不能抛出任何不是从Exception 派生的东西)或重新抛出捕获的异常。在前一种情况下,我建议将StdClass 作为一个成语,意思是“什么都抓不到”。在方法中,捕捉当前类也可以用来表示“不捕捉任何东西”,但使用StdClass在搜索文件时更简单,更容易找到。
try {
$finally = new Finally(/*...*/);
/* ... */
} catch (StdClass $exc) {}
try {
$finally = new Finally(/*...*/);
/* ... */
} catch (RuntimeError $exc) {
throw $exc
}