Nik*_*a 웃 12 php each performance foreach while-loop
使用PHP 7.2,each不推荐使用.文件说:
警告此功能自PHP 7.2.0起已废弃.非常不鼓励依赖此功能.
我正在努力调整电子商务应用程序并将所有while-each循环转换为(假设)等效foreach.
正如您在下面看到的,我已经用等效的替换了所有的reset&while循环foreach.
它工作得很好.但是,我们有一个客户在她的购物车中有很长的商品列表,试图结帐并抱怨她从服务器收到错误502.我试图重现它,发现只有她的购物车失败,加载结帐页面需要2分钟,然后加载502次.然后我开始调试我最近修改过的很多文件,试用和错误,直到我发现问题出在这个特定文件和特定功能上.每当我将第一个foreach循环切换回while循环时,客户可以在不到一秒的时间内加载结帐页面.切换回foreach- 再次需要几分钟,但PHP在结束执行之前超时.
我做的是,输出执行过程中的测试foreachVS的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;
}
Run Code Online (Sandbox Code Playgroud)
谢谢
编辑:这是阵列:https: //pastebin.com/VawX3XpW
该问题已在我尝试的所有配置上进行测试和复制:
1)高端窗口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然后重写代码,因为我们不会从中学到很多东西.我只是想找到一个合乎逻辑的解释或解决/调试这个谜的方法.也许有人在某个地方碰到了这样一个问题,并且可以对此有所了解.
更新01/Aug/2018
我一直试图调试这几天,最终发现了一些有趣的东西.我添加了"回声点"并exit在第一个foreach循环和while循环上添加如下:
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;
}
Run Code Online (Sandbox Code Playgroud)
当我运行循环时while,我得到的输出只是"1 13"一次,这意味着循环只运行一次并停止.然而,当我把它更改为时foreach,我得到了一个很长的列表"1 13 1 13 1 13 ...",这意味着它循环多次.我去了进一步调查,如果有什么区别breaks的while循环和foreach循环,我还是没能找到任何证明资料.然后我重写了最后break;是break 2;和测试foreach一次,这一次它似乎已经只是一次运行,就像当它是一个while用循环break;(不break 2;)
编辑:只是为了澄清-有,而没有任何区别breakS和的foreach break秒.他们以同样的方式工作.
更新#2:
我已修改} elseif ($this->show_weight() == 0) {为} elseif (2 == 0) {,while循环现在运行循环次数foreach.
var_dump($this->show_weight());结果float 4466.54.这个问题对我来说仍然没有任何意义.
再次感谢
rla*_*vin 15
这实际上是一个非常简单的算法问题,它与以下事实有关show_weight():0您何时循环同一个数组(编辑:并根据您的注释,show_weight()本身也循环同一个数组).
TL; DR随着while所有这些袢共享相同的内部指针和影响对方.随着foreach每个回路是独立的,因此其运行方式更多的迭代,因此性能问题.
由于一个例子胜过千言万语,希望以下代码能让事情变得更加清晰:
<?php
$array = ['foo','bar','baz'];
foreach ( array_keys($array) as $key ) {
echo $array[$key],"\n";
foreach ( array_keys($array) as $key ) {
echo "\t",$array[$key],"\n";
}
}
echo "---------------\n";
while ( list($key,) = each($array) ) {
echo $array[$key],"\n";
reset($array);
while ( list($key,) = each($array) ) {
echo "\t",$array[$key],"\n";
}
}
Run Code Online (Sandbox Code Playgroud)
这将输出:
foo
foo
bar
baz
bar
foo
bar
baz
baz
foo
bar
baz
---------------
foo
foo
bar
baz
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,对于大小为3的数组,foreach需要3次迭代,而while只需3次.这就是您的性能问题.
while更快?因为在第二个(内部)的末尾while,内部指针$array将指向数组的末尾,因此第一个(外部)while将简单地停止.
使用a foreach,因为您正在使用2个不同调用的结果array_keys,所以使用2个不共享相同内部指针的不同数组,因此没有理由循环停止.一个简单return的第二个(内部)foreach应该解决问题.