PHP创建大型JSON字符串


PHP creating large JSON string

我正在开发一个应用程序,用户应该在其中看到一个带有图表的列表。图表的数据应该从数据库(目前约有785行)中提取,然后进行排序以形成有效的JSON字符串。对不对,我试着像这个一样做

while($row = $res->fetch_assoc()) {
    if(count($appData) == 0 ){
        $appData[] = array(
            "name" => $row["name"],
            "date" => array($row["date"]),
            "android" => array($row["android_count"]),
            "ios" => array($row["apple_count"])
        );
    }else {
        for($i = 0; $i < count($appData); $i++) {
            if($appData[$i]["name"] == $row["name"]){
                $appData[$i]["date"][] = $row["date"];
                $appData[$i]["android"][] = $row["android_count"];
                $appData[$i]["ios"][] = $row["apple_count"];
            }else {
                $appData[] = array(
                    "name" => $row["name"],
                    "date" => array($row["date"]),
                    "android" => array($row["android_count"]),
                    "ios" => array($row["apple_count"])
                );
            }
        }
    }
}
echo json_encode($appData);

当我尝试运行代码时,它会给出一个"致命错误:允许的内存大小为536870912字节已耗尽(试图分配71字节)"的错误。我试着增加允许的最大内存,只是想看看会发生什么,但我得到了同样的结果。

有什么办法可以避免做这么多循环吗?或者我应该用一种完全不同的方式来处理这个问题,如果是,那是哪一种?

最终结果应该看起来像这个

[{"name":"Some name", "date":["2016-05-09", "2016-05-10", "2016-05-11"], "android":["3", "1", "8"], "ios":["4", "7", "5"]},...]

我们将不胜感激!

内存问题在"for"循环中。它可以在每个循环中向$appData添加一堆项,而不是"如果没有匹配的名称,则只添加一个项"。例如,如果$appData中已经有100个项,并且$row['name']与$appData的最后一个项匹配,那么在更新$appData内的最后一项之前,将有99个项添加到$appData。我敢打赌,当前的代码正在生成一个包含785个项目的$appData。

要解决内存问题,请将"for"循环更改为以下内容:

    $matchFound = false;
    for($i = 0; $i < count($appData); $i++) {
        if($appData[$i]["name"] == $row["name"]){
            $appData[$i]["date"][] = $row["date"];
            $appData[$i]["android"][] = $row["android_count"];
            $appData[$i]["ios"][] = $row["apple_count"];
            $matchFound = true;
            break;
        }
    }
    if (!$matchFound) {
        $appData[] = array(
            "name" => $row["name"],
            "date" => array($row["date"]),
            "android" => array($row["android_count"]),
            "ios" => array($row["apple_count"])
        );
    }

在效率方面,使用maximkou建议的关联数组将是一个很大的加速。

问题不在于循环的数量,而在于$appData数组的大小和php配置的memory_limit值。

如果无法缩小传递的数据的大小,则必须增加memory_limit值。但是在增加这个值时要小心,因为它是服务器将执行的每个正在运行的php脚本的值。我建议在每个循环中对输出缓冲区进行分页或发送。

如果您需要代码示例,只需索取即可。

分页意味着您的javascript页面将调用PHP脚本X次,每次检索N行,直到PHP脚本不再允许为止。因此,您必须返回一个数组,例如:

return array(
    'nextPage' => 2, // More data available on this page
    'data' => $json
);
// Or
return array(
    'nextPage' => null, // No more data available
    'data' => $json
);

或者发送到每个循环上的输出缓冲区并释放内存:

$first = true;
echo '[';
while($row = $res->fetch_assoc()) {
    if(!$first) {
        echo ',';
    } else {
        $first = false;
    }
    // some logic
    $row_data = array(...);
    echo json_encode($row_data);
}
echo ']';

这样就不会在php的变量中堆叠所有数据。

通过$row['name']对数组进行索引。这种强大的功能简化了您的代码。php中的数组分配了许多内存,因此按行对嵌套数据进行编码。如果您知道结果数组的大小,也可以尝试使用SplFixedArray。

试试这个:

while($row = $res->fetch_assoc()) {
    $appData[ $row["name"] ] = json_encode(array(
        "name" => $row["name"],
        "date" => array($row["date"]),
        "android" => array($row["android_count"]),
        "ios" => array($row["apple_count"])
    ));
}
echo "[".implode(',', $appData)."]";

这应该会创建完全相同的结果(tho尚未测试),并使用映射数组和array_key_exists()来避免额外的循环。这是在一个循环中完成的。

$nameMap = array(); // hold name and keys
while($row = $res->fetch_assoc()){
    $key = array_key_exists($row['name'], $nameMap) ? $nameMap[$row['name']] : count($appData);
    $nameMap[$row['name']] = $key;
    if(empty($appData[$key])) 
        $appData[$key] = array("name"=>$row['name'], "date"=>array(), "android"=>array(), "ios"=>array());
    $appData[$key]['date'][] = $row['date'];
    $appData[$key]['android'][] = $row['android'];
    $appData[$key]['ios'][] = $row['ios'];
}