官网参考:https://www.php.net/manual/zh/language.generators.overview.php
1. 官网说明
1)(PHP 5 >= 5.5.0, PHP 7, PHP 8)
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
2)一种常见的使用场景:在 foreach 迭代数组的时候,使用它。相比于在foreach中再去创建一个数组来说,会更省内存和节约时间。这是因为:yield 只在需要时才会返回一个值,而不是将整个数据集一直保存在内存中。
3)生成器只能在函数中使用。使用它的目的就是:用更少的内存处理数据
2. 例子
2-1 占用更少的内存
<?php
/**
* 生成器函数
* @param int $max
* @return Generator
*/
function getRange(int $max = 10): Generator
{
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
/**
* 更少的内存,更快的时间
*/
(function () {
foreach (getRange(PHP_INT_MAX) as $range) {
echo "{$range}";
}
})();
2-2 键值对
<?php
/**
* 键值对方式
* @param int $max
* @return Generator
*/
function getRange2(int $max = 10): Generator
{
for ($i = 1; $i <= $max; $i++) {
$value = $i * mt_rand();
yield $i => $value;
}
}
(function () {
foreach (getRange2(10) as $key => $value) {
echo "{$key} => {$value}" . PHP_EOL;
}
});
2-3 生成器传参
<?php
/**
* 生成器传参,发送stop跳出生成器
* @param int $max
* @return Generator
*/
function getRange3(int $max = 10): Generator
{
for ($i = 1; $i <= $max; $i++) {
$injected = yield $i;
if ('stop' === $injected) {
return;
}
}
}
(function () {
$generator = getRange3(100);
foreach ($generator as $range) {
if (50 === $range) {
$generator->send('stop');
}
echo "Dataset {$range}" . PHP_EOL;
}
})();
3. Generator的作用
1)用Generator来实现协程
2)用Generator来处理大量数据集
3.1 开销的比较代码
<?php
/**
* 测试:不使用Generator 和 使用Generator的开销
*/
$n = 10000000;
$startTime = microtime(true);
$startMemory = memory_get_usage();
$array = range(1, $n);
foreach ($array as $a) {
}
echo memory_get_usage() - $startMemory, " bytes\n";
echo microtime(true) - $startTime . " ms\n";
function xrange($start, $end, $step = 1)
{
for ($i = $start; $i < $end; $i += $step) {
yield $i;
}
}
$startTime = microtime(true);
$startMemory = memory_get_usage();
$g = xrange(1, $n);
foreach ($g as $i) {
}
echo memory_get_usage() - $startMemory, " bytes\n";
echo microtime(true) - $startTime . " ms\n";