benmorel / weakmap-polyfill
PHP 7.4 的 WeakMap 兼容库
0.5.0
2024-04-02 16:29 UTC
Requires
- php: ^7.4 || ^8.0
Requires (Dev)
- php-coveralls/php-coveralls: ^2.4
- phpunit/phpunit: ^9.0
README
此兼容库旨在与 PHP 8 中的 WeakMap
100% 兼容。
介绍
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_bc
的WeakMap::count()
很快,因为它不需要执行垃圾回收。
但有以下缺点
- 安装 PECL 的步骤比安装 composer 包多。
weakreference_bc
目前在 0.4.1 版本中实现了Iterator
而不是更精确的IteratorAggregate
。