PHP - json_encode生成器对象(使用yield)

时间:2016-04-06 09:01:23

标签: php json generator yield

我有一个非常大的PHP(5.6)数组,动态生成,我想转换为JSON。问题是数组太大而不适合内存 - 当我尝试处理它(耗尽的内存)时会出现致命的错误。所以我发现,使用生成器,内存问题就会消失。

这是我到目前为止尝试过的代码(这个简化示例显然没有产生内存错误):

<?php 
function arrayGenerator()// new way using generators
{
    for ($i = 0; $i < 100; $i++) {
        yield $i;
    }
}

function getArray()// old way, generating and returning the full array
{
    $array = [];
    for ($i = 0; $i < 100; $i++) {
        $array[] = $i;
    }
    return $array;
}

$object = [
    'id' => 'foo',
    'type' => 'blah',
    'data' => getArray(),
    'gen'  => arrayGenerator(),
];

echo json_encode($object);

但PHP似乎没有JSON编码来自生成器的值。这是我从previuos脚本得到的输出:

{
    "id": "foo",
    "type": "blah",
    "data": [// old way - OK
        0,
        1,
        2,
        3,
        //...
    ],
    "gen": {}// using generator - empty object!
}

在调用json_encode之前,是否可以对生成器生成的数组进行JSON编码而不生成完整序列?

2 个答案:

答案 0 :(得分:6)

不幸的是,json_encode无法从生成器函数生成结果。使用iterator_to_array仍然会尝试创建整个数组,这仍然会导致内存问题。

您需要创建将从生成器函数生成json字符串的函数。这是一个如何看起来的例子:

function json_encode_generator(callable $generator) {
    $result = '[';

    foreach ($generator as $value) {
        $result .= json_encode($value) . ',';
    }

    return trim($result, ',') . ']';
}

它不是一次编码整个数组,而是一次只编码一个对象,并将结果连接成一个字符串。

上面的例子只负责编码数组,但可以很容易地扩展为递归编码整个对象。

如果创建的字符串仍然太大而无法容纳在内存中,那么您唯一剩下的选项就是直接使用输出流。这是看起来的样子:

function json_encode_generator(callable $generator, $outputStream) {
    fwrite($outputStream, '[');

    foreach ($generator as $key => $value) {
        if ($key != 0) {
            fwrite($outputStream, ','); 
        }

        fwrite($outputStream, json_encode($value));
    }

    fwrite($outputStream, ']');
}

正如您所看到的,唯一的区别是我们现在使用fwrite来写入传入的流而不是连接字符串,我们还需要以不同的方式处理尾随逗号。 / p>

答案 1 :(得分:1)

什么是发电机功能?

生成器函数实际上是编写Iterator的更紧凑和有效的方法。它允许您定义一个计算并返回的函数,而您是looping over it

同样根据http://php.net/manual/en/language.generators.overview.php

的文件
  

生成器提供了一种简单的方法来实现简单的迭代器,而无需实现实现Iterator接口的类的开销或复杂性。

     

生成器允许您编写使用foreach迭代一组数据的代码,而无需在内存中构建数组,这可能会导致超出内存限制,或者需要相当长的处理时间来生成。相反,您可以编写一个生成器函数,它与普通函数相同,除了不是返回一次,生成器可以生成所需数量的次数,以便提供要迭代的值。

什么是yield

yield关键字returns data from a generator function:

  

生成器函数的核心是yield关键字。在最简单的形式中,yield语句看起来很像一个return语句,除了不是停止执行函数并返回,而是为代码提供循环生成器的代码值并暂停生成器函数的执行。

因此,在您生成预期输出的情况下,您需要使用arrayGenerator()循环或foreach迭代iterator函数的输出,然后将其处理为json(由@apokryfos建议)< / p>