PHP:从递归函数返回数组

11

我有一个这样的数组:

SimpleXMLElement Object
(
    [BrowseNodes] => SimpleXMLElement Object
        (
            [BrowseNode] => SimpleXMLElement Object
                (
                    [BrowseNodeId] => 969391031
                    [Name] => Bambine e ragazze
                    [Children] => SimpleXMLElement Object
                        (
                            [BrowseNode] => Array
                                (
                                    [0] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 969394031
                                            [Name] => Calze
                                        )

                                    [1] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 3635837031
                                            [Name] => Felpe
                                        )

                                    [2] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 3635838031
                                            [Name] => Giacche
                                        )

                                    [3] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 3635839031
                                            [Name] => Guanti da giocatore
                                        )

                                    [4] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 969392031
                                            [Name] => Maglie
                                        )

                                    [5] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 4351854031
                                            [Name] => Maglie per tifosi
                                        )

                                    [6] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 3635840031
                                            [Name] => Magliette da portiere
                                        )

                                    [7] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 969393031
                                            [Name] => Pantaloncini
                                        )

                                    [8] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 3635841031
                                            [Name] => Pantaloncini da portiere
                                        )

                                    [9] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 3635842031
                                            [Name] => Pantaloni
                                        )

                                    [10] => SimpleXMLElement Object
                                        (
                                            [BrowseNodeId] => 3635843031
                                            [Name] => Tute da ginnastica
                                        )

                                )

                        )

                    [Ancestors] => SimpleXMLElement Object
                        (
                            [BrowseNode] => SimpleXMLElement Object
                                (
                                    [BrowseNodeId] => 969386031
                                    [Name] => Abbigliamento
                                    [Ancestors] => SimpleXMLElement Object
                                        (
                                            [BrowseNode] => SimpleXMLElement Object
                                                (
                                                    [BrowseNodeId] => 937258031
                                                    [Name] => Calcio
                                                    [Ancestors] => SimpleXMLElement Object
                                                        (
                                                            [BrowseNode] => SimpleXMLElement Object
                                                                (
                                                                    [BrowseNodeId] => 524013031
                                                                    [Name] => Categorie
                                                                    [IsCategoryRoot] => 1
                                                                    [Ancestors] => SimpleXMLElement Object
                                                                        (
                                                                            [BrowseNode] => SimpleXMLElement Object
                                                                                (
                                                                                    [BrowseNodeId] => 524012031
                                                                                    [Name] => Sport e tempo libero
                                                                                )

                                                                        )

                                                                )

                                                        )

                                                )

                                        )

                                )

                        )

                )

        )

)

我需要做的是使用祖先元素构建面包屑导航。列表中最后一个应该是第一个。例如:

运动和休闲 > 类别 > 足球...

我想尝试使用函数迭代XML,但一直无法成功:

    $rec=$result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;

    $bread=array();
    function recursive($r)
    {
        do{
            $bread[]=$r->BrowseNodeId;
            recursive($r->Ancestors->BrowseNode);
        }while(isset($r->Ancestors));
        $bread=array_reverse($bread);
        return $bread;
    }

    print_r(recursive($rec));

我在stackoverflow上找到了类似的东西,但没有建议可以帮助我解决这个问题。


我不明白:Fan shop 是从哪里来的? - cFreed
我的错误。我编辑了帖子。 - Luca
你能把你的XML粘贴到某个地方吗?我想测试我的解决方案以确保它正常工作。 - quickshiftin
我在帖子中的所写内容就是我从一个帮助我通过api调用外部服务的类中得到的确切结果。 - Luca
3
如果您需要复制粘贴的解决方案,请提供XML。否则,请花几分钟理解我的答案,您就会意识到它是可行的。根据您的回答,我认为您甚至没有尝试去理解它。 - quickshiftin
5个回答

11

要创建一个带有输出的递归函数,您需要三个东西:

  1. 一个变量或参数来保存当前位置,这在您的代码中是 $r。您做得对。
  2. 一个变量或参数来保存结果,但您没有。一开始它似乎是 $bread,但由于每次调用 recursive() 时都为空,因此它不保存任何值。一个简单的解决方案是在函数内部将其声明为 global
  3. 一个检查停止条件的 if 语句,但您没有。相反,您的代码中有一个 do-while 循环。

因此,您有两个错误。基于您的代码,并尽可能少地进行修改,以下是正确的代码:

$rec = $result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;

$bread = array();
function recursive($r)
{
    global $bread;

    $bread[] = strval($r->BrowseNodeId);

    if(isset($r->Ancestors)){
        return recursive($r->Ancestors->BrowseNode);
    }else{
        return array_reverse($bread);
    }
}

print_r(recursive($rec));

就是这样。

更新:我同意@FlameStorm的看法,如果可能的话应避免使用global。 我也收到了建议使用static,但它会引入一个错误。 因此,如果您不确定如何使用它,我建议也避免使用static

这是改进后的代码:

$rec = $result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;

function recursive($r)
{
    if(isset($r->Ancestors))
        $bread = recursive($r->Ancestors->BrowseNode);

    $bread[] = strval($r->BrowseNodeId);
    return $bread;
}

print_r(recursive($rec));

函数外的$bread变量不再需要。此外,未使用globalstatic


太好了!它运行正常 :) 非常感谢你的帮助和解释。 - Luca

4
SimpleXMLElement 不是一个数组。你可以将它转换为数组,但 PHP 提供了专门用于此情况的迭代器——SimpleXMLIterator

由于你有一个递归结构,请使用RecursiveIteratorIterator 来扁平化它。假设你的数据保存在名为 $xml 的变量中,你的解决方案可能如下所示:

$xmlIterator  = new SimpleXMLIterator($xml->Ancestors);
$flatIterator = new RecursiveIteratorIterator($xmlIterator, RecursiveIteratorIterator::SELF_FIRST);

$breadcrumb = [];
foreach($flatIterator as $node) {
    $breadcrumb[] = $node['Name'];
}

$breadcrumb = array_reverse($breadcrumb);

2
请阅读我上面的评论,我要求您发布XML以便我进行测试。没有样本数据很难提供完美的工作答案! - quickshiftin

0
<?php
$sxe = new SimpleXMLElement("BrowseNodes.xml", NULL, TRUE);

// prepare a String for the SimpleXMLIterator.
// The SimpleXMLIterator expects:
// "A well-formed XML string or the path or URL to an XML document"
// therefore get the xml-string by calling asXML() on the 
$partitialXMLString = $sxe->BrowseNodes->BrowseNode->Ancestors->asXML();

$recursiveIterator = new RecursiveIteratorIterator(
        new SimpleXMLIterator($partitialXMLString), 
        RecursiveIteratorIterator::CHILD_FIRST
);

// if you need only the names
$name = array();

// if you need the links to something
$link = array();
$baseUrl = "http://example.com/BrowseNodeId/";

// if you need just the nodes in an array, and create the output later from it
// $xmlNode = array()

foreach($recursiveIterator as $node) {
    if (false == empty($node->Name)){
        $name[] = (string) $node->Name;
        $link[] = "<a href='" . $baseUrl . $node->BrowseNodeId . "'>" .  $node->Name . "</a>\n";

        // for later processing
        // $xmlNode[] = $node; 
    }
}

// Add the top BrowseNode->Name, from the node, where the First "Ancestors" is.
// This could be done also in the loop, when looping over all elements, 
// by adding conditions in order to differentiate between Children and Ancestors
// But this is more readable, and for your structure should be enough.
$firstNode = $sxe->BrowseNodes->BrowseNode;
$name[] = $firstNode->Name;
$link[] = "<a href='" . $baseUrl . $firstNode->BrowseNodeId . "'>" .  $firstNode->Name . "</a>\n";
// $xmlNode[] = $firstNode;

//output the path (child first)
// &gt; is  >
echo implode(' &gt; ', $name); 

echo "<br>\n";
//output the links (child first)
echo implode(' &gt; ', $link);

在浏览器中的输出:

运动和休闲 > 类别 > 足球 > 服装 > 女童
运动和休闲 > 类别 > 足球 > 服装 > 女童
(带链接的第二行)

生成的HTML代码:
Sport e tempo libero &gt; Categorie &gt; Calcio &gt; Abbigliamento &gt; Bambine e ragazze<br>
<a href='http://example.com/BrowseNodeId/524012031'>Sport e tempo libero</a>
 &gt; <a href='http://example.com/BrowseNodeId/524013031'>Categorie</a>
 &gt; <a href='http://example.com/BrowseNodeId/937258031'>Calcio</a>
 &gt; <a href='http://example.com/BrowseNodeId/969386031'>Abbigliamento</a>
 &gt; <a href='http://example.com/BrowseNodeId/969391031'>Bambine e ragazze</a>

0

修正了上面的一个答案 - 如果可以的话,应该避免使用global

因此,代码将会是:

$rec = $result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;

function recursive($r)
{
    $breads = [strval($r->BrowseNodeId)];

    if (isset($r->Ancestors)) {
        $breads = array_merge(recursive($r->Ancestors->BrowseNode), $breads);
    }

    return $breads;
}

$breadcrumbs = recursive($rec); // You got it.
print_r($breadcrumbs);

-2

或者你可以使用 $_SESSION 来从递归中获取数据

function start(){
$_SESSION['new1']=[];
$this->recursive($array);
$newVal = $_SESSION['new1'];
$_SESSION['new1']=[];
return $newVal;
}

function recursive($array){
...
$_SESSION['new1'][] = $val;
...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接