PHP过早退出while循环

Posted

技术标签:

【中文标题】PHP过早退出while循环【英文标题】:PHP prematurely exiting while loop 【发布时间】: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 左右,这样我就可以访问导致循环中断的响应?

【问题讨论】:

【参考方案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,它将更新或创建数据库记录。这已经解决了重复密钥问题,并将让进程继续运行,然后查看日志以查看是否可以进一步说明问题。再次感谢。

以上是关于PHP过早退出while循环的主要内容,如果未能解决你的问题,请参考以下文章

PHP流程控制

为啥while循环不退出?

while true 如何退出循环

使用外部事件退出 while 循环

Python流程控制-3 循环控制

没有退出“while”循环,但为啥呢?