2. Workshop: Monatskalender in PHP (von Michael Christoph)

PHP ist eine moderne Internetskriptsprache (eine Mischung aus Basic, C und 
Perl) und liegt auch in der aktuellen Version 4.0 für den Amiga vor.
Da es sich im Gegensatz zu JavaScript um eine serverseitige 
Programmiersprache handelt, ist ein laufender Webserver zwingende 
Voraussetzung für das Erstellen, Testen und Laufen lassen von PHP-Skripten. 
Auch diese Aufgabe meistert der Amiga mit dem Apache-Webserver souverän.


* DIE INSTALLATIONSANWEISUNGEN GIBT ES WOANDERS

Soviel als Einleitung zu PHP. Grundlagen und Installationsanleitungen 
finden sich im Amiga-Magazin 08/2001 bzw. auch online unter 
http://www.meicky-soft.de/amiga-magazin/mysql.html


* EIN PAAR EINLEITENDE WORTE

Ich will hier keinen Einstiegskurs in die PHP-Programmierung beginnen, wer 
aber bereits (in anderen Sprachen) programmieren kann und sich für PHP 
interessiert, sollte trotzdem gut zurechtkommen.

Und im Gegensatz zu anderen Publikationen und Vorgehensweisen werde ich 
sofort mit dem fertigen Beispiel beginnen, das dann Schritt für Schritt 
genauer betrachtet wird. Dabei handelt es sich auch schon um ein sehr 
praktisches Beispiel und nicht die in Bücher oft zu findenden dreizeiligen 
Kurzbeispiele. Trotzdem soll dieses Beispiel lediglich als Grundlage 
dienen, das jeder nach seinen Wünschen noch weiter ausbauen
kann.


* DAS BEISPIEL

Screenshot:
Screenshot

 1 <?php
 2   /* kalender.php */
 3   /* 01.08.2002, Michael Christoph <michael2meicky-soft.de> */
 4 
 5 
 6   /* Hilfsfunktion zur Ermittlung von Feiertagen */
 7   function isFeiertag($m,$d,$y)
 8   {
 9     /* es wird nur der Sonntag als Feiertag erkannt */
10     if( date("w",mktime(1,1,1,$m,$d,$y)) == 0 )
11       return 'red';
12     else
13       return 'black';
14   }
15 
16 
17   /* erstes Element leer, damit Monat 1 = Januar ist */
18   $monatstr = array("", "Januar", "Februar", "März", "April", "Mai",
                       "Juni", "Juli", "August", "September", "Oktober",
                       "November", "Dezember");
19 
20   /* beim ersten Aufruf den heutigen Monat und Jahr anzeigen */
21   if(!(isset($jahr)))
22   {
23     $jahr = date("Y",time());
24     $monat = date("n",time());
25   }
26   $tag = 1;
27   $timestamp = mktime(1,1,1,$monat,$tag,$jahr);
28 
29   /* das Kalenderblatt als Tabelle ausgeben */
30   echo "<center><br><br>";
31   echo "<table width='800' border='0'>";
32   echo "<tr>";
33   echo "<td align='center' valign='top'>";
34 
35   /* Link für vorherigen Monat erzeugen */
36   if($monat > 1)
37     echo "<a href='kalender.php?monat=" . ($monat-1) . "&jahr="
                    . $jahr . "'>" . $monatstr[$monat-1] . "</a> <--- ";
38   else
39     echo "<a href='kalender.php?monat=12&jahr=" . ($jahr-1) . "'>"
                                         . $monatstr[12] . "</a> <--- ";
40 
41   /* das aktuelle Monat dazwischen mit Jahreszahl ausgeben */
42   echo " <font size='5' face='tahoma'>" . $monatstr[$monat]
                                                        . " $jahr</font> ";
43 
44   /* Link für folgenden Monat erzeugen */
45   if($monat < 12)
46     echo " ---> <a href='kalender.php?monat=" . ($monat+1)
                  . "&jahr=" . $jahr . "'>" . $monatstr[$monat+1] . "</a>";
47   else
48     echo " ---> <a href='kalender.php?monat=1&jahr=" . ($jahr+1)
                                            . "'>" . $monatstr[1] . "</a>";
49 
50   echo "<table width='150' border='1'>";
51 
52   /* als Überschrift die Wochentage eintragen */
53   echo "<th align='center' width='20'>Mo</th>";
54   echo "<th align='center' width='20'>Di</th>";
55   echo "<th align='center' width='20'>Mi</th>";
56   echo "<th align='center' width='20'>Do</th>";
57   echo "<th align='center' width='20'>Fr</th>";
58   echo "<th align='center' width='20'>Sa</th>";
59   echo "<th align='center' width='20'><font color='red'>So</font></th>";
60   echo "<tr>";
61 
62   /* erst mal einrücken bis zum Wochentag des 1. */
63   $wochentag = date("w",$timestamp);
64   if($wochentag == 0) $wochentag = 7;  /* 0=Sonntag */
65   for($i=1; $i<$wochentag; $i++)
66   {
67     echo "<td align='center'> </td>";
68   }
69 
70   /* dann alle Tage des Monats ausgeben */
71   while(checkdate($monat,$tag,$jahr))
72   {
73     echo "<td align='center'><font color='"
                    . isFeiertag($monat,$tag,$jahr) . "'>$tag</font></td>";
74     /* am Sonntag ist die Zeile zu Ende und eine neue wird begonnen */
75     if(isFeiertag($monat,$tag,$jahr) == "red") { echo "</tr><tr>"; }
76     $tag++;
77   }
78 
79   /* noch auffüllen bis zum Sonntag */
80   if(! (isFeiertag($monat,$tag-1,$jahr) == "red"))
81   {
82     while(! (isFeiertag($monat,$tag,$jahr) == "red"))
83     {
84       echo "<td align='center'> </td>";
85       $tag++;
86     }
87     echo "<td align='center'> </td>";
88   }
89 
90   echo "</tr>";
91   echo "</table>";
92   echo "</td>";
93 
94   echo "</tr>";
95   echo "</table>";
96   echo "</center>";
97 
98 ?>


* ERKLÄRUNG ZUM BEISPIEL

Das Beispiel ist lediglich zur besseren Orientierung zeilenweise 
nummeriert. Die Zahlen sind nicht mit einzugeben! Zeilen ohne Nummer sind 
lediglich zur besseren Lesbarkeit umgebrochen worden und einzeilig. 
Speichert es unter dem Namen "kalender.php" ab. Wichtig ist, als Endung 
".php" oder ".php3" oder ".php4" oder ".phps" zu verwenden, um Apache 
mitzuteilen, dass der Textinhalt erst noch verarbeitet werden muss, bevor 
er an den Browser gesendet wird. Dabei hängt es von der Apache-
Konfiguration ab, welche Endungen erkannt und benutzt werden können. Lokal 
kann dies in Apache:conf/httpd.conf eingetragen werden, ansonsten ist beim 
ISP nachzufragen.

PHP kann innerhalb eines HTML-Codes eingebettet werden, aber auch 
umgekehrt, HTML-Anweisungen können von PHP-Code erzeugt werden.
Mittels "<?php" oder auch kurz "<?" wird der Begin eines PHP-Blocks 
gekennzeichnet. Entsprechend ist "?>" als Endmarkierung zu verwenden. 
Außerhalb dieses Blockes kann ganz normaler HTML-Code geschrieben
werden.

Das ganze Programm setzt sich lediglich aus ein paar unterschiedlichen PHP-
Funktionen zusammen. Diese werde ich hier nur kurz vorstellen.

Heutzutage gibt es eine große Anzahl an PHP-Literatur. "GoTo PHP 4" aus dem 
Addison-Wesley-Verlag (590 Seiten, 39,95 EURO mit CD) sei hier als 
empfehlenswertes Beispiel genannt. Ein sehr umfangreiches (über 7 MByte 
großen) PHP-Handbuch ist unter "http://www.php.net/manual/de/" zu finden.

- isset
  dient zur Überprüfung, ob eine Variable gesetzt ist oder nicht.
  Wird ein PHP-Skript aufgerufen, stehen automatisch alle Aufrufoptionen 
  der URL-Zeile als benannte Variablen zur Verfügung.
  Der Aufruf "http://localhost/kalender.php?monat=8&jahr=2002" 
  erzeugt die beiden Variablen $monat und $jahr (Variablen werden
  durch ein vorangestelltes Dollarzeichen gekennzeichnet) und setzt
  sie auf die Werte 8 und 2002. Wird nur die Anweisung
  "http://localhost/kalender.php" aufgerufen, sind beide Variablen
  nicht vorhanden.

- echo
  ist wohl die wichtigste Funktion und erlaubt neben printf
  die Ausgabe von Text und HTML-Code an den aufrufenden Web-Browser.

- mktime
  erlaubt die Erstellung eines sogenannten Timestamps. Der Timestamp
  kann Datum und Uhrzeit als Integerzahl repräsentieren. Zu beachten
  ist die Reihenfolge der Parameter: Stunde,Minute,Sekunde,Monat,Tag,Jahr!
  
- date
  erlaubt die Ausgabe eines Timestamps in benutzerverständlicher Form.
  Anhand Platzhalterzeichen kann sehr flexibel das Ausgabeformat festgelegt 
  werden ("w" z.B. für den Wochentag)
  
- checkdate
  dient zur Überprüfung eines Datums auf Gültigkeit. Zu beachten ist
  die Reihenfolge der Parameter: Monat,Tag,Jahr!
  
- array
  erlaubt das Erstellen von Arrays. Dabei können beliebige Datentypen
  in einem Array gemischt werden (also z.B. Zahlen und Strings).
  Auch assoziative Arrays sind möglich und werden in der Form
  "name => michael" geschrieben.

- for
  Zählschleife, wie in C
  
- while
  Schleife mit Kopfbedingung, wie in C
  
- isFeiertag
  eigene Hilfsfunktion


* DAS BEISPIEL WIRD BETRACHTET

Das Beispiel beginnt eigentlich in Zeile 18, da vorher lediglich eine neue 
Funktion bekannt gegeben wird. Es können auch mehr Funktionen sein bzw. 
diese aus externen Dateien "includiert" werden.

In Zeile 18 wird lediglich ein Array erzeugt, das die Monatsnamen in 
deutscher Schreibweise enthält. Die Funktion date() liefert zwar mittels 
des Platzhaltersymbols "F" auch den Monatsnamen, allerdings mit englischer 
Bezeichnung. Da PHP (wie auch C) bei 0 zu zählen beginnt, die Monate aber 
mit 1 beginnen, wird das erste Element des Arrays leer gelassen.

Die Zeilen 21 bis 25 dienen dazu, festzustellen, ob das Skript mit oder 
ohne Parameter aufgerufen wurde. Ohne Parameter (also 
"http://localhost/kalender.php" allein) werden der heutige Monat und Jahr 
als Startwerte verwendet. Ansonsten wird dieser Programmteil gar nicht 
ausgeführt.
Zeile 26 setzt den Tageszähler auf 1. Er wird später bei der Ausgabe 
jeweils ums eins erhöht, bis zum Monatsletzten.

Weiter geht es dann in der Zeile 27, in der aus dem Datum (und einer für 
unser Beispiel irrelevanten Uhrzeit 1 Uhr, 1 Minute und 1 Sekunde) ein 
Timestamp zur weiteren Verarbeitung generiert wird.

Die Zeilen 30 bis 33 beginnen dann mit der Erzeugung einer HTML-Tabelle.

In den Zeilen 36 bis 39 wird ein HTML-Link erzeugt, bei dessen Auswahl der 
vorhergehende Monat im Browser angezeigt wird. Falls der aktuelle Monat 
noch nicht Januar ist, wird der Link für einen Monat verringert erzeugt, 
ansonsten für Dezember des vorhergehenden Jahres.

Zeile 42 schreibt zwischen die beiden Links den Namen des aktuellen Monats 
mit Jahreszahl in einer größeren Schrift.

Zeilen 45 bis 48 erzeugen entsprechend einen Link für den folgenden Monat. 
Falls noch nicht Dezember ist, für den nächsten Monat, sonst für Januar des 
darauffolgenden Jahres.

Falls das Monatsarray (Zeile 18) sofort mit "Januar" als ersten Eintrag 
begonnen worden wäre, hätte für die vorangegangenen Zeilen 36 bis 48
jeweils als Index [$monat-1] geschrieben werden müssen. Deutlich besser zu 
lesen ist es allerdings ohne die -1, und auch die Fehleranfälligkeit wird 
verringert.

Die Zeilen 53 bis 59 sorgen dann dafür, dass als Tabellenüberschriften die 
Wochentage verwendet werden. Dabei wird der Sonntag rot geschrieben (wie 
alle späteren Sonntage im Kalender auch).

In Zeile 63 wird ermittelt, auf welchen Wochentag der Monatserste fällt. 
Das Ergebnis ist ein Zahlenwert von 0 (=Sonntag) bis 6 (=Samstag). Falls es 
sich um einen Sonntag handelt, erhöhen wir in Zeile 64 den Wert auf 7. In 
den folgenden Zeilen 65 bis 68 werden nämlich Wochentage-1 leere Tabellen-
Felder erzeugt.

Die eigentliche Ausgabe des Kalenders erfolgt dann in den Zeilen 71 bis 77. 
Diese Schleife wird solange durchlaufen, bis das Datum nicht mehr gültig 
ist. Dadurch werden alle Tage des Monats als einzelne Zellen ausgegeben. 
Zeile 73 ist dafür zuständig, dass die Tage, die auf einen Sonntag fallen, 
in rot ausgegeben werden.
Zeile 75 hingegen beginnt eine neue Reihe, wenn der Sonntag ausgegeben 
wurde. Zeile 76 zählt lediglich den Tageszähler um eins hoch.

In Zeile 80 wird geprüft, ob das zuletzt ausgegebene Datum ein Sonntag war. 
Ist dies nicht der Fall, werden die Zeilen 82 bis 87 ausgeführt, die dafür 
sorgen, dass noch entsprechend viele leere Zellen erzeugt werden, um die 
aktuelle Reihe zu vervollständigen.

Die Zeilen 90 bis 96 dienen dann lediglich noch zum Abschließen der 
Tabellenanweisungen.


Damit sollte das Beispiel doch ausführlich genug beschrieben worden sein, 
damit es jeder Interessierte leicht nachvollziehen und vor allem verstehen 
kann.

Lediglich die Zeilen 7 bis 14 muss ich noch nachreichen. Hierbei handelt es 
sich um eine Hilfsfunktion, mit der festgestellt werden soll, ob das 
übergebene Datum ein Feiertag ist. Bisher beschränkt sich die Funktion 
allerdings darauf, den Sonntag zu erkennen. Dazu wird in Zeile 10 das Datum 
in einen Timestamp konvertiert und mittels der Date-Funktion der Wochentag 
ermittelt. Liefert diese 0 zurück, ist es ein Sonntag, und es wird als 
Rückgabe "red" geliefert, ansonsten "black". Dieser String wird direkt als 
Farbkodierung für die HTML-Font-Anweisung verwendet.


* FORTSETZUNG ERWÜNSCHT?

Wenn seitens der Leser Interesse besteht, bin ich gerne bereit, dieses 
Beispiel weiter auszubauen und in Verbindung mit einer mySQL-Datenbank mit 
dynamischen Inhalten zu ergänzen, beispielsweise für einen Terminkalender.
Schreibt uns, was euch interessiert!

Michael Christoph <michael@meicky-soft.de>