Hierarchical Menu

I've been coding on experts exchange, testing myself. Here's some relevant code for one of the answers.

They're classes that have been used to generate hierarchical menus.


<?php
/**
 * @package menu
 * @author John Kawakami 
 * @link http://www.slaptech.net Developed by The Slaptech Collective
 * @copyright 2006 Public Domain
 * @version $Id: Menu.class.php 97 2006-07-27 10:43:40Z johnk $
 *
 * Library to help create hierarchical menus.
 * Use MenuRenderer to display the menus.
 *
 * 
 * $menuBar = new Menu('MenuBar');
 * $fileMenu = $menuBar->addMenu( 'File' );
 * $fileMenu->addUrl( 'Open', '/admin/foo.php' );
 * $fileMenu->addUrl( 'Save', '/admin/bar.php' );
 * $fileMenu->addDivider();
 * $fileMenu->addUrl( 'Close', '/admin/baz.php' );
 * 
 */

include_once('ACL.class.php');
 
class Menu
{
	var $acl;
	var $menu;
	var $menuHash; //fixme - combine these two structures into one
	var $name;
	var $description;
	var $icon;
	var $url;
	var $highlight;

	function Menu( &$acl, $name, $description='', $icon='', $url='' )
	{
		$this->acl			=& $acl;
		$this->menu 		= array();
		$this->name 		=& $name;
		$this->description 	=& $description;
		$this->icon 		=& $icon;
		$this->url 			=& $url;
		$this->highlight	= false;
	}
	function addObject( &$obj )
	{
		array_push( $this->menu, &$obj );
	}
	function addNamedObject( $name, &$obj )
	{
		$this->menuHash[$name] =& $obj;
		$this->addObject( &$obj );
	}
	/**
	 * Utility function.
	 */
	function &addMenu( &$acl, $name, $description='', $icon='', $url='' )
	{
		$new = new Menu( &$acl, $name, $description, $icon, $url );
		$this->addObject( $new );
		return $new;
	}
	function &getNamedObject( $name )
	{
		return $this->menuHash[$name];
	}
	function &addUrl( &$acl, $name, $url, $description='', $popup='' )
	{
		$new = new UrlMenuItem( &$acl, $name, $url, $description, $popup );
		$this->addObject( $new );
		return $new;
	}
	function &addDivider()
	{
		$new = new DividerMenuItem();
		$this->addObject( $new );
		return $new;
	}
	function select( $name )
	{
		for( $i=0; $i < count($this->menu); $i++ )
		{
			if ($this->menu[$i]->name==$name)
			{
				return $this->menu[$i];
			}
		}
	}
	function rewind()
	{
		reset($this->menu);
	}
	function &next()
	{
		// check the acl to see if this user can see the next item - fixme
		list( $index, $obj ) = each( $this->menu );
		return $obj;
	}
	function disable()
	{
		$this->enabled = false;
	}
	function enable()
	{
		$this->enabled = true;
	}
	function highlight()
	{
		$this->highlight = true;
	}
}

class UrlMenuItem extends Menu
{
	var $name;
	var $url;
	var $description;
	var $popup;

	function &UrlMenuItem( &$acl, $name, $url, $description='', $popup='' )
	{
		$this->acl			=& $acl;
		$this->name 		=& $name;
		$this->url 			=& $url;
		$this->description 	=& $description;
		$this->popup 		=& $popup;
		return $this;
	}

}
class DividerMenuItem extends Menu
{
	function DividerMenuItem()
	{
		$this->name = 'divider';
	}
}


?>


The menus are rendered via another class:


<?php
/**
 * @package menu
 * @author John Kawakami 
 * @link http://www.slaptech.net Developed by The Slaptech Collective
 * @copyright 2006 Public Domain
 * @version $Id: MenuRenderer.class.php 92 2006-07-27 09:13:10Z johnk $
 *
 * Rendering classes for menus.  These are kind of like "templates"
 * for menus, except they can render hierarchies, portions of menus,
 * etc.  There's HTML code in here.  Yuk.
 */

/**
 * Rendering interface.  Each renderer must implement the
 * url, divider, and menu methods.  The menu() method is the heart
 * of the renderer.
 */
class MenuRenderer
{
	function MenuRenderer() {}
	function divider() {}
	function url() {}
	function menu() {}
	function item( $obj ) 
	{
		return $obj->name;
	}
}

/**
 * Renders a menu as an html unordered list (UL).
 */
class HtmlMenuRenderer extends MenuRenderer
{
	function HtmlMenuRenderer() {}

	function menu( &$menu, $indent=0 )
	{
		$t = str_repeat( ' ', $indent );
		$m =& $menu;
		$m->rewind();
		if ($m->name != '@')
			$output .= "$t
  • "; if ($m->name != '@') { if ($m->url) $output .= "url'>"; if ($m->icon) { $output .= "icon' border='0' />"; $output .= "
    "; } $output .= $m->name."\n"; if ($m->url) $output .= '
    '; } $output .= "$t
      \n"; while( $obj =& $m->next() ) { $class = strtolower(get_class($obj)); // echo "$class
      "; switch( $class ) { case 'apmenu': case 'menu': $output .= $this->menu( $obj, $indent+4 ); break; case 'urlmenuitem': $output .= $this->url( $obj, $indent+4 ); break; case 'dividermenuitem': $output .= $this->divider( $obj, $indent+4 ); break; default: $output .= $this->item( $obj, $indent+4 ); break; } } $output .= "$t
    \n"; if ($m->name != '@') $output .= "$t
  • \n"; return $output; } function divider( &$obj, $indent=0 ) { $t = str_repeat( ' ', $indent ); return "$t
  • -----------
  • \n"; } function url( &$obj, $indent=0 ) { $t = str_repeat( ' ', $indent ); return "$t
  • url'>$obj->name
  • \n"; } }