PHP 生成器详解
生成器是 PHP 中一种强大的迭代器,它允许你用更少的代码和更少的内存来处理大量数据。

基本概念
生成器允许你在循环中迭代数据,而不必提前创建整个数组,它使用 yield 关键字来返回值,而不是 return。
基础语法
<?php
// 基本生成器示例
function generateNumbers($start, $end) {
for ($i = $start; $i <= $end; $i++) {
yield $i;
}
}
// 使用生成器
foreach (generateNumbers(1, 1000000) as $number) {
if ($number > 5) break;
echo $number . "\n";
}
键值对生成
<?php
// 生成键值对
function generateUser() {
yield 'name' => 'John';
yield 'email' => 'john@example.com';
yield 'age' => 30;
}
foreach (generateUser() as $key => $value) {
echo "$key: $value\n";
}
从生成器返回值
<?php
function generateData() {
yield '第一项';
yield '第二项';
return '生成器结束';
}
$generator = generateData();
foreach ($generator as $item) {
echo $item . "\n";
}
// 获取返回值
echo "返回值: " . $generator->getReturn() . "\n";
生成键值对处理数据库数据
<?php
// 模拟数据库查询结果
function getUsersFromDB() {
// 假设这是数据库查询结果
$users = [
['id' => 1, 'name' => '张三'],
['id' => 2, 'name' => '李四'],
['id' => 3, 'name' => '王五'],
];
foreach ($users as $user) {
yield $user;
}
}
// 不占用大量内存的处理
foreach (getUsersFromDB() as $user) {
echo "用户ID: " . $user['id'] . ", 名字: " . $user['name'] . "\n";
}
实用示例:处理大文件
<?php
// 逐行读取大文件
function readLargeFile($filePath) {
$file = fopen($filePath, 'r');
if (!$file) {
throw new Exception("无法打开文件");
}
while (($line = fgets($file)) !== false) {
yield trim($line);
}
fclose($file);
}
// 使用:处理超大文件而不占内存
try {
foreach (readLargeFile('/path/to/large/file.txt') as $line) {
// 处理每一行数据
echo $line . "\n";
}
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
生成器委托
<?php
// 生成器委托功能 (PHP 7+)
function subGenerator() {
yield '子生成器1';
yield '子生成器2';
}
function mainGenerator() {
yield '主生成器1';
// 委托给子生成器
yield from subGenerator();
yield '主生成器2';
}
foreach (mainGenerator() as $value) {
echo $value . "\n";
}
使用生成器创建无限序列
<?php
// 无限斐波那契数列
function fibonacci() {
$a = 0;
$b = 1;
while (true) {
yield $a;
$newB = $a + $b;
$a = $b;
$b = $newB;
}
}
$fib = fibonacci();
for ($i = 0; $i < 10; $i++) {
echo $fib->current() . " ";
$fib->next();
}
内存效率对比
<?php
// 传统方式(占用大量内存)
function getDataFromFile() {
$data = [];
$file = fopen('data.txt', 'r');
while (($line = fgets($file)) !== false) {
$data[] = trim($line);
}
return $data;
}
// 生成器方式(高效内存使用)
function getDataFromFileGenerator() {
$file = fopen('data.txt', 'r');
while (($line = fgets($file)) !== false) {
yield trim($line);
}
}
// 对比内存使用
$memory1 = memory_get_peak_usage();
$data = getDataFromFile();
$memory2 = memory_get_peak_usage();
echo "传统方式内存: " . ($memory2 - $memory1) . " bytes\n";
$memory3 = memory_get_peak_usage();
foreach (getDataFromFileGenerator() as $line) {
// 处理数据
}
$memory4 = memory_get_peak_usage();
echo "生成器方式内存: " . ($memory4 - $memory3) . " bytes\n";
注意事项
- 生成器只能单向迭代:不能回退或跳转
- 不能生成字符串:只能用于循环结构
- 不能被克隆:生成器对象不能使用 clone 关键字
- 需要 PHP 5.5+:至少 PHP 5.5 版本才支持生成器
最佳实践
<?php
// 最佳实践示例:数据处理管道
function readCSV($filePath) {
$file = fopen($filePath, 'r');
while (($row = fgetcsv($file)) !== false) {
yield $row;
}
fclose($file);
}
function filterData($generator, $callback) {
foreach ($generator as $key => $value) {
if ($callback($value)) {
yield $key => $value;
}
}
}
function transformData($generator, $callback) {
foreach ($generator as $key => $value) {
yield $key => $callback($value);
}
}
// 链式处理
$data = readCSV('data.csv');
$filtered = filterData($data, function($row) {
return $row[2] > 100;
});
$transformed = transformData($filtered, function($row) {
$row[3] = $row[0] + $row[1];
return $row;
});
foreach ($transformed as $row) {
print_r($row);
}
生成器是处理大数据集的强大工具,合理使用可以显著提高代码效率和内存使用率。