PHP - variable scope in recursively function - php

First assumption: Assume, we defined a variable (its name is $tmp) in a function(functioin name is 'ExpMenu') for temporary calculating and in end of function we return this variable.
Second assumption: Assume, we call that function recursively for create a navigation menu base on a multidimensional array.
My question is about scope of that variable ($tmp). In every call funtion, will its value overwritten? In other words, by every function call we lose previous value?
For more detail, please review below code:
/// --- { Declaration Block
$content = array(
array(
'level'=>'1',
'order'=>'1',
'text'=>'New Solution WorkFlow',
'is_parent'=>'yes',
'child'=> array(
array(
'level'=>'2',
'order'=>'1',
'text'=>'Define New Solution',
'is_parent'=>'no',
'url'=>'#'
),
array(
'level'=>'2',
'order'=>'2',
'text'=>'View Solutions',
'is_parent'=>'no',
'url'=>'#'
),
array(
'level'=>'2',
'order'=>'3',
'text'=>'View Solutions',
'is_parent'=>'no',
'url'=>'#'
)
)
),
array(
'level'=>'1',
'order'=>'2',
'text'=>'Solution Modify WorkFlow',
'is_parent'=>'yes',
'child'=> array(
array(
'level'=>'2',
'order'=>'1',
'text'=>'Request For Solution Modify',
'is_parent'=>'no',
'url'=>'#'
)
)
),
array(
'level'=>'1',
'order'=>'3',
'text'=>'Solution Close WorkFlow',
'is_parent'=>'yes',
'child'=> array(
array(
'level'=>'2',
'order'=>'1',
'text'=>'Declare For Solution Close',
'is_parent'=>'no',
'url'=>'#'
)
)
)
);
function ExpMenu($item_array ) {
$tmp='';
foreach ($item_array as $item) {
if ($item['is_parent']=='yes') {
$tmp = '<li class="hasChild">' . $item["text"] . '<ul>';
$tmp .= ExpMenu($item['child']);
$tmp .= '</ul></li>';
} else {
$tmp = '<li>';
$tmp .= ''. $item['text'] . '' ;
$tmp .= '</li>';
}
}
return $tmp;
}
/// --- }
$menu='<div><ul>';
$menu .= ExpMenu($content);
$menu.='</ul></div>';
echo $m . '<br />';

It seams by every call function we lose pervious value.
I thank #l0rkaY for her/him solution, But I found another solution that doesn't need add new parameter in my function.
Because $tmp scope is in 'ExpMenu' function and we call recursively function, therefore variable still alive and wasn't terminated.
So, I modify my function a bit:
function ExpMenu($item_array ) {
$tmp='';
foreach ($item_array as $item) {
if ($item['is_parent']=='yes') {
$tmp .= '<li class="hasChild">' . $item["text"] . '<ul>';
$tmp .= ExpMenu($item['child']);
$tmp .= '</ul></li>';
} else {
$tmp .= '<li>';
$tmp .= ''. $item['text'] . '' ;
$tmp .= '</li>';
}
}
return $tmp;
}

I assume your actual problem is, that your function generates only single item with a single child item.
It's because you are overwriting your previous item in your if/else blocks. That's why you only get the last item.
You just need to concatenate them to the existing items.
function ExpMenu($item_array ) {
$tmp='';
foreach ($item_array as $item) {
if ($item['is_parent']=='yes') {
$tmp .= '<li class="hasChild">' . $item["text"] . '<ul>';
$tmp .= ExpMenu($item['child']);
$tmp .= '</ul></li>';
} else {
$tmp .= '<li>';
$tmp .= ''. $item['text'] . '' ;
$tmp .= '</li>';
}
}
return $tmp;
}

Related

Foreach act wierd

i try to make an simple way to create an box in a class.
The problem is , it only give me the first element in the array. I echo out the $values and i get the whole css code and i try to place them in style at div. But still get only the last element.
My currently code looks like:
class general {
public function box($content,$style,$width = 50,$height = 50) {
foreach ($style as $k => $v) {
$values = ''.$k.':'.$v.';';
echo($values);
$box = '<div class="testBox" style="'.$values.'">'.$content.'</div> ';
}
return $box;
}
}
$general = new general();
$test = array(
'background-color' => '#000',
'font-size' => '120px'
);
echo $general->box('testValue',$test);
Try like this:
public function box($content,$style,$width = 50,$height = 50) {
$values = '';
foreach ($style as $k => $v) {
$values .= ''.$k.':'.$v.';';
}
$box = '<div class="testBox" style="'.$values.'">'.$content.'</div> ';
return $box;
}
$box = '<div class="testBox" style="'.$values.'">'.$content.'</div> ';
to
$box .= '<div class="testBox" style="'.$values.'">'.$content.'</div> ';
And declare
$box = ''; outside the loop.
You need to concate the data using .

How to output Multidimensional Arrays PHP

I'm trying to create a multidimensional array which stores a website navigation. Currently my navigation consists of Main -> Level 1 -> Level 2. I am having issues accessing level 2 correctly.
My Array example array is...
$pages = array
(
array('page_name' => 'Home'),
array('page_name' => 'About us', 'level_one' => array('first1','first2', 'level_two' => array('second'), 'first3')),
array('page_name' => 'Gallery', 'level_one' => array('first1','first2','first3','first4')),
array('page_name' => 'Contact us')
);
My code to retrieve the navigation so far is...
$count = count($pages);
for ($x=0; $x<$count; $x++) {
# echo yes for active page
if ($filename == $pages[$x][1]) { echo 'yes'; }
# echo main navigation
echo $pages[$x]['page_name'];
# check if the item has a sub page
$firstcount = count($pages[$x]['level_one']);
# if the item has a sub page echo
if ($firstcount > 0) { for ($y=0; $y<$firstcount; $y++) {
echo "\r\n" . '.' . $pages[$x]['level_one'][$y];
# check if the page has a sub page
$secondcount = count($pages[$x]['level_one']['level_two']);
if ($secondcount > 0) { for ($z=0; $z<$secondcount; $z++) {
if($pages[$x]['level_one']['level_two'][$z] != '') { echo "\r\n" . '..' . $pages[$x]['level_one']['level_two'][$z]; }
}}
} }
echo "\r\n";
}
And the output I'm currently getting is...
Home
About us
.first1
..second
.first2
..second
.first3
..second
.
..second
Gallery
.first1
.first2
.first3
.first4
Contact us
I am trying to create a multi level navigation. My expected output would be...
<!-- Expected output
Home
About us
.first1
.first2
..second
.first3
Gallery
.first1
.first2
.first3
.first4
Contact us
-->
I think you should check out some php recursion examples:
function returnArrayAsString($ar, $level = 0){
$str = '<ul class="level' . $level . '">';
foreach($ar as $k => $v){
if(is_array($v)){
$str .= '<li>' . $k . returnArrayAsString($v, $level + 1) . '</li>';
} else {
$str .= '<li>' . $v . '</li>';
}
}
$str .= '</ul>';
return $str;
}
if you then have a simple array:
$ar = array(
'Home',
'About us' => array(
'sub1a',
'sub1b',
'sub1c' => array(
'sub2a',
'sub2b'
)
)
);
Put array in function:
echo returnArrayAsString($ar);
This would give you a nice ordered list no matter the depth of sub items in the array.
As you can see, the function calls itself if it finds an array value. the $level variable can be used as a indicator, you could also use it for breadcrumbs ( About us > sub1c > sub2a).
Hope it helps....
The problem is in your loop code. Using foreach to make things more clear, results in the following, which outputs your result as you want it.
foreach($pages as $page)
{
echo "{$page['page_name']}\n";
foreach($page['level_one'] as $key => $level_one)
{
if($key === 'level_two')
{
foreach($level_one as $level_two)
{
echo "..$level_two\n";
}
}
else
{
echo ".$level_one\n";
}
}
}
You can use a lot of functions/ways to do this:
1.By foreach(Reference)
2.use var_dump to view any arrayvar_dump()
But remember to use <pre></pre> for prettifying.

array to generate form

I am trying to use an array to output a form with the following function:
public function createArrayForm($table, $do, $formDesc = '', $id, $array, $markFields = false) {
if (!isset($table) && !isset($do)) {
self::show_error('One or more parameters are missing in ' . __FUNCTION__);
} elseif ($table == 'update' && !isset($id)) {
self::show_error('For this form to be built, and ID must be set. Missing parameter `ID` in ' . __FUNCTION__);
}
if (is_array($array) && $do == 'insert') {
$out .= '<form action="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&table=' . $table . '" method="post" class="form-horizontal" ' . $formAppend . '>';
$out .= '<div class="form-desc">' . $formDesc . '</div>';
$out .= $markFields ? '<h3>Input Fields</h3>' : '';
foreach ($array as $type => $fieldname) {
if ($type == 'input') {
$out .= generateInputField($fieldname);
}
}
$out .= $markFields ? '<h3>Content Fields</h3>' : '';
foreach ($array as $type => $fieldname) {
if ($type == 'textarea') {
$out .= generateTextarea($fieldname, $cke);
}
}
$out .= $markFields ? '<h3>Images Fields</h3>' : '';
foreach ($array as $type => $fieldname) {
if ($type == 'image') {
$out .= generateImgField($fieldname);
}
}
$out .= form_hidden('user_data', '1');
$out .= form_hidden('id', self::generateID());
$out .= form_close();
return $out;
}
And call:
$arr = array("textarea"=>"project_name", "input"=>"created", "input"=>"last_modified", "input"=>"published");
echo $automate->createArrayForm('projects', 'insert', 'Some form desc', '123', $arr, true);
But it only outputs:
When it should look something like this:
Only one of each, for example input, is returned. Rather than all instances of it. So "input"=>"created", "input"=>"last_modified", "input"=>"published" should make three inputs, but it only returns one.
You're re-using array keys. So
$arr = array("textarea"=>"project_name", "input"=>"created", "input"=>"last_modified", "input"=>"published");
will end up looking like this:
$arr = array("textarea"=>"project_name", "input"=>"published");
Instead, modify your code to something like this:
$arr = array("textarea"=>array("project_name"), "input"=>array("created", "last_modified", "published"));
Then take those individual arrays, and iterate through them.
foreach ($array['input'] as $fieldname) { // etc and so on
In PHP, you cannot have arrays that share keys.
You'll be best creating a simple array, and creating sub-entries, so that you maintain order, but can have more than one input/textarea.
Like so:
$arr = array(
array('type' => 'textarea', 'name' => 'project_name'),
array('type' => 'input', 'name' => 'created'),
array('type' => 'input', 'name' => 'published'),
array('type' => 'input', 'name' => 'last_modified')
)
This would also allow you to add more parameters than type/name.
The problem is that you use the same key for everything in the array.
A possibility would be to exchange values and keys
$arr = array("textarea"=>"project_name", "input"=>"created", "input"=>"last_modified", "input"=>"published");
[...]
foreach ($array as $fieldname => $type) {
if ($type == 'textarea') {
$out .= generateTextarea($fieldname, $cke);
}
}
[...]

How to build a tree from a concatenated string in PHP?

I try to make a tree list in PHP from a hierarchy stored in a concatenated string in my mysql database
This my table :
and I'd like to reproduce something like this :
<ul>
<li>
<ul>
<li></li>
<li>
<ul>
<li></li>
<li></li>
</ul>
</li>
<li></li>
</ul>
</li>
<li></li>
<li></li>
</ul>
I know I have to use a recursive function I don't reach to do...
Maybe someone could help me
code without comments
see the usage and dataset section below to see what you need to pass and how to use these functions:
function items_to_tree( $items ){
$array = array();
foreach( $items as $item ) {
$parts = explode('.', $item['hierarchy']);
$last = array_pop( $parts );
$cursor = &$array;
foreach ( $parts as $part ) {
if ( !is_array($cursor[$part]) ) {
$cursor[$part] = array();
}
$cursor = &$cursor[$part];
}
$cursor[$last]['#item'] = $item;
}
return $array;
}
function tree_to_ul( $tree ){
$html = $children = '';
foreach( $tree as $key => $item ){
if ( substr($key,0,1) == '#' ) continue;
$children .= tree_to_ul( $item );
}
if ( isset($tree['#item']) ) {
$html .= '<li>' . PHP_EOL;
$html .= '<em>' . $tree['#item']['menu_text'] . '</em>' . PHP_EOL;
$html .= ( $children ? '<ul>' . $children . '</ul>' . PHP_EOL : '' );
$html .= '</li>' . PHP_EOL;
return $html;
}
else {
return $children;
}
}
code with comments and explanation
The code to convert your items to a tree structure:
function items_to_tree( $items ){
$array = array();
foreach( $items as $item ) {
/// split each hierarchy string into it's dot separated parts
$parts = explode('.', $item['hierarchy']);
/// pop off the last item of the array, we'll use this for assignment later
$last = array_pop( $parts );
/// create a reference to our position in the array we wish to fill out
$cursor = &$array;
/// step each hierarchy part and travel down the array structure,
/// just like you would if you typed an array path manually.
/// i.e. $array[$part][$part][...] and so on
foreach ( $parts as $part ) {
/// if at this point in the array, we don't have an array, make one.
if ( !is_array($cursor[$part]) ) {
$cursor[$part] = array();
}
/// ready for the next step, shift our reference to point to the next
/// $part in the array chain. e.g. if $cursor pointed to `$array[$part]`
/// before, after the next line of code the $cursor will point
/// to `$array[$oldpart][$part]`
$cursor = &$cursor[$part];
}
/// we popped the last item off the $parts array so we could easily
/// assign our final value to where the $cursor ends up pointing to.
/// starting with a hierarchy of '00001.00002.00003' would mean at this
/// point $cursor points to $array['00001']['00002'] and $last = '00003';
/// so finally we get $array['00001']['00002']['00003']['#item'] = $item;
$cursor[$last]['#item'] = $item;
/// use '#item' to keep our item's information separate from it's children.
}
/// return our built up array.
return $array;
}
The code to convert the tree structure to a UL:
function tree_to_ul( $tree ){
/// start with nothing
$html = $children = '';
/// step each item found in the current level of $tree
foreach( $tree as $key => $item ){
/// if the item's key starts with a # skip, these contain
/// our item's information and should not be treated as children
if ( substr($key,0,1) == '#' ) continue;
/// recurse this function so that we do the same for any child # any level.
$children .= tree_to_ul( $item );
}
/// if at this level a #item has been set, use this item information to
/// add a title to our level. You could change this to add whatever info
/// from your original database item that you'd like.
if ( isset($tree['#item']) ) {
$html .= '<li>' . PHP_EOL;
$html .= '<em>' . $tree['#item']['menu_text'] . '</em>' . PHP_EOL;
$html .= ( $children ? '<ul>' . $children . '</ul>' . PHP_EOL : '' );
$html .= '</li>' . PHP_EOL;
return $html;
}
/// if there wasn't an item, just return the traversed children.
else {
return $children;
}
}
dataset:
/// I simplified your dataset to an array, this could easily be generated
/// from a database query. You could also convert my code so that you
/// don't have to pre-generate an array, and instead could process after
/// each fetch from the database.
$items = array(
array('hierarchy' => '00001', 'menu_text' => 'One'),
array('hierarchy' => '00002', 'menu_text' => 'Two'),
array('hierarchy' => '00002.00001', 'menu_text' => 'Three'),
array('hierarchy' => '00002.00002', 'menu_text' => 'Four'),
array('hierarchy' => '00002.00003', 'menu_text' => 'Five'),
array('hierarchy' => '00002.00004', 'menu_text' => 'Six'),
array('hierarchy' => '00003', 'menu_text' => 'Seven'),
array('hierarchy' => '00003.00001', 'menu_text' => 'Eight'),
array('hierarchy' => '00003.00001.00001', 'menu_text' => 'Nine'),
array('hierarchy' => '00003.00001.00002', 'menu_text' => 'Ten'),
array('hierarchy' => '00003.00001.00003', 'menu_text' => 'Eleven'),
array('hierarchy' => '00003.00002', 'menu_text' => 'Twelve'),
);
usage:
/// Simple usage :) if a little complex explanation
$tree = items_to_tree( $items );
$html = tree_to_ul( $tree );
echo $html;
in the interests of codegolf ;)
The following could replace my items_to_tree function -- however it isn't advised.
$a = array();
foreach($items as $i){
eval('$a["'.str_replace('.','"]["',$i['hierarchy']).'"]=array("#item"=>$i);');
}
$refs = new stdClass();
//Assuming $data is the result array of your query to fetch the data.
foreach($data as $result)
{
$name = $result['hierarchy'];
$parent = substr($result['hierarchy'],0,strrpos($result['hierarchy'],'.'));
$thisref = &$refs->{$name};
foreach($result as $k => $v)
{
$thisref->{$k} = $v;
}
if ($parent == '') {
$tree->{$name} = &$thisref;
} else {
$refs->{$parent}->children->{$name} = &$thisref;
}
}
This will give you a nice object with every node's child in the property children.
function drawUL($level){
echo '<ul>';
foreach($level as $li){
echo '<li>'.$li->label;
if(isset($li->children))drawUl($li->children);
echo '</li>';
}
echo '</ul>';
}
drawUl($tree);

PHP — How to determine number of parents of a child array?

I'm not so strong with arrays but I need to determine how to count the number of parents a child array has in order to determine the indenting to display it as an option in a SELECT.
So, if I have this array:
array(
'World'=>array(
'North America'=>array(
'Canada'=>array(
'City'=>'Toronto'
)
)
)
);
How would I go about determining how many parents 'City' has in order to translate that into the number of spaces I want to use as an indent?
Thanks for any help.
EDIT: Let's see if I can explain myself better:
I have this code I'm using to build the OPTIONS list for a SELECT:
function toOptions($array) {
foreach ($array as $key=>$value) {
$html .= "<option value=\"" . $key . "\" >";
$html .= $value['title'];
$html .= "</option>";
if (array_key_exists('children', $value)) {
$html .= toOptions($value['children']);
}
}
return $html;
}
print toOptions($list);
So, I'm trying to determine how to get the number of parents in order to add spaces before the title in this line:
$html .= $value['title'];
Like:
$html .= " " . $value['title'];
But, I'm not sure how to figure out how many spaces to add.
Hopefully this is more clear.
Thanks for any help so far.
$x = array(
'World'=>array(
'North America'=>array(
'Canada'=>array(
'City'=>'Toronto'
)
)
)
);
// This function do something with the key you've found in the array
function visit($name, $depth)
{
echo $name . ' has ' . $depth . ' parents.';
}
// This function visits all the contents aff $array
function find_recursive($array, $depth = 0)
{
if (is_array($array)) {
foreach ($array as $k => $value) {
visit($k, $depth + 1);
find_recursive($array, $depth + 1);
}
}
}
For visiting:
find_recursive($x);
Well. Off the top what you are dealing with is a multi dimensional array.
You could run a count w/ foreach on each level of the array, and use the count number returned +1 for each level the foreach loops through.
I'm not sure if this answers your question, but I am trying to see exactly what it is you are trying to achieve.
As you are already using a recursive function to display that data, you can just extend your function. There is no need to traverse the array more often than one time:
function getWhitespaces($count) {
$result = '';
while($count--) {
$result .= '$nbsp;';
}
return $result;
}
function toOptions($array, $level=0) {
foreach ($array as $key=>$value) {
$html .= "<option value=\"" . $key . "\" >";
$html .= getWhitespaces($level) + $value['title'];
$html .= "</option>";
if (array_key_exists('children', $value)) {
$html .= toOptions($value['children'], $level + 1);
}
}
return $html;
}
print toOptions($list);
Try the following.. Your solution screams for recursion in my mind. Its a bit ugly but it seems to work
$totraverse = array(
'Moon' => array(
'Dark Side' => "Death Valley"
),
'Halley Commet' => "Solar System",
'World' => array(
'North America' => array(
'Canada' => array(
'City' => 'Toronto'
)
), 'South America' => array(
'Argentina' => array(
'City' => 'Toronto'
)
)
)
);
function traverse($totraverse_, $path="", $count=0) {
global $array;
// echo count($totraverse_) . " count\n";
if (!is_array($totraverse_)) {
echo "returning $path and $key\n";
return array($path, $count);
} else {
foreach ($totraverse_ as $key => $val) {
echo "assting $path and $key\n";
$result = traverse($val, $path . "/" . $key, $count + 1);
if($result){
$array[]=$result;
}
}
}
echo false;
}
$array = array();
traverse($totraverse);
foreach($array as $item){
echo "{$item[0]}--->{$item[1]}\n";
}

Categories