【发布时间】:2018-12-25 16:17:58
【问题描述】:
在 PHP 7.2 中,each 已弃用。 The documentation 说:
警告 自 PHP 7.2.0 起,该功能已被弃用。强烈建议不要依赖此函数。
我正在调整电子商务应用程序并将所有while-each 循环转换为(假定)等效的foreach。
如下所示,我已经将所有 reset 和 while 循环替换为等效的 foreach。
它大部分工作正常。但是,我们有一个客户,她的购物车中有很长的商品清单,试图结账并抱怨她从服务器收到错误 502。
我试图重现这一点,发现只有她的购物车失败,结帐页面加载需要 2 多分钟,然后是 502。
然后我开始调试我最近修改的很多文件,反复试验,直到我发现问题出在这个特定的文件和特定的功能上。
每当我将第一个foreach 循环切换回while 循环时,客户可以在不到一秒的时间内加载结帐页面。切换回foreach - 又需要几分钟,但 php 在结束执行之前会超时。
我确实对foreach 与while 循环(例如var_dump $products_id 和$this->contents)的输出进行了测试,它们看起来都相同。我已经重写了代码以使其顺利运行并保持 PHP 7.2 兼容,但我仍然无法弄清楚为什么会发生这种情况。
这是完整的功能:
function get_content_type() {
$this->content_type = false;
if ( (DOWNLOAD_ENABLED == 'true') && ($this->count_contents() > 0) ) {
// reset($this->contents);
// while (list($products_id, ) = each($this->contents)) {
foreach(array_keys($this->contents) as $products_id) {
if (isset($this->contents[$products_id]['attributes'])) {
// reset($this->contents[$products_id]['attributes']);
// while (list(, $value) = each($this->contents[$products_id]['attributes'])) {
foreach ($this->contents[$products_id]['attributes'] as $value) {
$virtual_check_query = tep_db_query("select count(*) as total from " . TABLE_PRODUCTS_ATTRIBUTES . " pa, " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad where pa.products_id = '" . (int)$products_id . "' and pa.options_values_id = '" . (int)$value . "' and pa.products_attributes_id = pad.products_attributes_id");
$virtual_check = tep_db_fetch_array($virtual_check_query);
if ($virtual_check['total'] > 0) {
switch ($this->content_type) {
case 'physical':
$this->content_type = 'mixed';
return $this->content_type;
break;
default:
$this->content_type = 'virtual';
break;
}
} else {
switch ($this->content_type) {
case 'virtual':
$this->content_type = 'mixed';
return $this->content_type;
break;
default:
$this->content_type = 'physical';
break;
}
}
}
} elseif ($this->show_weight() == 0) {
// reset($this->contents);
// while (list($products_id, ) = each($this->contents)) {
foreach (array_keys($this->contents) as $products_id) {
$virtual_check_query = tep_db_query("select products_weight from " . TABLE_PRODUCTS . " where products_id = '" . $products_id . "'");
$virtual_check = tep_db_fetch_array($virtual_check_query);
if ($virtual_check['products_weight'] == 0) {
switch ($this->content_type) {
case 'physical':
$this->content_type = 'mixed';
return $this->content_type;
break;
default:
$this->content_type = 'virtual';
break;
}
} else {
switch ($this->content_type) {
case 'virtual':
$this->content_type = 'mixed';
return $this->content_type;
break;
default:
$this->content_type = 'physical';
break;
}
}
}
} else {
switch ($this->content_type) {
case 'virtual':
$this->content_type = 'mixed';
return $this->content_type;
break;
default:
$this->content_type = 'physical';
break;
}
}
}
} else {
$this->content_type = 'physical';
}
return $this->content_type;
}
谢谢
编辑: 这里是数组: https://pastebin.com/VawX3XpW
该问题已在我尝试过的所有配置上进行了测试和重现:
1) 高端 windows 10 pc + WAMP (Apache 2.4 + MariaDB 10.2 + PHP 5.6+/7+/7.1+/7.2+)
2) 高端 CentOS/cPanel 服务器 + Litespeed + MariaDB 10.1 + PHP 5.6+
强调一下,我不打算重写代码或模仿each 然后重写代码,因为我们不会从中学到很多东西。我只是想找到一个合乎逻辑的解释或解决/调试这个谜团的方法。也许有人在某个地方遇到过这样的问题,并且可以对此有所了解。
2018 年 8 月 1 日更新
我已经尝试调试了好几天,最终发现了一些有趣的东西。我在第一个foreach 循环和while 循环上添加了“回声点”和exit,如下所示:
function get_content_type() {
$this->content_type = false;
if ( (DOWNLOAD_ENABLED == 'true') && ($this->count_contents() > 0) ) {
// reset($this->contents);
// while (list($products_id, ) = each($this->contents)) { echo '1 ';
foreach(array_keys($this->contents) as $products_id) { echo '1 ';
if (isset($this->contents[$products_id]['attributes'])) { echo '2 ';
// reset($this->contents[$products_id]['attributes']);
// while (list(, $value) = each($this->contents[$products_id]['attributes'])) {
foreach ($this->contents[$products_id]['attributes'] as $value) { echo '3 ';
$virtual_check_query = tep_db_query("select count(*) as total from " . TABLE_PRODUCTS_ATTRIBUTES . " pa, " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad where pa.products_id = '" . (int)$products_id . "' and pa.options_values_id = '" . (int)$value . "' and pa.products_attributes_id = pad.products_attributes_id");
$virtual_check = tep_db_fetch_array($virtual_check_query);
if ($virtual_check['total'] > 0) {
switch ($this->content_type) {
case 'physical':
$this->content_type = 'mixed'; echo '4 ';
return $this->content_type;
break;
default:
$this->content_type = 'virtual'; echo '5 ';
break;
}
} else {
switch ($this->content_type) {
case 'virtual':
$this->content_type = 'mixed'; echo '6 ';
return $this->content_type;
break;
default:
$this->content_type = 'physical'; echo '7 ';
break;
}
}
}
} elseif ($this->show_weight() == 0) {
// reset($this->contents);
// while (list($products_id, ) = each($this->contents)) {
foreach (array_keys($this->contents) as $products_id) {
$virtual_check_query = tep_db_query("select products_weight from " . TABLE_PRODUCTS . " where products_id = '" . $products_id . "'");
$virtual_check = tep_db_fetch_array($virtual_check_query);
if ($virtual_check['products_weight'] == 0) {
switch ($this->content_type) {
case 'physical':
$this->content_type = 'mixed'; echo '8 ';
return $this->content_type;
break;
default:
$this->content_type = 'virtual'; echo '9 ';
break;
}
} else {
switch ($this->content_type) {
case 'virtual':
$this->content_type = 'mixed'; echo '10 ';
return $this->content_type;
break;
default:
$this->content_type = 'physical'; echo '11 ';
break;
}
}
}
} else {
switch ($this->content_type) {
case 'virtual':
$this->content_type = 'mixed'; echo '12 ';
return $this->content_type;
break;
default:
$this->content_type = 'physical'; echo '13 ';
break;
}
}
} exit; //Exiting from the loop to check output
} else {
$this->content_type = 'physical';
}
return $this->content_type;
}
当我使用while 运行循环时,我得到的输出只是“1 13”一次,这意味着循环只运行一次并停止。
但是,当我将其更改为foreach 时,我得到了一长串“1 13 1 13 1 13...”,这意味着它循环了很多次。我已经去进一步调查breaks 的while 循环和foreach 循环之间是否有任何区别,但我仍然找不到任何支持信息。然后我将最后一个break; 重写为break 2; 并再次测试foreach,这一次它似乎只运行了一次,就像它是带有while 循环的break;(不是@ 987654353@)
编辑: 澄清一下 - while breaks 和 foreach breaks 之间没有区别。它们的工作方式相同。
更新 #2:
我已将} elseif ($this->show_weight() == 0) { 修改为} elseif (2 == 0) {,while 循环现在运行的次数与foreach 循环一样多。
var_dump($this->show_weight()); 结果float 4466.54。
这个问题对我来说仍然没有任何意义。
再次感谢
【问题讨论】:
-
当结帐页面加载需要 2 分钟时,视觉结果是什么?是正常的,还是有很多重复的项目,或者它可能被截断(表明某种无限循环)?
-
@rlanvin 首先,由于内存耗尽,它在大约 45 秒后崩溃。当我增加该值时,由于 120 秒的限制,它超时,然后我增加了更多,最终它在很长一段时间后加载,页面上的细节似乎正常。
-
你试过 xdebug 或 microtime 吗?
-
是的,WAMP 服务器开启了 xdebug。试过什么?
-
foreach实际上是在大多数情况下循环遍历数组的一种非常慢的方法,而for与预先计算的count相比。见phpbench.com。将foreach循环重写为for循环可能会显着提高性能,但显然您应该对此进行测试。
标签: php performance foreach while-loop each