【问题标题】:How to update code that uses deprecated each()如何更新使用已弃用 each() 的代码
【发布时间】:2022-01-24 18:24:35
【问题描述】:

我正在尝试更新一段旧代码(菜单构建器类)。我已经更新了其他所有内容,但我被困在使用 each() 函数的行上。我确实阅读了一些以前的线程,但是这个特定的实例太复杂了,我无法弄清楚如何更改。这里是:

while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) )

这是出现上述行的整个函数:

function get_menu_html($lang, $root_id = 0 )
{
    $this->html  = array();
    $this->items = $this->get_menu_items($lang);
    
    foreach ( $this->items as $item )
        $children[$item['sectionParentID']][] = $item;
    
    // loop will be false if the root has no children (i.e., an empty menu!)
    $loop = !empty( $children[$root_id] );
    
    // initializing $parent as the root
    $parent = $root_id;
    $parent_stack = array();
    
    // HTML wrapper for the menu (open)
    $this->html[] = '<ul>';
    
    while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) )
    {
        if ( $option === false )
        {
            $parent = array_pop( $parent_stack );
            
            // HTML for menu item containing children (close)
            $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 ) . '</ul>';
            $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ) . '</li>';
        }
        elseif ( !empty( $children[$option['value']['sectionID']] ) )
        {
            $tab = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 );
            
            // HTML for menu item containing children (open)
            $this->html[] = sprintf(
                '%1$s<li><a href="%2$s">%3$s</a>',
                $tab,   // %1$s = tabulation
                $option['value']['sectionPage'],   // %2$s = sectionPage (URL)
                $option['value']['sectionLabel']   // %3$s = title
            ); 
            $this->html[] = $tab . "\t" . '<ul class="submenu">';
            
            array_push( $parent_stack, $option['value']['sectionParentID'] );
            $parent = $option['value']['sectionID'];
        }
        else
            // HTML for menu item with no children (aka "leaf") 
            $this->html[] = sprintf(
                '%1$s<li><a href="%2$s">%3$s</a></li>',
                str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ),   // %1$s = tabulation
                $option['value']['sectionPage'],   // %2$s = sectionPage (URL)
                $option['value']['sectionLabel']   // %3$s = title
            );
    }

编辑:添加屏幕截图以显示此代码和以下两个建议产生的内容。

ORIGINAL(带有已弃用的 each() 函数):

乔希的版本:

Kevin Y 的版本:

编辑 2:如果您想用我自己的数据进行测试,这是我使用 get_menu_items($lang) 从我的数据库中得到的打印结果:

Array ( [0] => Array ( [sectionID] => 1 [sectionParentID] => 0 [sectionPage] => Home [sectionLabel] => Начало ) [1] => Array ( [sectionID] => 2 [sectionParentID] => 0 [sectionPage] => Translations [sectionLabel] => Преvоди ) [2] => Array ( [sectionID] => 3 [sectionParentID] => 0 [sectionPage] => Prose [sectionLabel] => Проzа ) [3] => Array ( [sectionID] => 4 [sectionParentID] => 0 [sectionPage] => Poetry [sectionLabel] => Поезiя ) [4] => Array ( [sectionID] => 5 [sectionParentID] => 3 [sectionPage] => Stories [sectionLabel] => Разкази ) [5] => Array ( [sectionID] => 6 [sectionParentID] => 3 [sectionPage] => Articles [sectionLabel] => Статии ) [6] => Array ( [sectionID] => 7 [sectionParentID] => 3 [sectionPage] => Essays [sectionLabel] => Есета ) [7] => Array ( [sectionID] => 8 [sectionParentID] => 3 [sectionPage] => Fragments [sectionLabel] => Фрагменти ) [8] => Array ( [sectionID] => 9 [sectionParentID] => 4 [sectionPage] => Woman [sectionLabel] => Аз и Жената ) [9] => Array ( [sectionID] => 10 [sectionParentID] => 4 [sectionPage] => Civilization [sectionLabel] => Аз и Цивилизацията ) [10] => Array ( [sectionID] => 11 [sectionParentID] => 4 [sectionPage] => Universe [sectionLabel] => Аз и Вселената ) [11] => Array ( [sectionID] => 12 [sectionParentID] => 4 [sectionPage] => Duskoreznitsa [sectionLabel] => (По-)Етична дъскорезница ) [12] => Array ( [sectionID] => 13 [sectionParentID] => 4 [sectionPage] => PrazniPrikazki [sectionLabel] => Празни приказки ) [13] => Array ( [sectionID] => 14 [sectionParentID] => 4 [sectionPage] => Extrakts [sectionLabel] => Екстракти ) [14] => Array ( [sectionID] => 17 [sectionParentID] => 0 [sectionPage] => Blog [sectionLabel] => Трънки и блогинkи ) [15] => Array ( [sectionID] => 18 [sectionParentID] => 0 [sectionPage] => Contact [sectionLabel] => Контакт ) [16] => Array ( [sectionID] => 19 [sectionParentID] => 0 [sectionPage] => About [sectionLabel] => За сайта ) [17] => Array ( [sectionID] => 20 [sectionParentID] => 3 [sectionPage] => Research [sectionLabel] => Изследвания ) [18] => Array ( [sectionID] => 21 [sectionParentID] => 3 [sectionPage] => Amsterdamned [sectionLabel] => Amsterdamned ) [19] => Array ( [sectionID] => 22 [sectionParentID] => 4 [sectionPage] => Treski [sectionLabel] => Трески ) [20] => Array ( [sectionID] => 23 [sectionParentID] => 2 [sectionPage] => PoetryTranslation [sectionLabel] => Преводи на поезия ) [21] => Array ( [sectionID] => 25 [sectionParentID] => 2 [sectionPage] => ProseTranslation [sectionLabel] => Преводи на проза ) [22] => Array ( [sectionID] => 27 [sectionParentID] => 2 [sectionPage] => SubtitleTranslation [sectionLabel] => Преводи на субтитри ) [23] => Array ( [sectionID] => 28 [sectionParentID] => 2 [sectionPage] => OpinionJournalismTranslation [sectionLabel] => Преводи на публицистика ) ) 

parent_id 替换为 sectionParentID id 替换为 sectionID link 替换为 sectionPage title 替换为 sectionLabel

【问题讨论】:

  • 如果您运行代码,它是否仍表示已弃用?
  • 你能发布一个函数的极简隔离示例,使用“每个”的示例输入和输出吗?循环是相当复杂的,它如何使用每个迭代并与它的父级一起玩,并且可能是一堆意大利面条。理想情况下,这可以重构为更易读的东西,比如首先循环父母,这样可以构建一个更流畅的循环,这样人类就不会那么复杂了。在一个孤立的例子中,你可以给我们整个函数。
  • @Dean:是的,我看到这条消息:“已弃用:each() 函数已弃用。此消息将在 C:... 的进一步调用中被禁止显示”
  • @KevinY:我很难真正理解这个功能。如果我确切地知道它的作用,我也许可以写一个新的来达到同样的效果。
  • @KevinY,请在这篇文章中找到原始代码(我从其他地方得到的,现在找不到了):stackoverflow.com/questions/29892308/…

标签: php each deprecated


【解决方案1】:

我从检查第三个 while 循环条件开始进行 foreach。

$this->html  = array();
$this->items = $this->get_menu_items();

foreach ($this->items as $item)
    $children[$item['parent_id']][] = $item;

// loop will be false if the root has no children (i.e., an empty menu!)
$loop = !empty($children[$root_id]);

// initializing $parent as the root
$parent = $root_id;
$parent_stack = array();

// HTML wrapper for the menu (open)
$this->html[] = '<ul>';
if ($loop) {
    foreach ($children[$parent] as $option) {
        if ($parent > $root_id) {
            break; // exit loop
        }
        if ($option === false) {
            $parent = array_pop($parent_stack);

            // HTML for menu item containing childrens (close)
            $this->html[] = str_repeat("\t", (count($parent_stack) + 1) * 2) . '</ul>';
            $this->html[] = str_repeat("\t", (count($parent_stack) + 1) * 2 - 1) . '</li>';
        } elseif (!empty($children[$option['id']])) {
            $tab = str_repeat("\t", (count($parent_stack) + 1) * 2 - 1);

            // HTML for menu item containing childrens (open)
            $this->html[] = sprintf(
                '%1$s<li><a href="%2$s">%3$s</a>',
                $tab,   // %1$s = tabulation
                $option['link'],   // %2$s = link (URL)
                $option['title']   // %3$s = title
            );
            $this->html[] = $tab . "\t" . '<ul class="submenu">';

            array_push($parent_stack, $option['parent_id']);
            $parent = $option['id'];
        } else
            // HTML for menu item with no children (aka "leaf") 
            $this->html[] = sprintf(
                '%1$s<li><a href="%2$s">%3$s</a></li>',
                str_repeat("\t", (count($parent_stack) + 1) * 2 - 1),   // %1$s = tabulation
                $option['link'],   // %2$s = link (URL)
                $option['title']   // %3$s = title
            );
    }

    // HTML wrapper for the menu (close)
    $this->html[] = '</ul>';

    return implode("\r\n", $this->html);
}

测试:

[ 
    ['id'=>1, 'parent_id'=>0, 'title'=>'title 1','link'=>'link 1','position'=>'0'],
    ['id'=>2, 'parent_id'=>1, 'title'=>'title 2','link'=>'link 2','position'=>'0'],
    ['id'=>3, 'parent_id'=>1, 'title'=>'title 3','link'=>'link 3','position'=>'0'],
    ['id'=>4, 'parent_id'=>0, 'title'=>'title 4','link'=>'link 4','position'=>'0'],
];

【讨论】:

  • 我将函数中的所有内容从while (...) 替换到最后,但收到的通知与以下相同。
  • 从下面在沙箱中运行该代码,从数组属性中删除['value'] 修复了索引错误,值或values 存储在数组中。更新答案
  • 我复制粘贴并编辑了它以使用我的数据库 - 一些索引的非常简单的替换 - 两次,两次似乎都有效(没有抛出错误),但菜单列表是生成在开头第一个子菜单中被截断:&lt;ul class="submenu"&gt;。不知道出了什么问题。我明天再试一次。
  • 我又试了一次,结果一样:只生成了主列表。我将尝试在我的原始帖子中添加屏幕截图,显示旧代码、您的代码和@Kevin Y 的代码产生的内容。
  • 请查看我的原始帖子以打印出get_menu_items($lang) 从我的数据库中提供的内容。也许您可以用它测试您的版本以找出问题所在。
【解决方案2】:

作为一般规则,如果您需要离开each($ar),您通常可以使用[key($ar),current($ar)] 作为替代品,但是您需要在循环内移动指针。您通常只需要在循环中调用next($ar) 并在您用完阅览室时中断,例如在密钥为空时中断。然后确保数组在空间用完时设置为 false。

这种类型的方法可能会很快变得丑陋,但由于额外的代码......

所以不用太在意你的代码,在这次更新之后,你修改后的代码看起来像:

function get_menu_html($lang, $root_id = 0 )
{
    $this->html  = array();
    $this->items = $this->get_menu_items($lang);
    
    foreach ( $this->items as $item )
        $children[$item['sectionParentID']][] = $item;
    
    // loop will be false if the root has no children (i.e., an empty menu!)
    $loop = !empty( $children[$root_id] );
    
    // initializing $parent as the root
    $parent = $root_id;
    $parent_stack = array();
    
    // HTML wrapper for the menu (open)
    $this->html[] = '<ul>';
    
    while ( $loop && ( ( $option = [key($children[$parent]),current($children[$parent])] ) || ( $parent > $root_id ) ) )
    {
        if($option[0] === null){$option = false;} // replicate each's behavior for after last element
        next($children[$parent]);
        if ( $option === false )
        {
            $parent = array_pop( $parent_stack );
            
            // HTML for menu item containing children (close)
            $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 ) . '</ul>';
            $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ) . '</li>';
            break;
        }
        elseif ( !empty( $children[$option['value']['sectionID']] ) )
        {
            $tab = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 );
            
            // HTML for menu item containing children (open)
            $this->html[] = sprintf(
                '%1$s<li><a href="%2$s">%3$s</a>',
                $tab,   // %1$s = tabulation
                $option['value']['sectionPage'],   // %2$s = sectionPage (URL)
                $option['value']['sectionLabel']   // %3$s = title
            ); 
            $this->html[] = $tab . "\t" . '<ul class="submenu">';
            
            array_push( $parent_stack, $option['value']['sectionParentID'] );
            $parent = $option['value']['sectionID'];
        }
        else
            // HTML for menu item with no children (aka "leaf") 
            $this->html[] = sprintf(
                '%1$s<li><a href="%2$s">%3$s</a></li>',
                str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ),   // %1$s = tabulation
                $option['value']['sectionPage'],   // %2$s = sectionPage (URL)
                $option['value']['sectionLabel']   // %3$s = title
            );
    }

编辑:请注意上面的代码不起作用,因为它从 each 引用 $option['value'] 而不是从 each 引用 $option[1]。需要在代码中进行调整。

您可能还想重构代码。但是在没有弃用代码的情况下让它工作是很好的第一步。

不过,这种方法通常非常难看。最好重构并使用某种特定于代码逻辑的 foreach 循环。

编辑:
这是我正在处理的一个独立示例,来自提供给原始代码的link

<?php

/**
 * Generate HTML for multi-dimensional menu from MySQL database
 * with ONE QUERY and WITHOUT RECURSION 
 * @author J. Bruni
 */
class MenuBuilder
{

    /**
     * Menu items
     */
    var $items = array();

    /**
     * HTML contents
     */
    var $html  = array();



    /**
     * Get all menu items from database
     */
    function get_menu_items()
    {
        // Change the field names and the table name in the query below to match tour needs
        return [
                ['id'=>1, 'parent_id'=>0, 'title'=>'title 1','link'=>'link 1','position'=>'0'],
                ['id'=>2, 'parent_id'=>1, 'title'=>'title 2','link'=>'link 2','position'=>'0'],
                ['id'=>3, 'parent_id'=>1, 'title'=>'title 3','link'=>'link 3','position'=>'0'],
                ['id'=>4, 'parent_id'=>0, 'title'=>'title 4','link'=>'link 4','position'=>'0'],
            ];
    }

    /**
     * Build the HTML for the menu 
     */
    function get_menu_html( $root_id = 0 )
    {
        $this->html  = array();
        $this->items = $this->get_menu_items();

        foreach ( $this->items as $item )
            $children[$item['parent_id']][] = $item;

        // loop will be false if the root has no children (i.e., an empty menu!)
        $loop = !empty( $children[$root_id] );

        // initializing $parent as the root
        $parent = $root_id;
        $parent_stack = array();

        // HTML wrapper for the menu (open)
        $this->html[] = '<ul>';

        while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) )
        {
            if ( $option === false )
            {
                $parent = array_pop( $parent_stack );

                // HTML for menu item containing childrens (close)
                $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 ) . '</ul>';
                $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ) . '</li>';
            }
            elseif ( !empty( $children[$option['value']['id']] ) )
            {
                $tab = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 );

                // HTML for menu item containing childrens (open)
                $this->html[] = sprintf(
                    '%1$s<li><a href="%2$s">%3$s</a>',
                    $tab,   // %1$s = tabulation
                    $option['value']['link'],   // %2$s = link (URL)
                    $option['value']['title']   // %3$s = title
                ); 
                $this->html[] = $tab . "\t" . '<ul class="submenu">';

                array_push( $parent_stack, $option['value']['parent_id'] );
                $parent = $option['value']['id'];
            }
            else
                // HTML for menu item with no children (aka "leaf") 
                $this->html[] = sprintf(
                    '%1$s<li><a href="%2$s">%3$s</a></li>',
                    str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ),   // %1$s = tabulation
                    $option['value']['link'],   // %2$s = link (URL)
                    $option['value']['title']   // %3$s = title
                );
        }

        // HTML wrapper for the menu (close)
        $this->html[] = '</ul>';

        return implode( "\r\n", $this->html );
    }
}

$menu = new MenuBuilder();
echo '<pre>' . $menu->get_menu_html() . '</pre>';



?>

它仍然有“每个”,但它是孤立的,所以应该更容易转换。 可以在这里玩:https://sandbox.onlinephpfunctions.com/

现在正在努力转换...

这是我写的一个直接替换:

    public function each(&$ar)
    {
        $key = key($ar);
        if($key === null){return false;}
        $current = current($ar);
        
        try
        {
        return [$key,$current,'key'=>$key,'value'=>$current];
        }finally{
          next($ar);  
        };
        
    }

然后代替:

while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $root_id ) ) )

使用:

while ( $loop && ( ( $option = $this->each( $children[$parent] ) ) || ( $parent > $root_id ) ) )

即使用 $this->each 而不是 each。

最终代码:

<?php

/**
 * Generate HTML for multi-dimensional menu from MySQL database
 * with ONE QUERY and WITHOUT RECURSION 
 * @author J. Bruni
 */
class MenuBuilder
{

    /**
     * Menu items
     */
    var $items = array();

    /**
     * HTML contents
     */
    var $html  = array();


    public function each(&$ar)
    {
        $key = key($ar);
        if($key === null){return false;}
        $current = current($ar);
        
        try
        {
        return [$key,$current,'key'=>$key,'value'=>$current];
        }finally{
          next($ar);  
        };
        
    }


    /**
     * Get all menu items from database
     */
    function get_menu_items()
    {
        // Change the field names and the table name in the query below to match tour needs
        return [
                ['id'=>1, 'parent_id'=>0, 'title'=>'title 1','link'=>'link 1','position'=>'0'],
                ['id'=>2, 'parent_id'=>1, 'title'=>'title 2','link'=>'link 2','position'=>'0'],
                ['id'=>3, 'parent_id'=>1, 'title'=>'title 3','link'=>'link 3','position'=>'0'],
                ['id'=>4, 'parent_id'=>0, 'title'=>'title 4','link'=>'link 4','position'=>'0'],
            ];
    }

    /**
     * Build the HTML for the menu 
     */
    function get_menu_html( $root_id = 0 )
    {
        $this->html  = array();
        $this->items = $this->get_menu_items();

        foreach ( $this->items as $item )
            $children[$item['parent_id']][] = $item;

        // loop will be false if the root has no children (i.e., an empty menu!)
        $loop = !empty( $children[$root_id] );

        // initializing $parent as the root
        $parent = $root_id;
        $parent_stack = array();

        // HTML wrapper for the menu (open)
        $this->html[] = '<ul>';

        while ( $loop && ( ( $option = $this->each( $children[$parent] ) ) || ( $parent > $root_id ) ) )
        {
            if ( $option === false )
            {
                $parent = array_pop( $parent_stack );

                // HTML for menu item containing childrens (close)
                $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 ) . '</ul>';
                $this->html[] = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ) . '</li>';
            }
            elseif ( !empty( $children[$option['value']['id']] ) )
            {
                $tab = str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 );

                // HTML for menu item containing childrens (open)
                $this->html[] = sprintf(
                    '%1$s<li><a href="%2$s">%3$s</a>',
                    $tab,   // %1$s = tabulation
                    $option['value']['link'],   // %2$s = link (URL)
                    $option['value']['title']   // %3$s = title
                ); 
                $this->html[] = $tab . "\t" . '<ul class="submenu">';

                array_push( $parent_stack, $option['value']['parent_id'] );
                $parent = $option['value']['id'];
            }
            else
                // HTML for menu item with no children (aka "leaf") 
                $this->html[] = sprintf(
                    '%1$s<li><a href="%2$s">%3$s</a></li>',
                    str_repeat( "\t", ( count( $parent_stack ) + 1 ) * 2 - 1 ),   // %1$s = tabulation
                    $option['value']['link'],   // %2$s = link (URL)
                    $option['value']['title']   // %3$s = title
                );
        }

        // HTML wrapper for the menu (close)
        $this->html[] = '</ul>';

        return implode( "\r\n", $this->html );
    }
}

$menu = new MenuBuilder();
echo '<pre>' . $menu->get_menu_html() . '</pre>';



?>

这里测试正常: https://sandbox.onlinephpfunctions.com/

【讨论】:

  • 我试图直接用你的版本替换现有函数,但是页面刚刚爆炸了一堆错误: 注意:未定义索引:值在...第 81 行,即 'elseif ( !empty ( $children[$option['value']['sectionID']] ) )' 注意:试图在 ... 中访问 null 类型值的数组偏移量(同一行)。这重复了十几次。我不明白出了什么问题,抱歉。
  • @cheeseus 用$option[1] 替换每个$option['value'],它应该可以工作。
  • @cheeseus 我添加了一个each 实现,作为一种相对容易使用的方法,与未来的读者一起使用。只需调用 $this-&gt;each(...) 而不是 each(...) 并将我的 each 方法添加到您的课程中。
  • 刚刚用我的数据库连接对此进行了测试,它可以正常工作,没有任何错误。我想完全摆脱这个额外的方法 each() 会很棒,但如果它有效,它就会有效。谢谢!
  • @cheeseus 如果你想用$option[1] 替换$option['value'] 的实例,我的原始代码将起作用,如果你想摆脱额外的每个方法......但是,嘿,如果它有效,它作品!也可以尝试将其移动到闭包$each,这样它作为类中的方法会产生更少的噪音。
猜你喜欢
  • 2018-03-11
  • 2012-01-24
  • 1970-01-01
  • 1970-01-01
  • 2021-02-14
相关资源
最近更新 更多