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 提供处理程序的自动调用。您的函数中可以有多个 returns (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 模式?如何?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 RAII 对套接字进行建模

如何使用 RAII 属性创建订阅者对象?

如何检测浏览器是不是支持暗模式

C++如何自动调用析构函数?

除了 C++ 之外,其他语言的程序员是不是使用、了解或理解 RAII?

如何放置 CSS 模式而不是基于 php 的网站背景