0

Right, I've searched all day long, html2array, html to json, html dom parser blah blah and I am struggling with this seemingly simple code.

I have the following html:

<ul>
    <li>menu1</li>
    <li>menu2
        <ul>
            <li>menu2.1</li>
        </ul>
    </li>
    <li>menu3</li>
</ul>

And I need to convert them to a parent child relationship. Or a php multi-dimensional array. I'm totally lost and frustrated. I'm hoping someone has an idea.

Edited by iamthwee

3
Contributors
15
Replies
109
Views
3 Years
Discussion Span
Last Post by pritaeas
Featured Replies
  • You may have seen this already in doing your research, but there might be a solution [here](http://stackoverflow.com/questions/9032676/nested-ul-li-to-php-array-incorrect-output-in-array). Ill keep looking. Read More

  • 2

    Here's a start, you just need to convert the output into a tree: <?php $html = <<<EOT <ul> <li>menu 1</li> <li>menu 2<ul> <li>menu 2.1</li> <li>menu 2.2</li></ul></li> <li>menu 3</li> </ul> EOT; function find_ul_or_li($source) { $result = false; $pos_ul = strpos($source, '<ul>'); $pos_li = strpos($source, '<li>'); $pos_cul = strpos($source, '</ul>'); $pos_cli = … Read More

  • 1

    Here's how to convert a single level. If you need deeper, then you'll have to make it recursive: $flat_array = ul_to_array($html); print_r($flat_array); $tree_array = array (); foreach ($flat_array as $item) { if ($item['level'] == 0) $tree_array[] = $item; else { for ($i = count($tree_array) - 1; $i >= 0; $i--) … Read More

  • 1

    A version using objects to simplify the nesting: <?php class MenuItem { public $Parent = NULL; public $Name = ''; public $Children = array (); } $html = <<<EOT <ul> <li>menu 1</li> <li>menu 2<ul> <li>menu 2.1<ul> <li>menu 2.1.1</li> <li>menu 2.1.2</li> <li>menu 2.1.3</li></ul></li> <li>menu 2.2</li> <li>menu 2.3</li></ul></li> <li>menu 3</li> </ul> EOT; … Read More

3

You may have seen this already in doing your research, but there might be a solution here.

Ill keep looking.

0

That link actually looks pretty useful, and no I've somehow missed that in my searches.

I want to nail this by the end of the weekend so I'll going to fully explore that. Yeah, simple_xml definitely sounds like the proper way to do this without any bugs.

0

What a waste of time, sorry to vent but the retard in that thread seems to create his list where each title is surrounded by a li.

Using the exact same code with my snippet

<?php

  $xml = <<<EOD
<li>menu1</li>
    <li>menu2
        <ul>
            <li>menu2.1</li>
        </ul>
    </li>
    <li>menu3</li>
EOD;

  function ul_to_array ($ul) {
    if (is_string($ul)) {
      if (!$ul = simplexml_load_string("<wrapper>$ul</wrapper>")) {
        trigger_error("Syntax error in UL/LI structure");
        return FALSE;
      }
      return ul_to_array($ul);
    } else if (is_object($ul)) {
      $output = array();
      foreach ($ul->li as $li) {
        $output[] = (isset($li->ul)) ? ul_to_array($li->ul) : (string) $li;
      }
      return $output;
    } else return FALSE;
  }

  print_r(ul_to_array($xml));

And I get this useless crap as my output:

Array ( [0] => menu1 [1] => Array ( [0] => menu2.1 ) [2] => menu3 )

Missing out menu2!

2

Here's a start, you just need to convert the output into a tree:

<?php
$html = <<<EOT
<ul>
    <li>menu 1</li>
    <li>menu 2<ul>
        <li>menu 2.1</li>
        <li>menu 2.2</li></ul></li>
    <li>menu 3</li>
</ul>
EOT;

function find_ul_or_li($source)
{
    $result = false;

    $pos_ul = strpos($source, '<ul>');
    $pos_li = strpos($source, '<li>');
    $pos_cul = strpos($source, '</ul>');
    $pos_cli = strpos($source, '</li>');

    if ($pos_ul === false)
        $pos_ul = PHP_INT_MAX;

    if ($pos_li === false)
        $pos_li = PHP_INT_MAX;

    if ($pos_cul === false)
        $pos_cul = PHP_INT_MAX;

    if ($pos_cli === false)
        $pos_cli = PHP_INT_MAX;

    if ($pos_ul < min($pos_li, $pos_cul, $pos_cli))
        $result = array ('token' => 'ul', 'pos' => $pos_ul);
    else if ($pos_li < min($pos_ul, $pos_cul, $pos_cli))
        $result = array ('token' => 'li', 'pos' => $pos_li);
    else if ($pos_cul < min($pos_ul, $pos_li, $pos_cli))
        $result = array ('token' => 'cul', 'pos' => $pos_cul);
    else if ($pos_cli < min($pos_ul, $pos_li, $pos_cul))
        $result = array ('token' => 'cli', 'pos' => $pos_cli);

    return $result;
}

function ul_to_array($source)
{
    $done = false;
    $result = array ();
    $level = -1;
    $previous_token = '';

    while (!$done)
    {
        $pos_result = find_ul_or_li($source);
        if (!$pos_result)
        {
            $done = true;
            continue;
        }

        if ($pos_result['token'] == 'ul')
        {
            if ($previous_token == 'li')
            {
                $value = substr($source, 0, $pos_result['pos']);
                $result[] = array ('level' => $level, 'value' => $value);
            }

            $level++;
            $source = substr($source, $pos_result['pos'] + 4);
            $previous_token = 'ul';
        }
        else if ($pos_result['token'] == 'li')
        {
            $source = substr($source, $pos_result['pos'] + 4);
            $previous_token = 'li';
        }
        else if ($pos_result['token'] == 'cul')
        {
            $level--;
            $source = substr($source, $pos_result['pos'] + 5);
            $previous_token = 'cul';
        }
        else if ($pos_result['token'] == 'cli')
        {
            $value = substr($source, 0, $pos_result['pos']);
            if ($value != '')
                $result[] = array ('level' => $level, 'value' => $value);

            $source = substr($source, $pos_result['pos'] + 5);
            $previous_token = 'cli';
        }
    }

    return $result;
}

print_r(ul_to_array($html));
?>

The output is this:

Array
(
[0] => Array
    (
        [level] => 0
        [value] => menu 1
    )
[1] => Array
    (
        [level] => 0
        [value] => menu 2
    )
[2] => Array
    (
        [level] => 1
        [value] => menu 2.1
    )
[3] => Array
    (
        [level] => 1
        [value] => menu 2.2
    )
[4] => Array
    (
        [level] => 0
        [value] => menu 3
    )
)

Edited by pritaeas

Votes + Comments
Exactly what I was looking for!
nice!
1

Here's how to convert a single level. If you need deeper, then you'll have to make it recursive:

$flat_array = ul_to_array($html);
print_r($flat_array);

$tree_array = array ();
foreach ($flat_array as $item)
{
    if ($item['level'] == 0)
        $tree_array[] = $item;
    else
    {
        for ($i = count($tree_array) - 1; $i >= 0; $i--)
        {
            if ($tree_array[$i]['level'] == $item['level'] - 1)
            {
                $tree_array[$i]['children'][] = $item;
                break;
            }
        }
    }
}

print_r($tree_array);

Outputs:

Array
(
    [0] => Array
        (
            [level] => 0
            [value] => menu 1
        )
    [1] => Array
        (
            [level] => 0
            [value] => menu 2
            [children] => Array
                (
                    [0] => Array
                        (
                            [level] => 1
                            [value] => menu 2.1
                        )
                    [1] => Array
                        (
                            [level] => 1
                            [value] => menu 2.2
                        )
                )
        )
    [2] => Array
        (
            [level] => 0
            [value] => menu 3
        )
)

Edited by pritaeas

1

A version using objects to simplify the nesting:

<?php
class MenuItem
{
    public $Parent = NULL;
    public $Name = '';
    public $Children = array ();
}

$html = <<<EOT
<ul>
    <li>menu 1</li>
    <li>menu 2<ul>
        <li>menu 2.1<ul>
            <li>menu 2.1.1</li>
            <li>menu 2.1.2</li>
            <li>menu 2.1.3</li></ul></li>
        <li>menu 2.2</li>
        <li>menu 2.3</li></ul></li>
    <li>menu 3</li>
</ul>
EOT;

function find_ul_or_li($source)
{
    // same as in previous post
}

function ul_to_objects($source)
{
    $menu = new MenuItem();
    $menu->Name = 'root';

    $currentItem = $menu;

    $done = false;
    $previous_token = '';

    while (!$done)
    {
        $pos_result = find_ul_or_li($source);
        if (!$pos_result)
        {
            $done = true;
            continue;
        }

        if ($pos_result['token'] == 'ul')
        {
            if ($previous_token == 'li')
            {
                $value = substr($source, 0, $pos_result['pos']);

                $newItem = new MenuItem();
                $newItem->Name = $value;
                $newItem->Parent = $currentItem;

                $currentItem->Children[] = $newItem;
                $currentItem = $newItem;
            }

            $source = substr($source, $pos_result['pos'] + 4);
            $previous_token = 'ul';
        }
        else if ($pos_result['token'] == 'li')
        {
            $source = substr($source, $pos_result['pos'] + 4);
            $previous_token = 'li';
        }
        else if ($pos_result['token'] == 'cul')
        {
            $currentItem = $currentItem->Parent;

            $source = substr($source, $pos_result['pos'] + 5);
            $previous_token = 'cul';
        }
        else if ($pos_result['token'] == 'cli')
        {
            $value = substr($source, 0, $pos_result['pos']);
            if ($value != '')
            {
                $newItem = new MenuItem();
                $newItem->Name = $value;
                $newItem->Parent = $currentItem;

                $currentItem->Children[] = $newItem;
            }

            $source = substr($source, $pos_result['pos'] + 5);
            $previous_token = 'cli';
        }
    }

    return $menu;
}

$menu = ul_to_objects($html);
print_r($menu);
?>

Output:

MenuItem Object
(
    [Parent] => 
    [Name] => root
    [Children] => Array
        (
            [0] => MenuItem Object
                (
                    [Parent] => MenuItem Object
                    [Name] => menu 1
                    [Children] => Array
                        (
                        )
                )
            [1] => MenuItem Object
                (
                    [Parent] => MenuItem Object
                    [Name] => menu 2
                    [Children] => Array
                        (
                            [0] => MenuItem Object
                                (
                                    [Parent] => MenuItem Object
                                    [Name] => menu 2.1
                                    [Children] => Array
                                        (
                                            [0] => MenuItem Object
                                                (
                                                    [Parent] => MenuItem Object
                                                    [Name] => menu 2.1.1
                                                    [Children] => Array
                                                        (
                                                        )
                                                )
                                            [1] => MenuItem Object
                                                (
                                                    [Parent] => MenuItem Object
                                                    [Name] => menu 2.1.2
                                                    [Children] => Array
                                                        (
                                                        )
                                                )
                                            [2] => MenuItem Object
                                                (
                                                    [Parent] => MenuItem Object
                                                    [Name] => menu 2.1.3
                                                    [Children] => Array
                                                        (
                                                        )
                                                )
                                        )
                                )
                            [1] => MenuItem Object
                                (
                                    [Parent] => MenuItem Object
                                    [Name] => menu 2.2
                                    [Children] => Array
                                        (
                                        )
                                )
                            [2] => MenuItem Object
                                (
                                    [Parent] => MenuItem Object
                                    [Name] => menu 2.3
                                    [Children] => Array
                                        (
                                        )
                                )
                        )
                )
            [2] => MenuItem Object
                (
                    [Parent] => MenuItem Object
                    [Name] => menu 3
                    [Children] => Array
                        (
                        )
                )
        )
)

Edited by pritaeas

0

Prit that flat file array is exactly what I was looking for. How many levels does it go or does it go to any level deep?

Second question now I just need a way to convert that flat array to a parent child relationship and I'm done!

0

does it go to any level deep?

Yes.

Now I just need a way to convert that flat array to a parent child relationship

Take my last post, probably more useful. Much easier to convert to whatever you need.

Edited by pritaeas

0

Too be honest Looping through the multidimensional array is confusing me, especially as it is an array of objects.

I think I should be able to convert the flat file to a parent child relationship which is just what I need.

I also used a bit of regex to remove whitespace between the uls and lis, I noticed that you gotta be very careful where you put a carriage return otherwise it creates additional array elements.

$html = preg_replace('~>\s+<~', '><', $html);

0

I think I should be able to convert the flat file to a parent child relationship which is just what I need.

Ok. If not, post the output you're looking for, and I'll add a method to convert the object list.

0

That would be great!

    <ul>
        <li>menu 1</li>
        <li>menu 2
            <ul>
                <li>menu 2.1
                    <ul>
                        <li>menu 2.1.1</li>
                        <li>menu 2.1.2</li>
                        <li>menu 2.1.3</li>
                    </ul>
                </li>
                <li>menu 2.2</li>
                <li>menu 2.3</li>
            </ul>
        </li>
        <li>menu 3</li>
    </ul>

    id   parent     content
    1    null       menu 1
    2    null       menu 2
    3    2          menu 2.1
    4    3          menu 2.1.1


    and so on...

That's the format of the array I'm looking for!

Edited by iamthwee

0

OK I managed to build the parent child relationship it wasn't too difficult. Thanks Prit if I ever release my menu builder I will be sure to give you credit.

 /**
      *  @Description: build the parent child relationship from array
      *       @Params: array
      *
      *      @returns: none
      */
    public function build_array($flat_array)
    {
        $test = array();

        $my_counter = 0;
        foreach ($flat_array as $key) 
        {
            $test[$my_counter]['level'] = $key['level'];

            //IMPORTANT TO TRIM THE PIPE SYMBOL
            $test[$my_counter]['value'] = trim($key['value'],"|");
            $test[$my_counter]['id'] = $my_counter;
            $my_counter++;

        }

        $arr_items = count($test);
        //echo $arr_items;
        for ($i=$arr_items - 1; $i >= 0 ; $i--) { 
            echo "id ".$test[$i]['id'];
            echo " parent id ".$this->get_parent_id($test,$i);
            echo " content ".$test[$i]['value'];
            echo br();
        }   

    }

     /**
      *  @Description: loop up to get the parent id
      *       @Params: array
      *
      *      @returns: parent id
      */
    public function get_parent_id($test,$offset)
    {
        $current_level = $test[$offset]['level'];
        $arr_items = count($test);
        //echo $arr_items;
        for ($i=$offset; $i >= 0 ; $i--) { 
            if($test[$i]['level']<$current_level)
            {
                //return the parent id
                return $test[$i]['id'];
                break;
            }

        }
    }
0

Too late I see, good. This is my updated version (the rest is in the previous post):

function ul_to_objects($source)
{
    $menu = new MenuItem();
    $menu->Name = 'root';

    $tableInfo = array ();

    $currentItem = $menu;
    $currentId = 1;

    $done = false;
    $previous_token = '';

    while (!$done)
    {
        $pos_result = find_ul_or_li($source);
        if (!$pos_result)
        {
            $done = true;
            continue;
        }

        if ($pos_result['token'] == 'ul')
        {
            if ($previous_token == 'li')
            {
                $value = substr($source, 0, $pos_result['pos']);

                $newItem = new MenuItem();
                $newItem->Id = $currentId;
                $newItem->Name = $value;
                $newItem->Parent = $currentItem;

                $tableInfo[] = array ('id' => $currentId, 'parent' => $currentItem->Id, 'content' => $value);

                $currentItem->Children[] = $newItem;
                $currentItem = $newItem;

                $currentId++;
            }

            $source = substr($source, $pos_result['pos'] + 4);
            $previous_token = 'ul';
        }
        else if ($pos_result['token'] == 'li')
        {
            $source = substr($source, $pos_result['pos'] + 4);
            $previous_token = 'li';
        }
        else if ($pos_result['token'] == 'cul')
        {
            $currentItem = $currentItem->Parent;

            $source = substr($source, $pos_result['pos'] + 5);
            $previous_token = 'cul';
        }
        else if ($pos_result['token'] == 'cli')
        {
            $value = substr($source, 0, $pos_result['pos']);
            if ($value != '')
            {
                $newItem = new MenuItem();
                $newItem->Id = $currentId;
                $newItem->Name = $value;
                $newItem->Parent = $currentItem;

                $tableInfo[] = array ('id' => $currentId, 'parent' => $currentItem->Id, 'content' => $value);

                $currentItem->Children[] = $newItem;

                $currentId++;
            }

            $source = substr($source, $pos_result['pos'] + 5);
            $previous_token = 'cli';
        }
    }

    //return $menu;
    return $tableInfo;
}
This question has already been answered. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.