Как я могу обновить код, использующий устаревшую функцию each()?

В PHP 7.2 each устарел. В документации говорится:

Внимание Эта функция устарела, начиная с PHP 7.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() устарела. Это сообщение будет скрыто при дальнейших вызовах


person yokogeri    schedule 29.09.2017    source источник
comment
выполнимо с foreach()   -  person    schedule 10.02.2018
comment
array_map() с замыканием тоже подойдет.   -  person cchoe1    schedule 10.02.2018
comment
Это похоже на материал из библиотеки xmlrpc. Что-то мне тоже нужно исправлять, лол.   -  person IncredibleHat    schedule 04.03.2020
comment
Да, он устарел без веской причины и просто заставил людей изменить свой код и ничего не получить взамен. Еще одно разочарование в мире PHP. wiki.php.net/rfc/deprecations_php_7_2   -  person PHPst    schedule 03.06.2020


Ответы (11)


  1. В первых двух примерах вы можете использовать key() и current() для присвоения нужных вам значений.

    $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)];
    

    В этих случаях вы можете использовать next() для последующего перемещения курсора, но это может не понадобиться, если остальная часть вашего кода не зависит от этого.

  3. Для третьего случая я бы предложил вместо этого просто использовать цикл foreach() и назначить $kv внутри цикла.

    foreach ($broken as $k => $v) {
         $kv = [$k, $v];
    }
    
  4. В четвертом случае похоже, что ключ в list() не учитывается, поэтому можно присвоить текущее значение.

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

    Как и в первых двух случаях, может потребоваться переместить курсор с помощью next() в зависимости от того, как остальная часть вашего кода взаимодействует с $this->cache_data.

  5. Пятую можно заменить на for() петлю.

    reset($array);
    for ($i = 0; $i < 30; $i++) {
        $id = key($array);
        $item = current($array);
        // code
        next($array);
    }
    
person Don't Panic    schedule 29.09.2017
comment
Для 4. я думаю, правильно заменить list($a, $b) = each($arr) на list($a, $b) = array(key($arr), current($arr)); next($arr);, не так ли? - person Metal3d; 03.11.2018
comment
@ Metal3d да, это должно быть эквивалентно. Хотя лично я бы не стал использовать list, я бы просто присвоил $a и $b напрямую с помощью key() и current(). Я знаю, что это еще одна строка кода, но это кажется более простым, чем создание массива только для того, чтобы вернуть значения с помощью list(). Впрочем, только мое мнение. :-) - person Don't Panic; 03.11.2018
comment
См. версию универсальной автоматической миграции ниже: stackoverflow.com/a/55514591/1348344 - person Tomas Votruba; 18.01.2020
comment
Для случая 1 я считаю, что вам нужно убедиться, что внутренний указатель продвигается после вызова current(), поскольку он не перемещает указатель. - person charitha; 06.06.2020

2019+ Instant Upgrade of each()

Ознакомьтесь с демонстрацией каждой each миграции

На самом деле существует множество случаев, когда each() можно заменить, поэтому в этом вопросе так много разных ответов, за которые проголосовали.

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

А также:

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

Вы можете заменить один за другим вручную. Но разве нет лучшего способа?

Помогаю мигрировать проекты, где таких кейсов более 150+. Мне лень, поэтому я сделал инструмент под названием Rector, который преобразует код так, как выше (+ есть еще случаи, но не хочу спамить ответ).

Это часть набора PHP_72.


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

Я надеюсь, что это поможет вам с вашей миграцией.


Если есть какая-то ошибка или аномалия, это дело Ректора пропустил. Создайте задачу, чтобы мы могли исправить ее и заставить ее работать в каждом возможном случае.

person Tomas Votruba    schedule 04.04.2019
comment
Последний пример с использованием key() и current() является правильным с точки зрения значения, но игнорирует тот факт, что each() также перемещает курсор массива в качестве побочного эффекта. Кроме того, вы, вероятно, имеете в виду $val, а не $callback в вызове list(). Подходящей заменой будет: -list($key, $val) = each($callbacks); +$key = key($opt-›option); +$val = текущий($opt-›option); +далее($обратные вызовы); - person Nils; 31.10.2019
comment
Не могли бы вы создать проблему для этого, чтобы она была исправлена? github.com/rectorphp/rector/issues - person Tomas Votruba; 01.11.2019
comment
Я не использую эту библиотеку, я просто искал замену each(), наткнулся на ваш пост здесь и нашел его полезным, но просто подумал, что укажу на это небольшое упущение, чтобы вы могли исправить свой пост. - person Nils; 03.11.2019
comment
Понимаю. Тем не менее всегда лучше решить эту проблему в репозитории Github. Редко сопровождающие посещают свои старые ответы, и ошибка обычно затрагивает больше людей. - person Tomas Votruba; 04.11.2019
comment
@Nils Я обновил пример. Очень сложно читать встроенный код в виде текстового комментария, gist.github.com будет лучше. Не могли бы вы проверить? - person Tomas Votruba; 14.05.2020
comment
Вы добавили next(), который заботится о перемещении указателя, но все равно должен использовать одни и те же имена переменных ($val и $callback), если примеры должны быть эквивалентны. См. 3v4l.org/DWYMt для полных примеров кода и тестов, подтверждающих результаты. - person Nils; 16.05.2020
comment
Я теряюсь в таком количестве строк кода. Не могли бы вы дать мне только содержание, чтобы пройти там? - person Tomas Votruba; 17.05.2020
comment
Я уже дважды дал вам полный ответ. Если вы не заинтересованы в том, чтобы потратить одну минуту на то, чтобы полностью понять, где ваш ответ неверен, я оставляю это на ваше усмотрение. Если вы только что перечитали свое решение, это довольно очевидно, поскольку вы даже не используете одни и те же имена переменных... - person Nils; 18.05.2020
comment
Извините, мне это просто непонятно, и это всего лишь пример ответа. Там может быть любой код. Дело в том, что все изменения должны быть автоматизированы, именно для этой путаницы. Я скину фрагмент, чтобы никого не вводить в заблуждение. - person Tomas Votruba; 18.05.2020

Я нашел способ исправить это и решил поделиться информацией. Вот также другие примеры того, как обновить циклы each() до foreach().

Случай 1: отсутствует $value

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

Обновить до:

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

Случай 2: отсутствует $key

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

Обновить до:

foreach($array as $value) {

Случай 3: ничего не упустить

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

Обновить до:

foreach($array as $key => $value) {
person Petro Mäntylä    schedule 10.02.2018

вы можете создать свою собственную функцию each(), используя key(), current() и следующий(). затем замените свои вызовы этой функцией, например:

<?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);) {...}
person Wee Zel    schedule 29.09.2017
comment
Если вы хотите полностью эмулировать каждый из них, я думаю, вам понадобятся ключи и значения в выводе, а также 0 и 1. - person Don't Panic; 29.09.2017
comment
@Don'tPanic, отредактированный ответ, в этой ситуации он не нужен, но могут быть случаи, которые могут быть. спасибо за предложение - person Wee Zel; 29.09.2017

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

ОБНОВИТЬ

reset($array);
foreach($array as $key => $value) {
person Stewen Guyon    schedule 19.09.2018
comment
Важно отметить, что они не эквивалентны, хотя в большинстве случаев foreach будет достаточно — если вы измените $array в цикле while, он будет перебирать измененные значения. foreach создает копию списка и перебирает ее, поэтому мутации в $array не изменят цикл. - person jpschroeder; 30.10.2018
comment
@jpschroeder хорошая мысль, это правда. Кроме того, с foreach сброс не требуется. - person Don't Panic; 03.11.2018
comment
Сброс в основном бесполезен перед foreach. - person FrancescoMM; 31.10.2019
comment
Это совершенно другая функция... ее нельзя использовать в рекурсиях - person Martin Zvarík; 01.11.2020

Вот несколько способов сделать это:

Стандартный цикл foreach (очень читаемый):

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

Или, уменьшая:

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

Или, в функциональном выражении:

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

Ни одному из этих методов не требуется reset($this->contents); перед ним.

person trincot    schedule 10.02.2018

То, что вам определенно не следует делать, — это поместить функцию «обратно в php», добавив ее в настройку auto_prepend_file в php.ini.

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

Затем создайте файл и введите функцию с оболочкой function_exists.

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

По сути, это объявляет функцию до запуска вашего php-приложения. Когда ваше приложение попытается запустить каждую функцию, вместо этого оно будет использовать вашу версию.

Это абсолютно не способ, которым вы должны подходить к этой проблеме, особенно в рабочей среде! Однако вы разработчик с ограничениями по времени, и вы просто хотите попробовать произвольные фреймворки для своего следующего проекта, и они не были обновлены для работы на вашем локальном сервере разработки без отката вашей версии php.

Когда вы зафиксируете базу кода для своего проекта, пожалуйста, продолжайте и внесите изменения в принятый ответ, потому что они действительно работают.

Я использовал эмуляцию каждой функции Wee Zel

person John Tilley    schedule 19.02.2019
comment
В моем случае функция замены превращается в бесконечный цикл. вероятно, потому что он не учитывает reset() и next() - person rubo77; 02.10.2020

Чтобы расширить Petro Mäntylä отличный правильный ответ для Case 3:

Вот полный пример ситуации «Случай 3», потому что я нахожу полные примеры гораздо более информативными, чем фрагменты однострочного кода:

Это подлинный код из сторонней базы старого кода (TCPDF)

УСТАРЕЛО:

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

ФИКСИРОВАННЫЙ:

 // 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);
person Martin    schedule 02.04.2019

Замените этот код

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];
//             }
//         }

с этим

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];
                }
            }
        }
person Sahil    schedule 02.12.2019

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

Обновить до:

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

Другое условие:

foreach($this->contents as $key =>$value) {
  $total_items += $this->get_quantity($products_id);
}
person Harendra Singh    schedule 25.12.2019

Как насчет использования этой функции?

function array_fetch(array $a) {
   $element = current($a);
   next($a);
   return $element;
}
person garsax    schedule 22.02.2019