views:

493

answers:

3

I have an interesting problem. The basis of the problem is that my last iteration of an array reference doesn't seem to "stick," if you will. A little context: I've devised a very simple data structure for page heirarchy that looks like this:

,1,2,3>,4>,5,6,7<<,8

Translation: forget about the annoying leading commas. Pages 1, 2, 3, & 8 are top-level page id's, 4 is a subpage of 3 (the '>' means to move a level deeper), and 5, 6, & 7, are subpages of 4.

A more human-readable format would look like this:

1
2
3
-- 4
-- -- 5
-- -- 6
-- -- 7
8

Don't ask me why I'm doing it this way. I haven't yet come up with a simpler way of generating the structure with javascript and posting via a web form.

The problem is that everything goes great throughout the recursive function, but I lose page #8 back in my caller function. I suspect I'm mistaken on some element of recursion, variable references, and variable scope, and this has turned into quite the puzzle.

Expected Output (working just fine within the last call of the function):

Array
(
[1] => Array
    (
    )

[2] => Array
    (
    )

[3] => Array
    (
        [4] => Array
            (
                [5] => Array
                    (
                    )

                [6] => Array
                    (
                    )

                [7] => Array
                    (
                    )

            )

    )

[8] => Array
    (
    )

)

Actual Output (outside the loop):

Array
(
[1] => Array
    (
    )

[2] => Array
    (
    )

[3] => Array
    (
        [4] => Array
            (
                [5] => Array
                    (
                    )

                [6] => Array
                    (
                    )

                [7] => Array
                    (
                    )

            )

    )

)

Any thoughts?

[EDIT]: I removed a couple of residual self:: references...

CODE:

<?php
// recursive string in this format: (,\d+)*[>|<]?
//   ,      = leading comma
//   n,n+1  = comma-delimited list of page_ids
//   >      = indicates the next step in our depth-first approach
//   <      = indicates we're done with that set of children. back it up.
function parse_page_orders($page_orders, &$cur_page, &$trail)
{
 // #1 matches our comma-led, comma-delimited list of page id's
 // #2 matches our next step--forward or backward
 preg_match('/([,\d+]*)([>|<])?/', $page_orders, $matches);

 // remove this section of the page_orders variable so we can get on with our lives
 $page_orders = str_replace($matches[0], '', $page_orders);

 // #1: get the list of page ids and add it to the current page item
 $p = explode(',', $matches[1]);
 // start at 1 to skip the empty element at the beginning
 for ($i=1; $i<count($p); $i++)
 {
  $cur_page[$p[$i]] = array();
 }
 // #2: determine our next step
 if (isset($matches[2]))
 {
  if ($matches[2] == '>')
  {
   $trail[] = &$cur_page;
   parse_page_orders($page_orders, $cur_page[end($p)], $trail);
  }
  elseif ($matches[2] == '<' && count($trail)>0)
  {
   parse_page_orders($page_orders, array_pop($trail), $trail);
  }
 }
 else
 {
  // we're done. this should be our result.
  print_r($cur_page); 
 }
}
$pages = array();
$trail = array();
$page_orders = ',1,2,3>,4>,5,6,7<<,8';
parse_page_orders($page_orders, $pages, $trail);
print_r($pages);

?>
A: 

If you want to send a data structure from javascript to php, then try JSON. It would look something like this in javascript:

var obj = {1:[], 
           2:[], 
           3:{
              4:{
                 5:[], 
                 6:[], 
                 7:[]
                }
             }, 
           8:[]};

var json = JSON.stringify(obj);

//Now send it to the server as a string

This is all you need on the server, assuming $json has now got the string you created in javascript

<?php    
$arr = json_decode($strng, true);
print_r($arr);
?>
Marius
That was my first inclination, but I couldn't seem generate a JSON object that validated. Can you use numbers as indices in a JSON object?
yes you can. PHP requires that the key be wrapped in "", so you might either do that manually, or check if the stringify function does it for you. Use the JSON object from here: http://www.json.org/js.html
Marius
Another thing, you might need to check that you don't have magic_quotes on in PHP, which will mess with the json string when you send it to PHP as a GET or POST argument.
Marius
A: 

When you want to return to the higher level (encounter an < symbol), your recursive function has to return. Instead, your function goes deeper.

Logic should be something like this:

//parse page order string or its single level
function parse_page_orders(&$page_orders)
{  
  $result=array();
  while($page_orders)
  {
    $token=nextToken($page_orders);
    if ($token=='>') //iterate deeper on >
    {
       $result[]=parse_page_orders($page_orders);
       continue;
    }
    if ($token=='<') 
       return $result;
    if (is_numeric($token))
       $result[]=parseInt($token);
  }
return $result;
}

function nextToken(&$page_orders)
{
  if(preg_match('/(\d+)/'),$page_orders,$m)
  {
     $page_orders=substr($page_orders,strlen($m[1]));
     return parseInt($m[1]);
  }
  else
  {
    $result=$page_orders{0};
    $page_orders=substr($page_orders,1);
    return $result;
  }
}
yu_sha
I'm not sure what you mean by "it goes deeper." I stored a back reference through the $trail variable. That should bring it back right?
+1  A: 

in case you're interested how to parse the string in "your" format:

 class Parser {

  function run($str) {
   preg_match_all('~(\d+)|[<>]~', $str, $a);
   $this->a = $a[0];
   return $this->expr();
  }

  function expr() {
   $q = array();
   while(1) {
    if(!count($this->a)) return $q;
    $sym = array_shift($this->a);
    if($sym == '<') return $q;
    if($sym == '>')
     $q[count($q) - 1]['children'] = $this->expr();
    else
     $q[] = array('id' => $sym);
   }
  }
 }


 $a = "1,2,3>4,>5,6,7<<,8>9,10,>11<,12,<,13,14";
 $p = new Parser;
 $result = $p->run($a);
 print_r($result);
stereofrog