|
ulf-wendel.de   
|
|
 Home < PHP Projekte  < Menu 3  < Tutorial    |       |  
Print Version    
---
|
|
 Home 
 PHP Projekte 
    PHPDoc 
    Forms 
    IT[X] Template 
    Userland Cache 
    Gtext 
    Menu 3 
       Tutorial 
       Browser 
       XML/XSLT Menü 
    Columbo 
 PHP Schulung 
 Technik der Site 
 Büchertipps 
 Fotografie 
 Airbrush 
 Kontakt 
 Stuff 

Statische Navigationsstrukturen dynamisch dargestellt

Die Erstellung von Navigationselementen und ihre Einbindung in ein HTML Dokument kann ein zeitraubender Prozeß sein, der besonders dann zur Herausforderung wird, wenn die Navigationsstruktur einer umfangreichen Präsentation geändert wird. In dem Maße in dem Template Klassen die Arbeit vereinfachen können, gelingt dies auch den Menü Klassen, sofern sie überhaupt eingesetzt werden können. Aufwendige Effekte lassen sich kaum erzielen sehr wohl aber einfache Darstellungen, die nicht minder zur Benutzbarkeit einer Präsentation beitragen.

Der Plan

Schritt für Schritt sollen die Features von Menu 3 an einem praktischem Beispiel dargestellt werden. Ziel ist es, eine einfache Menüstruktur für eine fiktive PEAR Website zu erstellen und das Layout anzupassen. Anschließend wird versucht, die Klasse so zu erweitern, daß auch mehrsprachige Navigationselemente möglich werden.

Vorbereitungen

Eine aktuelle Version von Menu 3 befindet sich im PEAR CVS im Verzeichnis Experimental/Html/. Vom Verzeichnisnamen sollte man sich nicht abschrecken lassen, mir sind keine Bugs mehr bekannt. Einzig aufgrund der fehlenden englischen Dokumentation liegt die Klasse noch im Experimental Verzeichnis.

Die Navigationsstruktur für die geplante Seite soll wie folgt aussehen.

  • Home
  • About PEAR
  • Download
  • Installation
  • Inside PEAR
    • CVS
    • Coding Guidelines
  • Packages
Basierend auf dieser (Baum-)Struktur sollen auch alle anderen Darstellungsformen erzeugt werden. Das Layout wird vom Mäuseschupser vorgegeben, im folgenden wird gezeigt, wie Anpassungen zu machen sind, die Anpassung selbst wird jedoch dem Leser als Übung überlassen.

Exkurs: Baumstrukturen in PHP

Baumstrukturen lassen sich in PHP nicht wie C/C++ oder Java implementieren, da PHP keine Zeiger oder gar Adreßoperationen kennt. Wer versucht, die klassischen Algorithmen in PHP zu formulieren, wird schnell aufgeben.

Eine einfach verlinkte Liste, Basisbaustein aller komplexeren Formen darunter auch Bäume, bedient sich in C folgender Datenstruktur.

typedef struct node *Ref;
typedef struct node {
  int value;
  Ref next;
} NODE;
Grafisch dargestellt, entspricht die resultierende Liste folgendem Bild.

0 *
->
1 *
->
2 *
-> ...
n *
->
NULL

Jeder Zeiger in C verweist auf eine bestimmte Adresse (Speicherstelle) im Hauptspeicher des Rechners, im obigem Beispiel ist "next" ein Zeiger, sein Wert, ist die Speicheradresse auf die er verweist. Da PHP keine Zeiger kennt, beginnt man damit den Hauptspeicher unter Verwendung einer Liste (Array) zu simulieren. Jeder Eintrag in der Liste entspricht einer Speicherzelle im Hauptspeicher. Die dargestellten Lücken (1, 2 -> 7) ergeben sich nach wenigen Lösch- und Einfügeoperationen.

C - Speicherorganisation   PHP - Simulation mittels Liste
Maximale Speicheradresse    count($node_list)
... ...    ... ...
Adresse: 7
Node 3
value: 3 next: keiner/NULL
   $node_list[7]
Node 3
value: 3 next: keiner/NULL
... ...    ... ...
Adresse: 2
Node 2
value: 2 next: steht in 7
   $node_list[2]
Node 2
value: 2 next: steht in 7
Adresse 1
Node 1
value: 1 next: steht in 2
   $node_list[1]
Node 1
value: 1 next: steht in 2

Erfahrene PHP Anwender ahnen bereits, welcher Verwaltungsaufwand entsteht, wenn ein Knoten nicht nur einfach, sondern zwei oder gar dreifach verlinkt ist. Wer es sich nicht vorstellen kann, dem würde ich danken, wenn er zu Demonstrationszwecken Menu ADT komplettiert.

Auch wenn es "grausam" aussieht und PHPDoc den Ruf des größten Hashtest Skripts für PHP eingebracht hat, plädiere ich für mehrdimensionale Hashes als Datenstruktur für Bäume in PHP. Hashes können leicht aufgebaut, ausgelesen und modifiziert werden. Die zahlreichen Arrayfunktionen, die besonders mit PHP 4 ausgebaut wurden, lassen keinen Wunsch offen.

Menüstrukturen definieren

Die Definition der Menüstruktur ist praktisch selbsterklärend. Es gilt lediglich zu beachten, daß jeder Eintrag im Hash einen eindeutigen Key verwendet, da diese zum Aufbau einer internen Lookup Table verwendet werden. Ob numerische Keys benutzt werden, die die Baumstruktur wiederspiegeln oder Strings ist nicht von Bedeutung.
Menüstruktur Top

<?php

var $menu = array(

              
=> array(
                      
"url"   => "/",
                      
"title" => "Home"
                    
),
                    
              
=> array(
                      
"url"   => "about/",
                      
"title" => "About PEAR"
                    
),

              
=> array(
                      
"url"   => "download/",
                      
"title" => "Download"
                    
),

              
=> array(
                      
"url"   => "installation/",
                      
"title" => "Installation"
                    
),

              
=> array(
                      
"url"   => "inside/",
                      
"title" => "Inside PEAR",
                      
"sub"   => array(
                      
                                    
41 => array(
                                              
"url"   => "inside/cvs/",
                                              
"title" => "CVS"
                                            
),
                                             
                                    
42 => array(
                                              
"url"   => "inside/guide/",
                                              
"title" => "Coding Guidelines"
                                            
)
                                            
                                  )
                    ),
              
              
=> array(
                      
"url"       => "packages/",
                      
"title"     => "Packages"
                    
)

            );
?>            
    

Die Daten werden in der Klassenvariable $menu abgelegt, um sie nicht bei jedem Skriptlauf dem Objekt neu zuweisen zu müssen. Wer mit dynamischen Strukturen arbeitet und auf eine Zuweisung zur Laufzeit angewiesen ist, der kann die Methode void setMenu(array $menu) verwenden.

Für die weitere Konfiguration von Menu 3 ist es notwendig, einige Methoden zu überschreiben, weshalb eine Ableitung von der Basisklasse erstellt wird.
Ableitung zur Anpassung Top

<?php
  
// Basisklasse ggf. einbinden
  
require_once($DOCUMENT_ROOT "/php/Menu.php");
  
  class 
pear_menu extends menu {
  
    var 
$menu = ...
    
  }
?>
    

Der erste Kontakt

Nach diesen Vorbereitungen ist es möglich, Menu 3 in Aktion zu sehen. Ein kleines Skript bindet die eigene Ableitung ein, erzeugt ein Objekt und gibt die Navigation in allen möglichen Darstellungsformen aus.
Testskript Top

<?php
  
require_once($DOCUMENT_ROOT "/php/pear_menu.php");
  
  
// Objekt erzeugen
  
$m = new pear_menu;
  
  
// Alle Darstellungsmodi ausgeben
  
$modes = array("sitemap""tree""rows""urhere""prevnext");
  foreach (
$modes as $k => $mode)
    
printf("mode: %s\n%s\n\n"$mode$m->get($mode));
    
?>
Ausgabe Top
mode: sitemap
Home
About PEAR
Download
Installation
Inside PEAR
   CVS
   Coding Guidelines
Packages
mode: tree
Home
About PEAR
Download
Installation
Inside PEAR
   CVS
   Coding Guidelines
Packages
mode: rows
HomeAbout PEARDownloadInstallationInside PEARPackages
CVSCoding Guidelines
mode: urhere
Inside PEAR >> CVS
mode: prevnext
<< Inside PEAR^ Inside PEAR ^Coding Guidelines >>

Anpassung des Aussehens

Menu 3 durchläuft den Hash mit der Menüstruktur rekursiv und ruft drei Methoden auf, die das Rendering übernehmen: string getStart(), string getEnd(), string getEntry(array $node, integer $level, integer $node_type). getStart() wird immer vor und getEnd() nach der Traversierung des Hashes aufgerufen. Meist werden Sie benutzt, um eine HTML Tabelle zu beginnen oder zu schließen. Eine Ausnahme bildet die Darstellungsform "rows", hier werden die Methoden während der Traversierung aufgerufen, dies ändert jedoch nichts an der Bedeutung der Methoden. Einträge im Menü werden durch die Methode getEntry() dargestellt. Es werden alle Daten des Eintrags (url, title, ...), die Tiefe im Navigationsbaum und eine Typangabe (aktiv, inaktiv, ...) übergeben.

Zur Anpassung werden die beschriebenen Methoden in der Klasse pear_menu überschrieben, zunächst also dorthin kopiert. Die Anpassung selbst besteht in der Veränderung des öffnenden HTML <table>-Tags. Da PHPDoc Inline Dokumentation vererbt, braucht diese nicht erneut niedergeschrieben werden.


Ableitung zur Anpassung Top

<?php
  
// Basisklasse ggf. einbinden
  
require_once($DOCUMENT_ROOT "/php/Menu.php");
  
  class 
pear_menu extends menu {
  
    var 
$menu = ...
    
    function 
getStart() {
    
        
$html "";
        switch (
$this->menu_type) {
            case 
"rows":
            case 
"prevnext":
                
$html .= '<table cellpadding="2" cellspacing="2" border><tr>';
                break;
                
            case 
"tree":
            case 
"urhere":
            case 
"sitemap":
                
$html .= '<table cellpadding="2" cellspacing="2" border>';
                break;
        }
        
        return 
$html;
    } 
// end func getStart


    
function getEnd() {

        
$html "";
        switch (
$this->menu_type) {
            case 
"rows":
            case 
"prevnext":
                
$html .= '</tr></table>';
                break;
            
            case 
"tree":
            case 
"urhere":
            case 
"sitemap":
                
$html .= '</table>';
                break;
        }
        
        return 
$html;
    } 
// end func getEnd     
    
    
    
function getEntry(&$node$level$item_type) {

        
$html "";
        
        if (
"tree" == $this->menu_type) {
            
// tree menu
            
$html .= '<tr>';
            
$indent "";
            if (
$level
                for (
$i 0$i $level$i++)
                    
$indent .= '&nbsp;&nbsp;&nbsp;';
        }
        
        
// draw the <td></td> cell depending on the type of the menu item
        
switch ($item_type) {
            case 
0:
                
// plain menu item
                
$html .= sprintf('<td>%s<a href="%s">%s</a></td>',
                                    
$indent,
                                    
$node["url"],
                                    
$node["title"]
                                 );
                break;
                
            case 
1:
                
// selected (active) menu item
                
$html .= sprintf('<td>%s<b>%s</b></td>'
                                   
$indent,
                                   
$node["title"]
                                );
                break;
                
            case 
2:
                
// part of the path to the selected (active) menu item
                
$html .= sprintf('<td>%s<b><a href="%s">%s</a></b>%s</td>',
                                    
$indent,
                                    
$node["url"],
                                    
$node["title"],
                                    (
"urhere" == $this->menu_type) ? " &gt;&gt; " ""
                                
);
                break;
                
            case 
3
                
// << previous url
                
$html .= sprintf('<td>%s<a href="%s">&lt;&lt; %s</a></td>',
                                    
$indent,
                                    
$node["url"],
                                    
$node["title"]
                                );
                break;

            case 
4:
                
// next url >>
                
$html .= sprintf('<td>%s<a href="%s">%s &gt;&gt;</a></td>',
                                    
$indent,
                                    
$node["url"],
                                    
$node["title"]
                                );
                break;

            case 
5:
                
// up url ^^
                
$html .= sprintf('<td>%s<a href="%s">^ %s ^</a></td>',
                                    
$indent,
                                    
$node["url"],
                                    
$node["title"]
                          );
                break;
                
        }
            
        if (
"tree" == $this->menu_type)
            
$html .= '</tr>';

        return 
$html;
    } 
// end func getEnty
    
  
}
?>
    

Ein etwas längeres switch() { ... } bildet das Herzstück der Methode getEntry(). Je nach Eintragstyp wird eine leicht andere Darstellungsform gewählt. Wer sehr viele Anpassungen vornehmen, muß tut gut daran, je nach Darstellungsform, ($this->menu_type) in andere Methoden zu verzweigen, um die Übersichtlichkeit zu wahren.

Für den Parameter $item_type können fünf Werte auftreten.

getEntry() - $item_type
0 "Normaler", nicht angewählter Eintrag. alle Darstellungsformen
1 "Aktiver", angewählter Eintrag. Der Menüpunkt beschreibt die angezeigte Seite. alle Darstellungsformen
2 Der Eintrag gehört zum Pfad des aktuellen Eintrags. 1 und 1.1 gehören zum Pfad zum Eintrag 1.1.1 alle Darstellungsformen
3 "Vorheriger" Eintrag des aktuellen Menüpunkts. 1 liegt vor 2. ausschließlich "prevnext"
4 "Nächster" Eintrag des aktuellen Menüpunkts. 3 liegt nach 2. ausschließlich "prevnext"
5 Eintrag "über" dem aktuellen Menü. 1 liegt über 1.2. ausschließlich "prevnext"

Wie findet Menu 3 den aktuellen Menüpunkt?

Der aktuelle Menüpunkt wird durch einen Vergleich der "url" Angabe im Menühash mit dem Rückgabewert von string getCurrentURL() ermittelt. Durch Überschreibung der Methode kann statt des voreingestellten Vergleichs mit $PHP_SELF z.B. ein Vergleich mit $REQUEST_URI durchgeführt werden, was die Verwendung von URLs in der Form http://www.example.com/?index ermöglicht.

Existiert kein Eintrag für den Rückgabewert von getCurrentURL(), wird der zurückgelieferte String so lange gekürzt, bis ein Eintrag gefunden wird. Dies erlaubt es, Seiten anzulegen, die nicht explizit im Menü aufgeführt werden. Wird die Seite angezeigt, aktiviert Menu 3 den nächsten "Hauptpunkt".

Ein Beispiel verdeutlicht das Gesagte. Im Menühash sei ein Eintrag "url" => "/example/" enthalten. Im Verzeichnis /example/ liegen die Seiten index.php, foo.php und bar.php, wobei die letzen beiden Dateien nicht im Menü eingetragen sind. Bei Aufruf der Seite foo.php liefert getCurrentURL() den Wert "/example/foo.php". Menu 3 findet keinen Eintrag und sucht den nach einer Alternative, dem "Hauptpunkt".

Suche nach Alternativen
/example/foo.php != /example/
/example/foo.ph != /example/
/example/foo.p != /example/
... != /example/
/example/f != /example/
/example/ == /example/

Das Verfahren der Verkürzung ist PHPLib Nutzern wohlbekannt, menu.inc bietet einen ähnlichen Mechanismus. Sollte die Verzeichnisstruktur nicht der Menüstruktur entsprechen, sind unliebsame Überraschungen leider vorprogrammiert.

Frames und Mehrsprachigkeit

Unterstützung für Frames, Mehrsprachigkeit und vieles mehr kann leicht hinzugefügt werden. Links in Framesets benötigen oft ein target Attribut, mehrsprachige Navigationen verlangen, daß mehrere Beschritungen (title) für einen Eintrag gespeichert werden. Diese Daten sind dem Menühash hinzuzufügen.

Menu 3 benötigt zur Funktion die Elemente "url" und (optional) "sub" in einem Eintrag (Node) im Hash (Baum). Neben diesen Pflichtelementen können beliebig viele weitere Angaben enthalten sein, selbst "title" ist eine solche, optionale Angabe. Bei Bedarf können "target" und weitere Daten im Node abgelegt werden, um sie durch getEntry() auszulesen. Vor diesem Hintergrund wird klar, das Frames und Mehrsprachigkeit nur wenige Erweiterungen benötigen. Für diese Homepage wurde in der verwendeten Klasse redsys_menu.php, eine "target" Angabe hinzugefügt.

Leistungsgrenze

Die einfache Menüklasse ist gut geeignet für Homepages und Präsentationen mit einer tendenziell statischen Struktur. Große Systeme, deren Navigation dynamisch aufgebaut wird, überfordern Menu 3, obwohl die Struktur auch dynamisch zugewiesen und manipuliert werden kann.

Sourcen:

Lizenz: PHP Licence (darf in kommerziellen Projekten benutzt werden)

<  ^  >

 Neues

 XML/XSLT Menu
 OOH-Form Rewrite

 PEAR Cache:
  SHM Container

 Suchstring Parser
 Buchrezensionen
 PEAR Cache:
  OutputCompression

 PEAR Menu Browser
 PEAR Menu Tutorial 
 PEAR Cache


 Tipp

Download Version:
oben rechts,
Download *.tar.gz
|
| --- |
|
  Top   |   <  ^  >   |   phpOpenTracker Statistik   |   URL: http://www.ulf-wendel.de/projekte/menu/tutorial.php   |   Stand: 15.03.2001   |   © Ulf Wendel   
|
| --- |

0.033 s Bearbeitungszeit, 0.003 s IT[X], 0.002 s Menu 3