PHP是否支持RAII模式?如何实现?

15
大多数关于PHP的资源都不涉及内存管理,因为语言本身在这方面做得很好。但是,在PHP中,您经常需要处理外部资源,这些资源不是内存——例如数据库句柄、会话、数据库事务等。这些外部资源可以使用某种形式的RAII对象进行最清晰的管理。
我最初认为PHP使用类似于JVM或CLR的垃圾回收方案,其中不存在析构函数的概念。(请记住:每个人都以错误的方式思考垃圾回收——终结器不是析构函数!)有特殊的__destruct方法,但我认为它是类似于Java或C#终结器的“终结器”。因此,在JVM或CLR上无法使用RAII(C#的using块可以让您完成大约95%的工作,但这有点不同...)。
然而,Google似乎表明PHP支持RAII模式,尽管我在PHP文档中找不到验证。语言是否支持并将清理逻辑放在__destruct中足以完成RAII任务?
4个回答

15

这个问题与在PHP中,析构函数是否可预测?几乎是一样的,答案也相同。 PHP使用引用计数,它承诺在引用计数降为零时(通常是对象超出范围时)立即调用析构函数。因此,如果您创建一个对象并确保不将其泄漏超出作用域,RAII是可行的。


5
另外需要注意的是:当多个对象同时离开作用域时,它们的析构函数的调用顺序是正式未定义的,通常按照先进先出的顺序(与正确的RAII所需的相反)。对于我的特定用例来说,这是不能接受的。 - Brilliand
@Brilliand 你可以人为地添加大括号来强制排序? :) - hobbs
花括号不行 - 只有函数才能引入新的作用域。我想这仍然是可能的,但那可能会导致很多样板文件。 - Brilliand
“执行周围”惯用语(https://dev59.com/SXRC5IYBdhLWcg3wUfJ2)看起来可能会有所帮助。但是,我担心的是当多个作用域同时结束时会发生什么(特别是当抛出异常时)-也许它们最终都会以FIFO顺序结束? - Brilliand

5

PHP使用引用计数,所以当您完成变量时,它会立即得到清除。(除非您创建了循环。)这样可以及时释放资源,因此通常不需要担心显式资源管理,只需确保不创建内存循环即可。

如果您确实想要实现任何特定策略,可以通过确保该资源仅由一个变量使用来实现。每当该变量指向资源时,应立即释放该资源。


4
以下的类 ReturnHandlerReturnHandler 实例超出作用域时提供了处理程序的自动调用。您可以在函数 (myfunc) 中有多个 return,无需考虑在每个 return 前释放资源。
/**
 * 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);
  }
}

对于大多数情况,我更喜欢使用一个包装所需资源的类型。但是如果你的程序中只有一个给定资源的实例,这将作为一种快速而简单的解决方案。 - Billy ONeal

0
略微偏题:您可以使用lambda表达式来实现类似于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语法...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接