如何更新使用已弃用的 each() 的代码

How to update code that uses deprecated each()

我正在尝试更新一段旧代码(菜单生成器 class)。我已经更新了所有其他内容,但我被困在使用 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
            );
    }

编辑:添加屏幕截图以显示这段代码和下面的两个建议产生的结果。

原始(使用已弃用的 each() 函数):

乔希的版本:

凯文 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

作为一般规则,如果您需要离开 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
            );
    }

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

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

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

编辑:
这是我正在从提供给原始代码的 中处理的一个孤立示例:

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



?>

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

现在正在努力进行转换...

这是我写的 drop-in 替换:

    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/

我从检查第三个 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'],
];