cgtag/php-disposable

一个为PHP添加可丢弃模式的微型库

1.0.0 2017-02-26 17:21 UTC

This package is not auto-updated.

Last update: 2024-09-28 20:45:56 UTC


README

License Build Status codecov Total Downloads Latest Stable Version

一个为PHP添加可丢弃模式的微型库

需求

PHP 7.1或以上(目前至少PHP 7.1.0)

安装

您可以使用以下命令通过Composer轻松安装此库:Composer

    $ composer require cgtag/php-disposable

文档

使用此库的基本说明是实现IDisposable接口并声明一个实现所有资源清理逻辑的dispose()方法。

以下示例显示了基本模式的一个简单实现。

use cgTag\Disposable\IDisposable;

class ResourceHolder implements IDisposable {
    
    private $file;
    
    public function __constructor(string $filename) {
        $this->file = fopen($filename, "r");
    }
    
    public function read() {
        return stream_get_contents($this->file);
    }
    
    public function dispose() {
        if($this->file) {
            fclose($this->file);
        }
        $this->file = null;
    }
}

现在您可以使用全局的using函数在完成对象使用后销毁该对象。

以下是using函数的一个示例

$content = using(new ResourceHolder(), function($resource) {
    return $resource->read();
});

这看起来很简单,但一旦您开始遵循可丢弃模式,内存泄漏将成为过去式。

dispose()

确保将dispose()的调用传播到继承类中。您可以通过重写dispose()方法并确保调用parent::dispose()来实现这一点。

注意:dispose()只会被调用一次。它将在调用__destruct()之前作为类上执行的最后一个方法。由于在调用时将调用dispose(),因此您不必担心之后使用属性。您不必担心属性在之后被使用,因为dispose()会被调用。

IDisposable接口

这是库中使用的首要接口。在PHP中,垃圾收集器会在对象不再使用时自动释放分配给托管对象的内存。然而,无法预测垃圾收集何时发生。此外,垃圾收集器对未管理资源(如文件句柄、图像和流)没有任何了解。

如果使用此接口,请使用dispose()方法显式释放未管理资源,并配合垃圾收集器。当对象不再需要时,对象的使用者可以调用此方法。

全局using()函数

提供一个便利函数,确保正确使用IDisposable对象。以下示例显示了如何使用全局using()函数。

    using(new ConfigReader("config.ini"), function(ConfigReader $reader) {
        $debug = $reader->get('debug');
    });

using函数确保即使在调用对象的方法时发生异常,也会调用dispose()。您可以通过在try块中放置对象并在finally块中调用dispose()来实现相同的结果;实际上,这正是using函数的编写方式。前面的代码示例可以写成以下示例

    $reader = new ConfigReader("config.ini");
    try {
        $debug = $reader->get('debug');
    } finally {
        $reader->dispose();
    }

您可以实例化资源对象,然后将变量传递给using函数,但这不是最佳实践。在这种情况下,即使它可能不再有权访问其未管理资源,对象也会在控制离开using块后保持作用域。换句话说,它将不再完全初始化。如果您在using回调之外尝试使用对象,则可能会抛出异常。因此,通常最好将对象作为传递给using的参数实例化,并将作用域限制在using回调中。

   $reader = new ConfigReader("config.ini");
   using($reader, function(ConfigReader $reader) {
       // use reader
   });
   // reader is still in scope, but calling it throws an exception
   $debug = $reader->get('debug');

注意:using不应与use混淆

DisposableTrait

DisposeTrait 允许对象自动释放公共属性。当一个对象实现了 IDisposable 接口并在对象被释放时使用 DisposeTrait,则所有引用 IDisposable 对象的公共属性也将被释放并取消设置。该特性还会深度遍历所有公共数组,释放数组中的任何对象。

以下是一个使用上面示例中的 ConfigReader 的对象示例

use cgTag\Disposable\IDisposable;
use cgTag\Disposable\Traits\DisposeTrait;

class Service implements IDispose {
    use DisposeTrait;
    
    public $reader;
    
    public function __constructor() {
        $this->reader = new ConfigReader("config.ini");
    }
}

私有属性和内存泄漏

DisposeTrait 无法释放私有属性,但当私有属性引用实现 IDisposable 接口的对象时,会抛出异常。特性这样做是因为它发现了一个可能的内存泄漏。由于对象正在使用特性且不支持私有属性,这意味着该属性可能没有被释放。

为了解决这个冲突,请将属性设置为公共的或在对象上实现 dispose() 方法。

许可证

php-disposable 在 MIT 许可证下授权 - 有关详细信息,请参阅 LICENSE 文件