【问题标题】:PHP prematurely exiting while loopPHP过早退出while循环
【发布时间】:2015-09-14 22:11:21
【问题描述】:

对 PHP 很陌生,我被困在一个(我认为)一个奇怪的问题上。我已经将这个文件拼接在一起,(它被分割成几个不同的函数)进行测试,并且更容易解释这个问题。

这是 Laravel 中的一个基本 while 循环,它似乎过早退出,但奇怪的是没有退出到循环之后的行,而是在循环之前退出然后再次进入。不能为我的生活锻炼为什么。我在整个函数中添加了一些日志事件,以便我可以尝试了解发生了什么。

在第 7 页之前,这可以正确地获取产品并将其写入数据库,然后我在日志中收到“启动 API 帮助程序”事件,但从未收到“结束 API 帮助程序”。因此,在第 7 页的某处某处导致 while 循环退出到上面的行,将 pagecount 重置为 0。然后重新进入循环,获取第一批产品并在写入时抛出 SQL 重复键异常。我知道它正在退出到循环之前,因为我在日志中的“新客户端”之后得到“之前”。当然,页数正在重置。这怎么可能发生?

任何帮助将不胜感激。

public function store()
{
    $items = array();
    Log::info('Before while');
    $pagecount = 0;
    $prodcount = 1;
     while ($prodcount > 0) {
        Log::info('Top of while');
        $prodcount = 0; // Reset product counter
        Log::info('Page Count:'.$pagecount);
        Log::info('Start API Helper');
    $headers = array(
        'NETOAPI_KEY' => env('NETO_API_KEY'),
        'Content-Type' => 'application/json',
        'Accept' => 'application/json',
        'NETOAPI_ACTION' => 'GetItem'
    );
    Log::info('Headers Declared');
    $body = '{
        "Filter": {
            "DateAddedFrom" : "2013-06-01 12:00:00",
            "Page": "'.$pagecount.'",
            "Limit": "500",
            "OutputSelector": [
                **Lots of JSON here - removed for readability**
            ]
        }
    }'; 

    Log::info('Start API Helper');
    $client = new client();
    Log::info('New Client');
    try {
       $res = $client->post(env('NETO_API_URL'), [ 'headers' => $headers , 'body' => $body ]);
    } catch (RequestException $e) {
        echo $e->getRequest();
        Log::error($e->getRequest());
        if ($e->hasResponse()) {
            echo $e->getResponse();
            Log::error($e->getResponse());
        }
    }
    Log::info('End API Helper');
    $items = json_decode(utf8_encode($res->getBody()), true);
        foreach($items["Item"] as $item)
        {
            // JSON is returning an empty array for empty strings. Need to convert to empty string for SQL save.
            foreach($item as $key => $value){
                if (empty($value)) {
                     $item["$key"] = "";
                }
            }
            $Product = new Item;
            $Product->SKU = array_get($item, 'SKU');
            ** LOTS OF DB FIELDS REMOVED FROM HERE FOR READABILITY**
            $Product->save();
            $prodcount++;               
        } 
        $pagecount++;
        Log::info($prodcount.' products written to DB:');
        Log::info('Bottom of while'); 
    };
    Log::info('Exited While'); 
    return 'Complete';
}

更新:我已将 ELoquent SQL 客户端修改为 firstorNew 以避免任何潜在的重复键错误。现在日志在第 7 页(偶尔在第 8 页)显示相同的“while exit”,然后在第 0 页重新进入循环,但最奇怪的是它似乎最终以 2 个循环实例运行。然后最终是 3 个实例,然后是 4 个。然后它抛出一个内存耗尽异常。日志看起来像。

local.INFO: Page Count:1
local.INFO: Page Count:2
local.INFO: Page Count:3
local.INFO: Page Count:4 
local.INFO: Page Count:5 
local.INFO: Page Count:6 
local.INFO: Page Count:7 
local.INFO: Page Count:8
local.INFO: Page Count:0
local.INFO: Page Count:9
local.INFO: Page Count:1
local.INFO: Page Count:10 
local.INFO: Page Count:2
local.INFO: Page Count:11 
local.INFO: Page Count:3
local.INFO: Page Count:12 

...等等...

格式错误的 JSON API 响应是否可能导致此问题?也许我会尝试将分页限制在 25 左右,这样我就可以访问导致循环中断的响应?

【问题讨论】:

    标签: php json laravel-5 guzzle


    【解决方案1】:

    我猜从第 7 页开始,您的处理行数已用完,以下 API 调用以某种方式失败:

    try {
           $res = $client->post(env('NETO_API_URL'), [ 'headers' => $headers , 'body' => $body ]);
        } catch (RequestException $e) {
            echo $e->getRequest();
            Log::error($e->getRequest());
            if ($e->hasResponse()) {
                echo $e->getResponse();
                Log::error($e->getResponse());
            }
        }
    

    至于您的循环如何重新进入很可能是因为您将 store 方法包装在另一个重新尝试的 try catch 中?因为我在函数内部看不到任何表示循环重新启动的 try catch。

    我的建议是将您的方法重写为 do-while 循环并将您的 while 条件修改为:

    while ($prodcount === 500)
    

    这将确保您不会进行任何您知道没有意义的后续 API 调用,并减少代码中出现问题的可能性。

    另一个不错的检查是在您的响应对象上:

    if (!isset($items["Item"])) {
        echo 'Response contains no "Item" value';
        exit;
    }
    

    除此之外,如果不添加更多日志记录和更多 var_dump() / print_r() 来逐步执行您的逻辑,这很难调试。

    祝你好运。

    【讨论】:

    • 嗨,丹。非常感谢您的回复。我已将循环修改为 do-while,这是一个好主意,谢谢!我还添加了空数组循环出口。当然,我仍然被困在循环问题上。在第 7 页之后还有更多产品页面。我已将 Eloquent 修改为 firstOrNew,它将更新或创建数据库记录。这已经解决了重复密钥问题,并将让进程继续运行,然后查看日志以查看是否可以进一步说明问题。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多