I'm struggling a bit with some of my code...I have an array of my menu layout:
$navigationMenu = [
SECTION_OVERVIEW => [
'hide' => TRUE,
'id' => 'overview_button',
'order' => 0,
'content/overview.php' => [
SECTION_CABINET => [
'hide' => TRUE,
'id' => 'cabinet_button',
'content/cabinet.php' => [
SECTION_AMP_PSU => [
'hide' => TRUE,
'id' => 'amps_psu_button',
'ampNoIndicator' => TRUE,
'order' => 3,
'content/amp_psu.php' => [
SECTION_AMPS_OFF => [
'hide' => false,
'id' => 'amps_off_button',
'ampNoIndicator' => TRUE,
'class' => 'red',
'order' => 4,
],
SECTION_AMPS_ALARMS => [
'id' => 'amps_alarms',
'ampNoIndicator' => TRUE,
'order' => 5,
'content/amp_alarms.php' => NULL
]
]
],
'POOP' => [
'hide' => false,
'id' => 'amps_psu_button',
'ampNoIndicator' => TRUE,
'order' => 3,
'content/amp_psu.php' => NULL
]
]
]
]
],
SECTION_SYSCONFIG => [
'id' => 'sysConfig',
'order' => 1,
'content/system_configuration.php' => NULL,
],
SECTION_SYS => [
'id' => 'system',
'order' => 2,
'content/system.php' => NULL,
],
BACK_BUTTON => [
'id' => 'backBtn',
'order' => 100,
'prev' => NULL,
],
RESCAN_BUTTON => [
'id' => 'rescan',
'order' => 101,
]
];
And I have a class that generates the menu.
class Navigation {
private $doc;
private $xpath;
private $rootNode;
/**
* Initialize navigation.
*/
public function __construct() {
global $navigationMenu;
$menu = $navigationMenu;
$this->doc = new DOMDocument();
$this->doc->appendChild($this->buildMenu($menu));
$this->xpath = new DOMXpath($this->doc);
}
/**
* Build Menu
* @param $menu array see $menu array at the begining of the class
* @param $depth int maintain a depth count for nested UL menu
* @param $nestingUL bool false (default): does not output menu in a nested UL config
* true: nested UL config
*
* @return DOMElement function returns DOM element of UL
*/
private function buildMenu($menu, $depth = 0, $nestingUL = false) {
$ulNode = $this->doc->createElement('ul');
if (!$depth) {
$ulNode->setAttribute('id', 'primary-nav');
$this->rootNode = $ulNode;
}
$this->appendtoNode($menu, $depth, $nestingUL, $ulNode);
return $ulNode;
}
/**
* Append menu items to a node (either UL or LI)
*
* @param $menu array array of menu items list
* @param $depth array depth count for nested UL menu
* @param $nestingUL bool false: no nesting UL; true: nesting UL
* @param $node DOMElement passing node variable
* @param $showElementOverride bool override skipping hidden elements
*
*/
private function appendtoNode($menu, $depth, $nestingUL, $node, $showElementOverride = false) {
foreach ($menu as $itemText => $item) {
if ((empty($item['hide']) || !($item['hide']) || $showElementOverride)) {
$node->appendChild($this->buildMenuItem($itemText, $item, $depth, $nestingUL));
}
else if (array_key_exists('hide', $item) && $item['hide']) {
$newArr = $this->hiddenArraySkipNext($menu);
$this->appendtoNode($newArr, $depth, $nestingUL, $node, $showElementOverride);
}
}
return;
}
/**
* Build menu item.
*
* @param $itemText string (button/menu label)
* @param $item array (button modifiers array)
* @param $depth int (maintaining depth count (only for creating a nested UL))
* @param $nesting bool (true if nesting is required; false if no nesting)
*/
private function buildMenuItem($itemText, $item, $depth, $nesting) {
$id = '';
$spanclassAttr = array('navButton-color');
$order = '';
$url = '';
$ampNo = '';
$childMenu = false;
// prepare node structure
$liNode = $this->doc->createElement('li');
$spanNode = $this->doc->createElement('span');
$glareNode = $this->doc->createElement('span'); // spare span tag used to carry the glare class attribute
$pNode = $this->doc->createElement('p');
// initialize node attributes
$liNode->setAttribute('class', 'navButton');
$glareNode->setAttribute('class', 'glare'); // spare span tag with "glare" class attribute
// iterate item properties
foreach ($item as $key => $value) {
switch ($key) {
case 'hide':
break;
case 'id':
$id = $value;
break;
case 'ampNoIndicator':
$ampNo = $value;
break;
case 'class':
$spanclassAttr[] = $value;
break;
case 'order':
$order = $value;
break;
default:
$url = $key;
$childMenu = $value;
}
}
// map iterated items to nodes
$liNode->setAttribute('id', $id);
if ($spanclassAttr) {
$spanNode->setAttribute('class', implode(' ', $spanclassAttr));
}
$pNode->nodeValue = $itemText;
$spanNode->appendChild($pNode);
$liNode->appendChild($spanNode);
$liNode->appendChild($glareNode);
if (is_array($childMenu) && $nesting) {
$liNode->appendChild($this->buildMenu($childMenu, $depth + 1, $nesting));
}
else if (is_array($childMenu) && !$nesting) {
$this->appendtoNode($childMenu, $depth, $nesting, $liNode);
}
return $liNode;
}
/**
* Iterating menu array
*
* @param $item menu array
* @return array | bool return the nested array, else return false
*/
private function hiddenArraySkipNext($arr) {
$output = $arr;
foreach ($arr as $tempArr) {
if (array_key_exists('hide', $tempArr) && $tempArr['hide']) {
foreach ($tempArr as $key => $value) {
if (is_array($value)) {
$output = $value;
$this->hiddenArraySkipNext($output);
}
}
}
else if (!array_key_exists('hide', $tempArr) || (array_key_exists('hide', $tempArr) && !$tempArr['hide'])) {
return $output;
}
}
return $output;
}
/**
* Get menu.
*/
public function getMenu() {
return $this->doc->saveHTML();
}
}
So the purpose of the class is to be able to generate either a nested or non-nested UL of the $navigationMenu (see the use of the $nestingUL/$nesting parameters I have through buildMenu, appendtoNode, etc). I also want to have the ability to override any "hide" keys in my $navigationMenu (see $showElementOverride in the appendtoNode() function). The menu displays as planned when the array is in the configuration I have set up. However, if I ever wanted to expand on the menu, I run into issues.
In this array configuration, below, my SECTION_AMPS_OFF and SECTION_AMPS_ALARMS are output twice.
$navigationMenu = [
SECTION_OVERVIEW => [
'hide' => TRUE,
'id' => 'overview_button',
'order' => 0,
'content/overview.php' => [
SECTION_CABINET => [
'hide' => TRUE,
'id' => 'cabinet_button',
'content/cabinet.php' => [
SECTION_AMP_PSU => [
'hide' => TRUE,
'id' => 'amps_psu_button',
'ampNoIndicator' => TRUE,
'order' => 3,
'content/amp_psu.php' => [
SECTION_AMPS_OFF => [
'hide' => false,
'id' => 'amps_off_button',
'ampNoIndicator' => TRUE,
'class' => 'red',
'order' => 4,
],
SECTION_AMPS_ALARMS => [
'id' => 'amps_alarms',
'ampNoIndicator' => TRUE,
'order' => 5,
'content/amp_alarms.php' => NULL
]
]
],
'POOP' => [
'hide' => true,
'id' => 'amps_psu_button',
'ampNoIndicator' => TRUE,
'order' => 3,
'content/amp_psu.php' => NULL
]
]
]
]
],
SECTION_SYSCONFIG => [
'id' => 'sysConfig',
'order' => 1,
'content/system_configuration.php' => NULL,
],
SECTION_SYS => [
'id' => 'system',
'order' => 2,
'content/system.php' => NULL,
],
BACK_BUTTON => [
'id' => 'backBtn',
'order' => 100,
'prev' => NULL,
],
RESCAN_BUTTON => [
'id' => 'rescan',
'order' => 101,
]
];
And when I have the menu configured as below, I get a fatal error about the allowed memory size exhausted.
$navigationMenu = [
SECTION_OVERVIEW => [
'hide' => TRUE,
'id' => 'overview_button',
'order' => 0,
'content/overview.php' => [
SECTION_CABINET => [
'hide' => TRUE,
'id' => 'cabinet_button',
'content/cabinet.php' => [
SECTION_AMP_PSU => [
'hide' => TRUE,
'id' => 'amps_psu_button',
'ampNoIndicator' => TRUE,
'order' => 3,
'content/amp_psu.php' => [
SECTION_AMPS_OFF => [
'hide' => true,
'id' => 'amps_off_button',
'ampNoIndicator' => TRUE,
'class' => 'red',
'order' => 4,
],
SECTION_AMPS_ALARMS => [
'id' => 'amps_alarms',
'ampNoIndicator' => TRUE,
'order' => 5,
'content/amp_alarms.php' => NULL
]
]
],
'POOP' => [
'hide' => false,
'id' => 'amps_psu_button',
'ampNoIndicator' => TRUE,
'order' => 3,
'content/amp_psu.php' => NULL
]
]
]
]
],
SECTION_SYSCONFIG => [
'id' => 'sysConfig',
'order' => 1,
'content/system_configuration.php' => NULL,
],
SECTION_SYS => [
'id' => 'system',
'order' => 2,
'content/system.php' => NULL,
],
BACK_BUTTON => [
'id' => 'backBtn',
'order' => 100,
'prev' => NULL,
],
RESCAN_BUTTON => [
'id' => 'rescan',
'order' => 101,
]
];
I'm pretty sure the solution is not expanding the memory limit. The ultimate goal is to be able to loop through the array and spit out any unhidden nav menu buttons (either nested or non-nested UL), as long as $showElementOverride is not set TRUE. If a parent array is hidden, the child may be shown, as long as it's not a nested UL. I haven't tackled that issue yet, as I've been trying to tackle the hiddenArraySkipNext() function. I've been struggling with this for the past couple of days, so a fresh set of eyes would definitely be appreciated!