官网参考: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 占用更少的内存

  1. <?php
  2. /**
  3. * 生成器函数
  4. * @param int $max
  5. * @return Generator
  6. */
  7. function getRange(int $max = 10): Generator
  8. {
  9. for ($i = 1; $i <= $max; $i++) {
  10. yield $i;
  11. }
  12. }
  13. /**
  14. * 更少的内存,更快的时间
  15. */
  16. (function () {
  17. foreach (getRange(PHP_INT_MAX) as $range) {
  18. echo "{$range}";
  19. }
  20. })();

2-2 键值对

  1. <?php
  2. /**
  3. * 键值对方式
  4. * @param int $max
  5. * @return Generator
  6. */
  7. function getRange2(int $max = 10): Generator
  8. {
  9. for ($i = 1; $i <= $max; $i++) {
  10. $value = $i * mt_rand();
  11. yield $i => $value;
  12. }
  13. }
  14. (function () {
  15. foreach (getRange2(10) as $key => $value) {
  16. echo "{$key} => {$value}" . PHP_EOL;
  17. }
  18. });

2-3 生成器传参

  1. <?php
  2. /**
  3. * 生成器传参,发送stop跳出生成器
  4. * @param int $max
  5. * @return Generator
  6. */
  7. function getRange3(int $max = 10): Generator
  8. {
  9. for ($i = 1; $i <= $max; $i++) {
  10. $injected = yield $i;
  11. if ('stop' === $injected) {
  12. return;
  13. }
  14. }
  15. }
  16. (function () {
  17. $generator = getRange3(100);
  18. foreach ($generator as $range) {
  19. if (50 === $range) {
  20. $generator->send('stop');
  21. }
  22. echo "Dataset {$range}" . PHP_EOL;
  23. }
  24. })();

3. Generator的作用

1)用Generator来实现协程

2)用Generator来处理大量数据集

3.1 开销的比较代码

  1. <?php
  2. /**
  3. * 测试:不使用Generator 和 使用Generator的开销
  4. */
  5. $n = 10000000;
  6. $startTime = microtime(true);
  7. $startMemory = memory_get_usage();
  8. $array = range(1, $n);
  9. foreach ($array as $a) {
  10. }
  11. echo memory_get_usage() - $startMemory, " bytes\n";
  12. echo microtime(true) - $startTime . " ms\n";
  13. function xrange($start, $end, $step = 1)
  14. {
  15. for ($i = $start; $i < $end; $i += $step) {
  16. yield $i;
  17. }
  18. }
  19. $startTime = microtime(true);
  20. $startMemory = memory_get_usage();
  21. $g = xrange(1, $n);
  22. foreach ($g as $i) {
  23. }
  24. echo memory_get_usage() - $startMemory, " bytes\n";
  25. echo microtime(true) - $startTime . " ms\n";