PHP 是不是支持 RAII 模式?如何?
Posted
技术标签:
【中文标题】PHP 是不是支持 RAII 模式?如何?【英文标题】:Does PHP support the RAII pattern? How?PHP 是否支持 RAII 模式?如何? 【发布时间】:2011-06-23 17:54:31 【问题描述】:php 上的大多数资源从不涉及内存管理,因为该语言本身非常擅长为您做这件事。但是,在 PHP 中,您通常会处理非内存的外部资源——数据库句柄、会话、数据库事务等。这些外部资源可以使用某种形式的 RAII 对象进行最干净的管理。
我最初认为 PHP 使用了类似于 JVM 或 CLR 的垃圾回收方案,其中不存在析构函数的概念。 (记住:Everyone thinks about garbage collection the wrong way——终结器不是析构函数!)有一个特殊的__destruct
方法,但我认为这是一个类似于Java 或C# 终结器的“终结器”。出于这个原因,您不能在 JVM 或 CLR 上使用 RAII(C# 的 using
块可以让您完成大约 95% 的工作,但这有点不同......)。
但是,Google seems to indicate that PHP supports the RAII pattern,尽管我在 PHP 文档中找不到对此的验证。语言是否支持这一点并且将清理逻辑放入__destruct
足以完成 RAII 任务?
【问题讨论】:
【参考方案1】:这与Is destructor in PHP predictable? 几乎是同一个问题,答案也是一样的。 PHP 使用引用计数,它承诺一旦引用计数变为零(通常是当对象超出范围时),就会立即调用析构函数。因此,如果您创建一个对象并注意不要将其泄漏到范围之外,那么 RAII 是可行的。
【讨论】:
另一个警告:当多个对象同时离开作用域时,调用它们的析构函数的顺序是官方未定义的,通常是 FIFO 顺序(与正确的 RAII 所需的完全相反)。这对我的特定用例来说是个大问题。 @Brilliand 你可以人为地添加大括号来强制排序吗? :) 大括号不会这样做 - 只有一个函数可以引入一个新的范围。我想还是有可能的,但这可能相当于很多样板。 “执行周围”成语 (***.com/questions/341971/…) 看起来可能会有所帮助。但是,我担心当多个作用域同时结束时会发生什么(特别是当抛出异常时)——也许它们都会以 FIFO 顺序结束?【参考方案2】:PHP 使用引用计数,因此当您使用完一个变量后,它会立即被清除。 (除非您创建循环。)这会迅速释放资源,因此您通常无需担心显式资源管理,只需小心不要创建内存循环。
如果您确实想要实施任何特定策略,您可以通过确保资源仅由一个变量使用来实现。每当该变量指向远离资源时,应立即释放该资源。
【讨论】:
【参考方案3】:当ReturnHandler
的实例超出范围时,以下类ReturnHandler
提供处理程序的自动调用。您的函数中可以有多个 return
s (myfunc
),而无需考虑在每个函数之前释放资源。
/**
* Automatically calls a handler before returning from a function. Usage:
*
* function myfunc()
*
* $resource = new Resource();
* $rh = new ReturnHandler( function() use ($resource) $resource->release(); );
* // ...
* if(...)
* return; // look, ma, automatic clean up!
*
*
*/
class ReturnHandler
private $return_handler;
public function __construct( $return_handler )
$this->return_handler = $return_handler;
public function __destruct()
$handler = $this->return_handler;
$handler();
这是一个测试:
class ReturnHandlerTest extends PHPUnit_Framework_TestCase
private static function trigger_return_handler(&$var)
$rh = new ReturnHandler(function() use (&$var) $var++; );
public function test()
$a = 0;
$this->assertEquals(0, $a);
self::trigger_return_handler($a);
$this->assertEquals(1, $a);
【讨论】:
我宁愿有一种类型可以包装有问题的资源以供大多数用途。但这将作为一个快速而肮脏的解决方案,例如如果您的程序中只使用了给定资源的一个实例。【参考方案4】:有点题外话:你可以用 lambdas 做一个类似using
的模式。像这样:
function WithFile($Name, $Func)
$File = fopen($Name, 'r');
$r = $Func($File);
fclose($File);
return $r;
然后像这样使用它
$FileHeader = WithFile('myfile', function($File) return fread($File, 16););
完全确定性。也就是说,是否有更简洁的 lambda 语法...
【讨论】:
以上是关于PHP 是不是支持 RAII 模式?如何?的主要内容,如果未能解决你的问题,请参考以下文章