【问题标题】: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 任务?

【问题讨论】:

    标签: php raii


    【解决方案1】:

    这与Is destructor in PHP predictable? 几乎是同一个问题,答案也是一样的。 PHP 使用引用计数,它承诺一旦引用计数变为零(通常是当对象超出范围时),就会立即调用析构函数。因此,如果您创建一个对象并注意不要将其泄漏到范围之外,那么 RAII 是可行的。

    【讨论】:

    • 另一个警告:当多个对象同时离开作用域时,调用它们的析构函数的顺序是官方未定义的,通常是 FIFO 顺序(与正确的 RAII 所需的完全相反)。这对我的特定用例来说是个大问题。
    • @Brilliand 你可以人为地添加大括号来强制排序吗? :)
    • 大括号不会这样做 - 只有一个函数可以引入一个新的范围。我想还是有可能的,但这可能相当于很多样板。
    • “执行周围”成语 (stackoverflow.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 语法...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-03
        • 2021-05-29
        • 1970-01-01
        • 2013-09-05
        • 2021-12-25
        • 2018-05-22
        • 2019-12-10
        相关资源
        最近更新 更多