Luca
Luca

Reputation: 984

PHP: Return an array from recursive function

I have an array like this one:

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
                                                                                )

                                                                        )

                                                                )

                                                        )

                                                )

                                        )

                                )

                        )

                )

        )

)

What I need to do is to build a breadcrumb using Anchestors. The one that is at the end of the list should be the first one. So, as an example:

Sport e tempo libero > Categorie > calcio...

I'm trying to iterate the xml with a function in this way without success:

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

I found something similar on stackoverflow but no suggestions helped me to sort this out.

Upvotes: 11

Views: 8650

Answers (5)

Vit
Vit

Reputation: 488

Alternatively you can use $_SESSION for getting data from recursion

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

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

Upvotes: -2

Rei
Rei

Reputation: 6363

To create a recursive function with an output, you need three things:

  1. A variable or a parameter to hold the current position, which is $r in your code. You got this one right.
  2. A variable or a parameter that holds the result, which you don't have. At first it appears to be $bread, but it is not holding any value because it is empty every time you call recursive(). A simple solution is to declare it as global inside the function.
  3. An if statement that checks the stop condition, which you don't have. Instead, you have a do-while loop in your code.

So, you have two mistakes there. Based on your code and modifying it as little as possible, this is the correct code:

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

There you go.

Update: I agree with @FlameStorm, global should be avoided if possible. I also received suggestion to use static instead, but it introduces a bug. Thus, I recommend avoiding static as well if you're not sure how to use it.

This is the improved code:

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

The $bread variable outside the function is no longer needed. Also, neither global or static is used.

Upvotes: 11

quickshiftin
quickshiftin

Reputation: 69611

SimpleXMLElement is not an array. You could convert it to an array, but PHP offers an iterator specifically for this case, SimpleXMLIterator.

Since you have a recursive structure, my suggestion is to flatten it with RecursiveIteratorIterator. Assuming your data is in a variable called $xml, your solution might look something like this:

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

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

$breadcrumb = array_reverse($breadcrumb);

Upvotes: 4

FlameStorm
FlameStorm

Reputation: 1004

Fixed one answer above - you should avoid global if you can.

So, the code will be

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

Upvotes: 0

code_angel
code_angel

Reputation: 1537

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

The output in browser:

Sport e tempo libero > Categorie > Calcio > Abbigliamento > Bambine e ragazze
Sport e tempo libero > Categorie > Calcio > Abbigliamento > Bambine e ragazze
( the second line with links)

The generated html code:

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>

Upvotes: 0

Related Questions