Thanks in advance for any help.
I have created a collection of functions that I use to generate a menu on my website (prestopnik.com). My webhost uses PHP 4.0.6.
A couple of months ago I noticed that it was taking 5-15 seconds on average to load a page of my website, and most of that time the browser said "waiting for prestopnik.com". I talked to my web host, and they suggested it had to do with my php.
I've isolated the problem to my menuing functions (when I remove the menu, the problem goes away).
I'll attach the relevant code to the end of this post, but here is some basic documentation/pseudo-code:
My website is organized as a tree. Each page of the website is represented by one (or more) nodes in the tree. The root node is a dummy node. Currently there are about 95 nodes in this tree.
When you visit a page on my website, the menu is generated dynamically based on this process:
1) The menu data is loaded from a text file into a tree.
2) The tree is traveresed in order and at each node a 4 part test is applied to decide if that node should be displayed as part of the menu. If any of the four parts of the test is true, the node is printed, otherwise it is not.
The 4 part test:
a) Is this node the child of the root? (i.e. all top level nodes are printed)
b) Is this node a sibling of a node representing the current page?
c) Is this node a child of a node representing the current page?
d) Is this node in the path from the root to the node representing this page?
----end documentation/pseudocode----
I think that the slowest part of this is the 4th test, which I could probably do a bit more efficiently.
I have a few questions:
1) Do you have any recomendations for how I might speed my code up.
1b) I use a lot of recursion in this code. Is recursion particularly slow in PHP? How about creating arrays or joining them?
2) Should a decent webhost be able to run this php code faster than mine is. I have a pretty cheap ($8/month) webhost, and I'm wondering if I just don't get many cpu cycles for my website because I pay so little.
2b) Also if this is mostly the result of a cheap web host, what sort of questions could I ask my webhost or future webhosts that would ensure my website was running on a server with more cpu cycles to spare?
Again, thanks for any help that you can offer.
$GlobalSerial = 0;
class menu_tree_node
{
var $serial;
var $url;
var $menu_name;
//var $mouse_over;
var $children_pages = array();
//var $index_terms = array();
var $hidden;
var $font_size;
var $icon;
function menu_tree_node()
{
// $this->mouse_over = "mouse over test";
$this->hidden = false;
$this->font_size = "medium";
$this->icon = "none";
$this->serial = $GLOBALS['GlobalSerial']++;
}
}
function load_tree($file_name)
{
$lines = file($file_name);
for($i = 0; $i<count($lines); $i++)
$lines[$i] = rtrim($lines[$i]);
$root = new menu_tree_node();
read_and_add_node($lines, $root);
return $root;
}
//$root needs to be passed by reference
function read_and_add_node(&$lines, &$root)
{
$root->url = $lines[0];
$root->menu_name = $lines[1];
if($lines[2] == "true")
$root->hidden = true;
else
$root->hidden = false;
$root->font_size = $lines[3];
$root->icon = $lines[4];
$num_children = $lines[5];
for($i = 0; $i < $num_children; $i++) //$line[5] is a string, but converts to a #
{
$root->children_pages[$i] = new menu_tree_node();
$lines = array_slice($lines,6);
read_and_add_node($lines, $root->children_pages[$i]);
}
}
function print_full_tree($root)
{
print_node($root);
for($i = 0; $i < count($root->children_pages); $i++)
{
print_full_tree($root->children_pages[$i]);
}
}
function get_parent_n($root, $node)
{
if ($root == NULL)
return NULL;
if (is_parent($root,$node))
{
return $root;
}
for($i = 0; $i < count($root->children_pages); $i++)
{
$prNode = $root->children_pages[$i];
//print (":$prNode->url");
$tmpNode = get_parent_n($root->children_pages[$i], $node);
if ($tmpNode != NULL)
{
//print("found!! = $tmpNode->url");
return $tmpNode;
}
}
return NULL;
}
function load_and_print_menu($file_name)
{
$root = load_tree($file_name);
print_menu($root,$root);
}
function print_menu($real_root, $root, $num_spaces=0)
{
if($real_root == NULL)
{
echo("print_menu() passed $real_root = NULL ");
return;
}
if ($root == NULL)
return;
$main_URL = "http://prestopnik.com/";
$test_string = $GLOBALS["SERVER_NAME"] . $GLOBALS["PATH_INFO"];
$thisURL = $GLOBALS["PATH_INFO"];
for($i = 0; $i < count($root->children_pages); $i++)
{
display_menu_node($real_root, $root->children_pages[$i],$num_spaces);
print_menu($real_root, $root->children_pages[$i],$num_spaces+3);
}
}
function getNodesFromURL($root, $url)
{
$nodes = array();
$tmpNodes = array();
if($root->url == $url)
{
$nodes[0] = new menu_tree_node();
$nodes[0] = $root;
}
for($i = 0; $i < count($root->children_pages); $i++)
{
$tmpNodes = getNodesFromURL($root->children_pages[$i], $url);
$nodes = array_merge($nodes,$tmpNodes);
}
return $nodes;
}
function display_menu_node($root, $node, $num_spaces)
{
$thisURL = $GLOBALS["PATH_INFO"];
$thisURL = substr($thisURL,1); //remove leading slash
$thisNodes = getNodesFromURL($root, $thisURL);
//print all pages that are children of the root
if( is_parent($root,$node))
{
print_node_link($node,$num_spaces);
return;
}
//thisNodes are the nodes that represent the page currently being viewed
//$node is the node that may be printed as part of the menu.
for($i = 0; $i < count($thisNodes); $i++)
{
//print all siblings of this page
if( is_sibling($root, $node, $thisNodes[$i]))
{
print_node_link($node,$num_spaces);
return;
}
//print all children of this page
if (is_child($thisNodes[$i],$node))
{
print_node_link($node,$num_spaces);
return;
}
//print all nodes in the path from the root to this page
if ( in_path($node,$root,$thisNodes[$i]))
{
print_node_link($node,$num_spaces);
return;
}
}
}
function is_parent($node_p, $node)
{
for($i = 0; $i < count($node_p->children_pages); $i++)
if($node_p->children_pages[$i] == $node)
return true;
return false;
}
function is_sibling($root, $node1, $node2)
{
$parent1 = get_parent_n($root,$node1);
$parent2 = get_parent_n($root,$node2);
if($parent1 == $parent2)
return true;
else
return false;
}
function is_child($node_p, $node_c)
{
for($i = 0; $i < count($node_p->children_pages); $i++)
if ($node_p->children_pages[$i] == $node_c)
return true;
return false;
}
function is_ancestor($node, $node_a)
{
return is_descendent($node_a, $node);
}
function is_descendent($node, $node_d)
{
if($node == NULL)
return false;
if($node_d == NULL)
return false;
if ($node == $node_d)
return true;
if(is_child($node,$node_d))
return true;
for($i = 0; $i < count($node->children_pages); $i++)
if( is_descendent($node->children_pages[$i], $node_d))
return true;
return false;
}
//returns true if $node_bottom is a descendent of $node and a $node_top is an ancestor of $node)
function in_path($node, $node_top, $node_bottom)
{
return (is_descendent($node,$node_bottom) && is_ancestor($node,$node_top));
}
function print_node_link($node,$num_spaces)
{
if( $node == NULL)
return;
$url = "http://" . $GLOBALS["SERVER_NAME"] . "/" . $node->url;
if ($node->hidden == false)
{
echo("<$node->font_size>");
for($j=0; $j<$num_spaces; $j++)
echo(" ");
print_unfinished_link($node->menu_name, $url);
echo("</$node->font_size>");
echo("</a>");
if ($node->icon != "none")
echo ("<img src=\"http://prestopnik.com/$node->icon\">");
echo("<br>\n");
}
}
function print_unfinished_link($name, $url)
{
$test_string = "http://" . $GLOBALS["SERVER_NAME"] . $GLOBALS["PATH_INFO"];
if($url == $test_string)
echo("$name");
else
{
$trimmed_name = ltrim($name);
$len1 = strlen($name);
$len2 = strlen($trimmed_name);
for($i=0; $i<($len1-$len2); $i++)
echo(" ");
echo("<a href=\"$url\">$trimmed_name");
}
}
function get_this_parent($root)
{
// $thisPage = $GLOBALS["SERVER_NAME"] . $GLOBALS["PATH_INFO"];
$thisPage = $GLOBALS["PATH_INFO"];
$thisPage = substr($thisPage,1); //remove leading slash
return get_parent($root, $thisPage);
}