benmorel/weakmap-polyfill

PHP 7.4 的 WeakMap 兼容库

0.5.0 2024-04-02 16:29 UTC

This package is auto-updated.

Last update: 2024-09-02 18:53:21 UTC


README

此兼容库旨在与 PHP 8 中的 WeakMap 100% 兼容。

Build Status Coverage Status Latest Stable Version Total Downloads License

介绍

PHP 7.4 引入了 WeakReference,但未包含 WeakMap 实现。 自那时起,该功能已实现,但仅适用于 PHP 8。

请求规范(RFC)的作者 Nikita Popov 突出了为什么用户空间的 WeakMap 是次优的

弱映射需要一等语言支持,并且不能使用 PHP 提供的现有功能实现。

乍一看,似乎可以使用从 spl_object_id() 到任意值的数组映射来达到弱映射的目的。但这并非如此,原因有很多

  • spl_object_id() 值在对象被销毁后会被重用。两个不同的对象可以具有相同的对象 ID,只是不是在同一个时间。
  • 对象 ID 不能转换回对象,因此无法遍历映射。
  • 在对象销毁时,存储在 ID 下的值不会被释放。

使用 PHP 7.4 中引入的 WeakReference 类,可以避免前两个问题(…)。但是,这并不能解决第三个问题:当对象被销毁时,数据不会被释放。它只会在使用具有相同重用 ID 的对象进行下一次访问时释放,或者如果实现了垃圾回收机制,该机制定期遍历整个映射。

本机弱映射实现将立即从弱映射中删除对象键销毁后的值。

这是此库提供的权衡:100% 兼容的实现,但

  • 较慢
  • 其值不会在对象键销毁时立即删除,而是在再次使用 WeakMap 时删除;请注意,这也影响对象析构函数的调用时间

以下是它的工作方式

  • count() 调用将始终立即进行垃圾回收
  • 使用类似数组的特性:set、get、isset()unset() 将至少每 100 次操作进行一次垃圾回收(对于大型 WeakMaps,频率较低)。
  • 当 GC 循环收集时(自动或通过 gc_collect_cycles()),垃圾回收将立即触发

这提供了性能和内存使用之间的合理权衡。

安装

此库可以通过 Composer 安装。

composer require benmorel/weakmap-polyfill

要求

此库需要 PHP 7.4 或更高版本。

快速入门

$weakMap = new WeakMap();

$a = new stdClass();
$b = new stdClass();

$weakMap[$a] = 123;

var_export(isset($weakMap[$a])); // true
var_export(isset($weakMap[$b])); // false

echo $weakMap[$a]; // 123
echo $weakMap[$b]; // Error

echo count($weakMap); // 1

// removing the last reference to the object will remove it from the WeakMap
unset($a);

echo count($weakMap); // 0

替代方案

weakreference_bc PECL 将原生兼容库 WeakReference 和 WeakMap 迁移到 PHP 7.0-7.4。PECL 有以下优点

  • weakreference_bc 支持 PHP 7.0+
  • 与真实的 WeakMap/WeakReference 一样,值在对象键销毁时立即删除。
  • 原生实现更快,占用更少的内存。
  • weakreference_bcWeakMap::count() 很快,因为它不需要执行垃圾回收。

但有以下缺点

  • 安装 PECL 的步骤比安装 composer 包多。
  • weakreference_bc 目前在 0.4.1 版本中实现了 Iterator 而不是更精确的 IteratorAggregate