How can I update code that uses the deprecated each() function?

104,310

Solution 1

  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);
    }
    

Solution 2

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.

Solution 3

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) {

Solution 4

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);) {...}

Solution 5

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

UPDATE

reset($array);
foreach($array as $key => $value) {
Share:
104,310
yokogeri
Author by

yokogeri

Updated on January 22, 2022

Comments

  • yokogeri
    yokogeri over 2 years

    With PHP 7.2, each is deprecated. The documentation says:

    Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.

    How can I update my code to avoid using it? Here are some examples:

    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++;
      }
      

    When I execute the code on PHP 7.2 I receive the following error:

    Deprecated: The each() function is deprecated. This message will be suppressed on further calls

    • Admin
      Admin over 6 years
      doable with a foreach()
    • cchoe1
      cchoe1 over 6 years
      array_map() with a closure would also work.
    • IncredibleHat
      IncredibleHat over 4 years
      This looks like stuff from the xmlrpc library. Something I'm having to go fix as well lol.
    • Handsome Nerd
      Handsome Nerd about 4 years
      Yea, it's deprecated for no compelling reason and just made people change their code and get nothing in turn. Another frustration in PHP world. wiki.php.net/rfc/deprecations_php_7_2
  • Don't Panic
    Don't Panic almost 7 years
    If you want to fully emulate each, I guess you'd need the "key" and "value" keys in the output as well as 0 and 1.
  • Wee Zel
    Wee Zel almost 7 years
    @Don'tPanic, edited answer, this situation didn't need it but there could be cases out there that might. thanks for suggestion
  • jpschroeder
    jpschroeder over 5 years
    Important to note that these are not equivalent, although in most cases a foreach will suffice – if you modify $array in the while loop it will iterate over the modified values. foreach creates a copy of the list and iterates over it, so mutations to $array will not change the loop.
  • Metal3d
    Metal3d over 5 years
    For 4., I think it's right to replace list($a, $b) = each($arr) by list($a, $b) = array(key($arr), current($arr)); next($arr); isn't it ?
  • Don't Panic
    Don't Panic over 5 years
    @Metal3d yes, that should be equivalent. Although personally, I wouldn't use list, I would just assign to $a and $b directly with key() and current(). I know it's one more line of code, but it seems more straightforward than creating an array just to take the values back out with list(). Just my opinion, though. :-)
  • Don't Panic
    Don't Panic over 5 years
    @jpschroeder good point, that's true. Also, with foreach, reset is not necessary.
  • FrancescoMM
    FrancescoMM over 4 years
    The reset is mostly usless before foreach.
  • Nils
    Nils over 4 years
    The last example using key() and current() is correct value-wise but disregards the fact that each() also advances the array cursor as a side-effect. Also, you probably mean $val and not $callback in the list() call. A proper replacement would be: -list($key, $val) = each($callbacks); +$key = key($opt->option); +$val = current($opt->option); +next($callbacks);
  • Tomas Votruba
    Tomas Votruba over 4 years
    Could you create an issue for this so it's fixed? github.com/rectorphp/rector/issues
  • Nils
    Nils over 4 years
    I'm not using that library, I was just googling for an each() replacement, came across your post here and found it useful but just thought I'd point out that small omission so you could correct your post.
  • Tomas Votruba
    Tomas Votruba over 4 years
    I see. Still always better address that in Github repo issue. Rarely maintainers visit their old responses and the bug usually hits more people
  • Tomas Votruba
    Tomas Votruba over 4 years
    See universal automated migration version bellow: stackoverflow.com/a/55514591/1348344
  • Tomas Votruba
    Tomas Votruba about 4 years
    @Nils I've updated the example. It's very hard to read from the inlined code as text comment, gist.github.com woudl be better. Could you check?
  • Nils
    Nils about 4 years
    You added next() which takes care of advancing the pointer but still need to use the same variable names throughout ($val vs $callback) if the examples should be equivalent. See 3v4l.org/DWYMt for full code examples and tests proving the results.
  • Tomas Votruba
    Tomas Votruba about 4 years
    I get lost in so many lines of code. Could you give me only the content to past there?
  • Nils
    Nils about 4 years
    I've given you the complete answer twice already. If you're not interested in spending the one minute it takes to fully understand where your answer goes wrong, I leave it up to you. If you just re-read your solution it's pretty obvious since you do not even use the same variable names...
  • Tomas Votruba
    Tomas Votruba about 4 years
    Sorry, it's just not clear to me and this is just example answer. Any code could be there. The point is all the changes should be automated, exactly for this confusion. I'll drop the snippet, no to lead anyone astrey.
  • charitha
    charitha about 4 years
    For case 1 I believe you need to make sure internal pointer is advanced after calling current() since it doesn't move the pointer.
  • rubo77
    rubo77 almost 4 years
    The replacement function runs into an endless loop in my case. probably because it doesent take into account reset() and next()
  • Martin Zvarík
    Martin Zvarík over 3 years
    That's completely different function... cannot be used in recursions