PHP Menu Builder Class: Create nested menus using HTML link tags

Recommend this page to a friend!
  Info   View files Example   Screenshots Screenshots   View files View files (3)   DownloadInstall with Composer Download .zip   Reputation   Support forum   Blog    
Last Updated Ratings Unique User Downloads Download Rankings
2020-01-26 (5 months ago) RSS 2.0 feedNot yet rated by the usersTotal: 160 This week: 1All time: 8,715 This week: 327Up
Version License PHP version Categories
cmenu 1.0.0BSD License7HTML, PHP 5
Description Author

This class can create nested menus using HTML link tags.

It can create menus for a Web page that have a set of links to configurable target pages with given URLs, as well configurable link attributes.

The link target URL and the attribute values for each menu entry are passed as individual strings.

The menu entries may also be sub-menus that are passed to a class as previously created objects of this menu class.

  Performance   Level  
Name: phvandenberghe <contact>
Classes: 1 package by
Country: Belgium Belgium
Age: 50
All time rank: 413725 in Belgium Belgium
Week rank: 287 Up1 in Belgium Belgium Up

Class cMenu

The cMenu class allows creating menu and sub-menus and define the behaviour for specific pages.


The class can manage a cascade of menus with a pattern like:

<a href="page1">menu-1</a> | <a href="page2">menu-2</a> | <a href="page3">menu-3</a>

or like :

<div class="menu-group">
  <div><a id="page1" href="...">menu-1</a></div>
  <div><a id="page2" href="...">menu-2</a></div>
  <div><a id="page3" href="...">menu-3</a></div>

The cMenu->ouput() function generates the menus (adding the id, href, class,... attributes),
but also can change the class or onclick (or any attribute) for a specific id or a specific href.
This specific menu will thus be displayed differently (or with a click-event disabled)


This changes the menus taking into account that 'page2' must be active (and thus no more clickable) 
with a pattern like:

<a href="page1">menu-1</a> | <a href="javascript:void(0)" onclick="return false;">menu-2</a> | <a href="page3">menu-3</a>

or like:

<div class="menu-group">
  <div><a href="page1">menu-1</a></div>
  <div><span class="active">menu-2</span></div>
  <div><a href="page3">menu-3</a></div>

Quick user manual

We can start defining a list of menu-items (cMenu objects). 

$items[] = new cMenu( 'page-1', 'id=page-1|href=page-1.html' );
$items[] = new cMenu( 'page-2', 'id=page-2|href=page-2.html' );
$items[] = new cMenu( 'page-3', 'id=page-3|href=page-3.html' );
$menu    = new cMenu( $items, 'class=group-menu' );

Each cMenu object is created by defining the inner-text, then the attributes for the enclosing tag (if not specified, the first encloser tag will be <a> while next enclosing tags will be <div>).

The attributes can be declared as a json format '{"attr":"value","attr":"value"}' or as a simple ini-format 'attr=value|attr=value'.
Json is a strict format (requiring curly brackets and double-quote), while the ini-format can be unquoted and can have extra white spaces.

These menu-items are themselves placed inside a cMenu-object (to create a div encloser)

Basic usage #1:

No specific active page is defined, the menus will be rendered with a simple serie of <a> in a <div>. 
echo $menu->output();

This will output:

<div class="group-menu">
  <a id="page-1" href="page-1.html">page-1</a>
  <a id="page-2" href="page-2.html">page-2</a>
  <a id="page-3" href="page-3.html">page-3</a>

Basic usage #2:

Here we specify that the menu having the id 'page-2' must have the class "active", href and onclick event disabled.

echo $menu->output( 'page-2' );

This will output:

<div class="group-menu">
  <a id="page-1" href="page-1.html">page-1</a>
  <a id="page-2" href="javascript.void(0)" class="active" onclick="return false;">page-2</span>
  <a id="page-3" href="page-3.html">page-3</a>

Note that the output() function (without attributes-definition) uses the 'default' attribute-definition for the menu-item having the id 'page-2'.
'default' is equivalent to the attributes-definition 'addclass=active|href=javascript.void(0)|onclick=return false;'

Basic usage #3:

echo $menu->output( 'page-2', 'tag=span|class=active|onclick=return false;', 'addclass=active' );

As before, we specify that the menu having the id 'page-2' must have the class "active", a onclick event disabled and the tag <a> becomes <span>.
But, here we also specify that the encolsing tag (the parent containing the 'page-2') must also be changed by appending "active" to the class attribute.

This will output:

<div class="group-menu active">
  <a id="page-1" href="page-1.html">page-1</a>
  <span id="page-2" class="active" onclick="return false;">page-2</span>
  <a id="page-3" href="page-3.html">page-3</a>

Note: In the output function, the second attributes-definition will be applied the next enclosing tag (only).
If you have a cascade of several encosing tag (nested cMenu), you can nevertheless apply several times this attributes-definition.
See the last argument of the output() function to limit attributes-definition in case of several nested cMenu.  

Class definition

class cMenu
  public $inner; // array of texts or cMenu-objects
  public $outer; // outer tag attributes
  public function __construct($inner='', $outer='', $separator=' ')
  public function addInnersAttr($attr='')
  public function output($active='', $attr='default', $parentattr='',$maxparent=1)

Function __construct
$inner     : [array]  array of [string] or array of [cMenu] object. If a single [string] or [cMenu] object is provided, the constructor convert it automatically into an array of one [string] or [cMenu].
$outer     : [string] attributes-definition (using json-format, ini-format, or 'default') that must be use for the enclosing tag. After initialisation, $outer becomes an array of attributes=>values.
$separator : [string] separator to use between inners

Attributes definition
The tag that will enclose each inner is defined by an attributes-definition [string] describing the html-tag attributes and their values.
Any html attributes-values can be used. Most frequently used are : id, class, title, href, onclick, ...

Specific cMenu attributes
tag        : The html-tag to be used. If not specified, the first enclosing tag will be <a>...</a>, then <div>...</div> 
addclass   : Appends the value to the existing class-value (if current class is not defined or empty, addclass-value becomes the class-value)
activewith : Other id or href that must trigger an 'active'-output (can be several id or href, space separated). 

The attributes can be declared using json-format or ini-format (also using the 'default' keyword).

- Json format is '{"a":"x","b":"y"}'
  It uses comma separator, requires curly brackets and double-quotes.
  Example '{"id":"page2","href":"page-2.html","class":"menu"}'

- Ini format is 'a=x|b=y'
  It uses | as separator. Double-quotes are not mandatory. You can also add white spaces
  Example 'id=page2 | href=page-2.html | class=menu'

- 'default'. This keyword is designed to convert <a> into non-clickable html-tag 
  Attributes-definition 'default' is equivalent to 'href=javascript.void(0)|addclass=active|onclick=return false;'.

Note #1: In both Json- and ini-format, attribute-names are converted to lowercase.
This is because W3C recommends attribute-names in lowercase, while strict XHTML requires lowercase.

Note #2: Remember that menu items (html-tags <a>) may have onclick events.
In that case, don't forget to include in you attributes-definition for the output() function:
'onclick=return false;' (to disable the event) or 'onclick=' (to remove the event).

Function addInnersAttr
Allows adding attributes-values definition to each already defined cMenu 
$attr   : [string] attributes-definition using json-format or ini-format ('default' cannot be used here).

Function output
Allows rendering the menus and sub-menus
$active     : [string] id or href that triggers an 'active'-output. 'active'-output will use $attr (attributes-defintion) on top of already defined attributes.
$attr       : [string] attributes definition (using json-format, ini-format, or 'default') to be used in case of an 'active'-output for the first enclosing-tag.
$parentattr : [string] attributes definition (using json-format, ini-format, or 'default') to be used in case of an 'active'-output for the parent enclosing-tag.
$maxparent  : [integer] number of times the $parentattr attributes definition must be applied through the enclosing-tags cascade.

Rules for applying $attr (or $parentattr) attributes-definition: 
- if $attr contains new attributes, they are added;
- for existing attributes, the values are replaced;
- if $attr contains a tag-value (ex: tag=span), the enclosing html-tag is changed (ex: <span>...</span>);
- if $attr contains a addclass-value (ex: addclass=active), the value is appended to the class (ex: class="item active")
- Note that if the $attr changes a <a> tag into an other tag, the href attribute-value is automatically removed.

Advanced usages

How adding separators?

We just need a cMenu having a white space in a <span> block (or div block).
To create it, we use in the attributes-definition tag=span (other cMenu items are automatically in a <a> block).
Note that here, we also add a ' | ' (vertical-bar) between each item.

$items[] = new cMenu( 'page-1', 'id=page-1|href=page-1.html' );
$items[] = new cMenu( '      ', 'tag=span|class=separator' ); // cMenu('<span class="separator"> </span>') is allowed and equivalent.
$items[] = new cMenu( 'page-2', 'id=page-2|href=page-2.html' );
$items[] = new cMenu( 'page-3', 'id=page-3|href=page-3.html' );
$menu    = new cMenu( $items, 'class=groupmenu', ' | ' );

echo $menu->output();

This will output

<div class="groupmenu">
  <a id="page-1" href="page-1.html">page-1</a> | 
  <span class="separator">    </span> |
  <a id="page-2" href="page-2.html">page-2</a> | 
  <a id="page-3" href="page-3.html">page-3</a>

Advanced example: Nested menus

Here we will first build a more advanced cascade of <div> blocks.
In the second part of this example, we will see the effect of adding argument 'page-2' in the output function:
We want to append the class 'active' to all levels in the div cascade.
We also want that the inner block <a> becomes a disabled <span> (and the href attribute-value will thus be removed automatically).

Here, instead of repeating 'class=item-menu' in each definition,
we use the addInnersAttr() function after the definition step to add this attribute in each inner block.

$items[] = new cMenu( new cMenu ('page-1', 'id=page-1|href=page-1.html'), 'id=item-1' );
$items[] = new cMenu( new cMenu ('page-2', 'id=page-2|href=page-2.html'), 'id=item-2' );
$items[] = new cMenu( new cMenu ('page-3', 'id=page-3|href=page-3.html'), 'id=item-3' );
$menu    = new cMenu( $items, 'id=group-1|class=group-menu' );
$menu->addInnersAttr( 'class=item-menu' );
$menu    = new cMenu( $menu, 'id=main-menu' );

Using the output function without argument

echo $menu->output();

This will output a cascade of blocks (without any menu item activated) 

<div id="main-menu">
  <div id="group-1" class="group-menu">
    <div id="item-1" class="item-menu"><a id="page-1" href="page-1.html">page-1</a></div>
    <div id="item-2" class="item-menu"><a id="page-2" href="page-2.html">page-2</a></div>
    <div id="item-3" class="item-menu"><a id="page-3" href="page-3.html">page-3</a></div> 

Using the output function with arguments

echo $menu->output( 'page-2.html', 'tag=span', 'addclass=active', 2 );

This will output

<div id="main-menu">
  <div id="group-1" class="group-menu active">
    <div id="item-1" class="item-menu"><a id="page-1" href="page-1.html">page-1</a></div>
    <div id="item-2" class="item-menu active"><span id="page-2">page-2</span></div>
    <div id="item-3" class="item-menu"><a id="page-3" href="page-3.html">page-3</a></div> 

In this example, the first argument of the output() function was the href value 'page-2.html' (instead of the id) to identify the active item-menu.
We also used 2 as last argument of the output() function to specify that the 'addclass=active' must be used in 2 levels of the nested menus.

Several active pages (or sub-pages) for one menu item

In the following example, we add the 'activewith' attribute defining the other pages (sub-pages) that must trigger an 'active'-output.
We have 3 menu-items and we want the 'Login' item displayed as active also when the user go to sub-pages like register, reset password or unregister. 

$items[] = new cMenu( 'Home',  'id=page-1 | href=home.html' );
$items[] = new cMenu( 'News',  'id=page-2 | href=news.html' );
$items[] = new cMenu( 'Login', 'id=page-3 | href=login.html | activewith=register.html resetpassword.html unregister.html' );
$menu    = new cMenu( $items, 'class=menu' );

echo $menu->output( 'login.html', 'tag=span|class=active' );

This will output:
<div class="menu">
  <a id="page-1" href="home.html">Home</a>
  <a id="page-2" href="news.html">News</a>
  <span id="page-3" class="active">Login</span>

And we will have the same output (the third menu item will be active)
when using output('register.html'), output('resetpassword.html') or output('unregister.html').
This means that with one menu definition, the menu can be included in several pages (home, news, login, register, resetpassword, unregister).

How to access attribute values?

A cMenu is composed of two parts: the inner and the outer (the attributes-definition).
The inner is always an array, even if you declare a simple string, so you can access it with: inner[0] 
The outer is always an array of attribute-names and attribute-values.

For a menu item like:

$items[0] = new cMenu( 'page-1', 'href=page-1.html | title=First page' );
$items[1] = new cMenu( 'page-2', 'href=page-2.html | title=Second page' );
$menu = new cMenu( $items, 'id=group-1' );

You can access the inner value or the outer attribute with:

echo $items[0]->inner[0]; // page-1
echo $items[0]->outer['title']; // Frist page

In a cascade of cMenu(s), from the enclosing menu:

echo $menu->inner[0]->inner[0]; // page-1
echo $menu->inner[0]->outer['title']; // First page
  • class_cmenu.jpg
  Files folder image Files  
File Role Description
Accessible without login Plain text file class_cmenu.txt Doc. Class cMenu Documentation
Accessible without login Plain text file class_cmenu_test.php Example Test sample for cMenu
Plain text file class_cmenu.php Class Class cMenu

 Version Control Unique User Downloads Download Rankings  
This week:1
All time:8,715
This week:327Up

For more information send a message to info at phpclasses dot org.