【问题标题】:How can I update code that uses the deprecated each() function?如何更新使用已弃用 each() 函数的代码?
【发布时间】:2018-03-11 13:59:35
【问题描述】:

在 PHP 7.2 中,each 已弃用。 The documentation 说:

警告 自 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() 函数已弃用。此消息将在进一步调用时被隐藏

【问题讨论】:

  • 可以使用foreach()
  • array_map() 也可以使用闭包。
  • 这看起来像是来自 xmlrpc 库的东西。我也必须去修复一些东西,哈哈。
  • 是的,它没有令人信服的理由而被弃用,只是让人们改变他们的代码,却一无所获。 PHP世界的另一个挫折。 wiki.php.net/rfc/deprecations_php_7_2

标签: php each


【解决方案1】:

2019+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 个步骤

1。安装它

composer require rector/rector --dev

2。创建rector.php配置

vendor/bin/rector init

3。添加PHP_72设置

<?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。在你的代码上运行它

vendor/bin/rector process src --set php72

希望它对您的迁移有所帮助。


如果有一些错误或异常,那就是 Rector 遗漏的情况。 Create an issue,所以我们可以修复它并使其适用于所有可能的情况。

【讨论】:

  • 最后一个使用 key() 和 current() 的例子在值方面是正确的,但忽略了 each() 也将数组光标作为副作用前进的事实。此外,您可能指的是 list() 调用中的 $val 而不是 $callback 。一个合适的替换是: -list($key, $val) = each($callbacks); +$key = key($opt->option); +$val = current($opt->option); +next($callbacks);
  • 您能为此创建一个问题以便解决吗? github.com/rectorphp/rector/issues
  • 我没有使用那个库,我只是在谷歌上搜索 each() 替代品,在这里看到你的帖子,发现它很有用,但只是想我会指出那个小遗漏,这样你就可以更正您的帖子。
  • 我明白了。在 Github 回购问题中仍然总是更好地解决这个问题。很少有维护人员访问他们的旧响应,并且该错误通常会影响更多人
  • @Nils 我已经更新了示例。很难从内联代码中读取文本注释,gist.github.com 会更好。你能查一下吗?
【解决方案2】:

替换此代码

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

【讨论】:

    【解决方案3】:
    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-&gt;cache_data 交互。

    5. Fifth 可以替换为 for() 循环。

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

    【讨论】:

    • 对于 4.,我认为将 list($a, $b) = each($arr) 替换为 list($a, $b) = array(key($arr), current($arr)); next($arr); 不是吗?
    • @Metal3d 是的,应该是等价的。虽然就个人而言,我不会使用列表,我只会使用 key() 和 current() 直接分配给 $a 和 $b。我知道这是多行代码,但它似乎比创建一个数组更直接,只是用 list() 取回值。不过,只是我的看法。 :-)
    • 查看通用自动迁移版本如下:stackoverflow.com/a/55514591/1348344
    • 对于案例 1,我相信您需要确保在调用 current() 后内部指针是前进的,因为它不会移动指针。
    【解决方案4】:
     //  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);
    }
    

    【讨论】:

      【解决方案5】:

      扩展Petro Mäntylä案例3的优秀正确答案:

      这是“案例 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);
      

      【讨论】:

        【解决方案6】:

        使用这个功能怎么样?

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

        【讨论】:

          【解决方案7】:

          你绝对不应该做的方式是将函数“重新放入 php”,方法是将其添加到 php.ini 中的 auto_prepend_file 设置中

          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 应用程序运行之前声明函数。当您的应用程序尝试运行 each 函数时,它将使用您的版本。

          这绝对是你应该解决这个问题的方式,尤其是在生产中!但是,您是一名有时间限制的开发人员,您只想为您的下一个项目尝试任意框架,并且它们尚未更新为在您的本地开发服务器上工作而无需回退您的 php 版本。

          当您为项目提交代码库后,请继续执行已接受答案中的更改,因为它们确实有效。

          我使用 Wee Zel 的每个函数的仿真

          【讨论】:

          • 在我的例子中,替换函数陷入了无限循环。可能是因为它没有考虑到reset()next()
          【解决方案8】:
          reset($array);
          while (list($key, $value) = each($array)) {
          

          更新

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

          【讨论】:

          • 重要的是要注意这些是不等价的,尽管在大多数情况下一个 foreach 就足够了——如果你在 while 循环中修改 $array 它将遍历修改后的值。 foreach 创建列表的副本并对其进行迭代,因此对 $array 的突变不会改变循环。
          • @jpschroeder 好点,这是真的。此外,对于 foreach,不需要重置。
          • 在foreach之前reset基本没用。
          • 那是完全不同的函数...不能在递归中使用
          【解决方案9】:

          我找到了修复它的方法,并想分享信息。这里还有其他关于如何将 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) {
          

          【讨论】:

            【解决方案10】:

            以下是一些方法:

            标准的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-&gt;contents);

            【讨论】:

              【解决方案11】:

              您可以使用key()current()next() 创建自己的each() 函数。然后用该函数替换您的调用,如下所示:

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

              【讨论】:

              • 如果你想完全模拟每一个,我猜你需要输出中的“key”和“value”键以及 0 和 1。
              • @Don'tPanic,已编辑答案,这种情况不需要它,但可能存在一些情况。谢谢建议
              猜你喜欢
              • 1970-01-01
              • 2020-04-20
              • 2012-01-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多