如何更新使用不推荐的 each()函数的代码?

在 PHP 7.2中,不推荐使用 each文件表示:

警告 从 PHP7.2.0开始,这个函数就已经被禁止使用了。我们强烈建议不要依赖这个函数。

如何更新代码以避免使用它? 下面是一些例子:

  1. $ar = $o->me;
    reset($ar);
    list($typ, $val) = each($ar);
    
  2. $out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
    $expected = each($out);
    
  3. for(reset($broken);$kv = each($broken);) {...}
    
  4. list(, $this->result) = each($this->cache_data);
    
  5. // iterating to the end of an array or a limit > the length of the array
    $i = 0;
    reset($array);
    while( (list($id, $item) = each($array)) || $i < 30 ) {
    // code
    $i++;
    }
    

当我在 PHP 7.2上执行代码时,会收到以下错误:

已弃用: each ()函数已弃用。此消息将在进一步调用时被禁用

114414 次浏览

you could create your own each() function using key(), current() and next(). then replace your calls with that function, like this:

<?php
function myEach(&$arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}

1.

$ar = $o->me;
reset($ar);
list($typ, $val) = myEach($ar);

2.

$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = myEach($out);

3.

for(reset($broken);$kv = myEach($broken);) {...}
  1. For your first two example cases, you could use key() and current() to assign the values you need.

    $ar = $o->me;   // reset isn't necessary, since you just created the array
    $typ = key($ar);
    $val = current($ar);
    
  2. $out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
    $expected = [key($out), current($out)];
    

    In those cases, you can use next() to advance the cursor afterward, but it may not be necessary if the rest of your code doesn't depend on that.

  3. For the third case, I'd suggest just using a foreach() loop instead and assigning $kv inside the loop.

    foreach ($broken as $k => $v) {
    $kv = [$k, $v];
    }
    
  4. For the fourth case, it looks like the key is disregarded in list(), so you can assign the current value.

    $this->result = current($this->cache_data);
    

    Like the first two cases, it may be necessary to advance the cursor with next() depending on how the rest of your code interacts with $this->cache_data.

  5. Fifth can be replaced with a for() loop.

    reset($array);
    for ($i = 0; $i < 30; $i++) {
    $id = key($array);
    $item = current($array);
    // code
    next($array);
    }
    

Here are some ways to do it:

The standard foreach loop (very readable):

foreach($this->contents as list($products_id)) {
$total_items += $this->get_quantity($products_id);
}

Or, reducing:

$total_items = array_reduce($this->contents, function($acc, $item) {
return $acc + $this->get_quantity($products_id[0]);
});

Or, in a functional expression:

$total_items = array_sum(array_map([$this, 'get_quantity'],
array_column($this->contents, 0)));

None of these methods need reset($this->contents); preceding it.

I found a way to fix it and thought to share the information. Here are also other cases about how to upgrade each() loops to foreach().

Case 1: Missing $value

reset($array);
while (list($key, ) = each($array)) {

Update to:

foreach(array_keys($array) as $key) {

Case 2: Missing $key

reset($array);
while (list(, $value) = each($array)) {

Update to:

foreach($array as $value) {

Case 3: Not missing anything

reset($array);
while (list($key, $value) = each($array)) {

Update to:

foreach($array as $key => $value) {
reset($array);
while (list($key, $value) = each($array)) {

UPDATE

reset($array);
foreach($array as $key => $value) {

The way you most definitely shouldn't do is put the function "back into php" by adding it to the auto_prepend_file setting in php.ini

auto_prepend_file = "/var/www/php/auto_prepend.php"

Then make the file and enter in the function with an function_exists wrapper.

<?php
/**
* Adds the depreciated each() function back into 7.2
*/
if (!function_exists('each')) {
function each($arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
}

This essentially declares the function before your php application runs. When your application tries to run the each function it'll use your version instead.

This is absolutely not the way you should be approaching this problem, especially in production! However you're a developer with time constraints and you just want to try arbitrary frameworks for your next project and they haven't been updated to work on your local development server without winding back your php version.

When you've committed to a code base for your project please go ahead and implement the changes in the accepted answer because they do work.

I used Wee Zel's emulation of the each function

What about using this function?

function array_fetch(array $a) {
$element = current($a);
next($a);
return $element;
}

To expand on Petro Mäntylä excellent correct answer for Case 3:

Here is a full example of a "Case 3" situation, because I find full examples far more informative that one line code fragments:

This is genuine code from a 3rd party old code base (TCPDF)

DEPRECATED:

while (list($id, $name) = each($attr_array)) {
$dom[$key]['attribute'][$name] = $attr_array[$id];
...
...
}

FIXED:

 // while (list($id, $name) = each($attr_array)) {
foreach($attr_array as $feKey => $feRow){
// $dom[$key]['attribute'][$name] = $attr_array[$id];
$dom[$key]['attribute'][$feRow] = $attr_array[$feKey];
...
...
}
unset($feKey,$feRow);

2019+ Instant Upgrade of each()

There are actually plenty of cases that each() can be replaced, that's why there are so many different upvoted answers in this question.

-while (list($key, $callback) = each($callbacks)) {
+foreach ($callbacks as $key => $callback) {
// ...
}

And:

-while (list($key) = each($callbacks)) {
+foreach (array_keys($callbacks) as $key) {
// ...
}

You can replace one by one manually. But isn't there a better way?

I help to migrate projects, where are over 150+ cases like this. I'm lazy so I made a tool called Rector, that converts the code the way above (+ there are more cases, but I don't want to spam the answer).

It's part of the PHP_72 set.


4 Steps to Upgrade your Code

1. Install it

composer require rector/rector --dev

2. Create rector.php config

vendor/bin/rector init

3. Add PHP_72 set

<?php


use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;


return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::SETS, [
Setlist::PHP_72,
]);
};

4. Run it on your code

vendor/bin/rector process src --set php72

I hope it helps you with your migration.


If there is some bug or anomaly, it's Rector missed case. Create an issue, so we can fix it and make it work for every case possible.

Replace this code

while (list($_key,$_resourceTypeNode) = each($GLOBALS['config']['ResourceType'])) {
//             if ($_resourceTypeNode['name'] === $resourceTypeName) {
//                 $this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);


//                 return $this->_resourceTypeConfigCache[$resourceTypeName];
//             }
//         }

with this one

foreach ($GLOBALS['config']['ResourceType'] as $key => $_resourceTypeNode) {
if (isset($_resourceTypeNode['name'])) {
if ($_resourceTypeNode['name'] === $resourceTypeName) {
$this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
                    

return $this->_resourceTypeConfigCache[$resourceTypeName];
}
}
}
 //  while (list($products_id, ) = each($this->contents)) {
//  $total_items += $this->get_quantity($products_id);
// }

Update To :

foreach(array_keys($this->contents) as $products_id) {
$total_items += $this->get_quantity($products_id);
}

Other Condition:

foreach($this->contents as $key =>$value) {
$total_items += $this->get_quantity($products_id);
}

For all commentators. Function foreach not work with dinamic arrays with changing of elements count. I think using custom function "each" John Tilley - single right way for dinamic arrays. For static arrays not use "each" nobody. "Foreach" know all.