Академический Документы
Профессиональный Документы
Культура Документы
Juni 2005
Diese Anleitung, die sich gleichermaen an Anfnger wie allgemein Interessierte in Saa chen Datenbanken, SQL und PHP richtet, versucht schrittweise und spielerisch in die Geheimnisse der dynamischen Informationsverarbeitung im WWW einzufhren. Von der u Datenbank-Theorie und -Praxis (am Beispiel MySQL) uber die webfreundliche Script sprache PHP (inklusive Themen wie Datenbank-Anbindung und Objektorientierung) bis hin zu XML wird auf leicht verstndliche Art beschrieben, welche Mglichkeiten moderne a o Technologien bieten, Webseiten dynamisch zu gestalten. Nicht fehlen drfen dabei natru u lich die zahlreichen Beispiele und und Hinweise; so gibt es u. a. auch ein Kapitel, das sich mit der Behandlung und Vermeidung von Fehlern beschftigt. a Die aktuelle Version sowie verschiedene Formate zum Herunterladen benden sich unter http://reeg.net/ .
DSP - Datenbank SQL PHP Copyright (c) 2000 by Christoph Reeg (dsp@reeg.net). Dieses Material darf nur gem den Regeln und Bedingungen, wie sie von der Open Pua blication Licence, Version v1.0, festgelegt werden, verteilt werden (die letzte Version ist o gegenwrtig verfgbar unter http://www.opencontent.org/openpub/). Diese Verentlia u chung macht von keiner der im Abschnitt LIZENZ-OPTIONEN genannten Optionen Gebrauch. Die genaue Lizenz ndet sich im Anhang C.
Inhaltsverzeichnis
1 Vorwort 1.1 Aktuelle Version/Download . . . . . . 1.2 Was ist das hier oder was ist es nicht? 1.3 Weitere Informationsquellen oder Hilfe 1.4 Unterteilung . . . . . . . . . . . . . . . 1.5 Typographische Konventionen . . . . . 1.6 Autoren . . . . . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
1 1 1 2 2 4 5
Theoretische Grundlagen
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
8 8 8 9 9 10 10 11 11 11 12 12 13 13 14 14 14 14 15 15 15 15 16 16 18
2 Bentigte Software o 2.1 Apache: Der Webserver . 2.2 PHP - ist das gefhrlich? a 2.3 MySQL . . . . . . . . . . 2.4 LAMP / WAMP . . . . .
3 Datenbanksystem 3.1 Komponenten eines Datenbanksystems 3.2 Ebenen eines Datenbanksystems . . . 3.2.1 Betriebssystem/Hardware . . . 3.2.2 Interne Ebene . . . . . . . . . . 3.2.3 Konzeptionelle Ebene . . . . . 3.2.3.1 Tabellenstruktur . . . 3.2.3.2 Schlssel . . . . . . . u 3.2.4 Externe Ebene . . . . . . . . . 4 Datenbanken entwickeln 4.1 Vorgehensweise . . . . . . 4.2 Grundstze . . . . . . . . a 4.2.1 Keine Redundanz . 4.2.2 Eindeutigkeit . . . 4.2.3 Keine Prozedaten 4.3 Datenmodelle entwickeln . 4.3.1 Tabellen erstellen . 4.4 Die fnf Normalformen . . u 4.4.1 Die 1. Normalform 4.4.2 Die 2. Normalform
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
Christoph Reeg
Seite i
Inhaltsverzeichnis
Inhaltsverzeichnis
4.4.3 Die 3. Normalform . . . . . . . 4.4.4 Die 4. Normalform . . . . . . . 4.4.5 Die 5. Normalform . . . . . . . 4.4.6 Denormalisierung der Tabellen Streifendiagramm . . . . . . . . . . . . Das ER-Modell . . . . . . . . . . . . . Relationen erstellen . . . . . . . . . . . Datentypen . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
18 20 21 21 21 21 24 25
II
26
5 SQL benutzen 27 5.1 MySQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 5.1.1 Dateien abarbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 5.1.2 Kommentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 6 SQL-Befehle 6.1 CREATE DATABASE . . . . . . . 6.2 CREATE TABLE . . . . . . . . . 6.3 SHOW . . . . . . . . . . . . . . . . 6.4 DROP TABLE . . . . . . . . . . . 6.5 INSERT INTO . . . . . . . . . . . 6.6 SELECT . . . . . . . . . . . . . . 6.6.1 ORDER BY . . . . . . . . 6.6.2 GROUP BY . . . . . . . . 6.6.3 LIMIT . . . . . . . . . . . . 6.6.4 select expression . . . . . . 6.6.5 Alias . . . . . . . . . . . . . 6.6.5.1 Tabellen-Alias . . 6.6.5.2 Spalten-Alias . . . 6.6.6 where denition . . . . . . 6.6.6.1 LIKE . . . . . . . 6.6.6.2 BETWEEN . . . 6.6.6.3 IN . . . . . . . . . 6.7 Funktionen . . . . . . . . . . . . . 6.7.1 Mathematische Funktionen 6.7.2 Logische Operatoren . . . . 6.7.3 Sonstige Funktionen . . . . 6.7.4 Datums-Funktionen . . . . 6.7.5 Gruppenfunktionen . . . . . 6.8 Joins . . . . . . . . . . . . . . . . . 6.8.1 Equi-Join . . . . . . . . . . 6.8.2 Self-Join . . . . . . . . . . . 6.8.3 Outer-Join . . . . . . . . . 6.9 DELETE FROM . . . . . . . . . . 30 30 31 33 34 34 35 37 39 39 40 41 41 41 42 47 47 48 50 50 50 51 51 52 54 54 55 56 58
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
Christoph Reeg
Seite ii
Inhaltsverzeichnis
Inhaltsverzeichnis
6.10 UPDATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 6.11 ALTER TABLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 7 Tips 7.1 7.2 7.3 7.4 & Tricks zufllige Daten auswhlen . . . . . . . . . . a a Nutzer in MySQL . . . . . . . . . . . . . . PHPMyAdmin . . . . . . . . . . . . . . . . Bume in SQL . . . . . . . . . . . . . . . . a 7.4.1 Was ist ein Baum? . . . . . . . . . . 7.4.2 Beispieldaten . . . . . . . . . . . . . 7.4.3 Baumdarstellung mit Vater-Zeiger . 7.4.4 Nested Set Modell . . . . . . . . . . 7.4.4.1 Lschen . . . . . . . . . . . o 7.4.4.2 Einfgen . . . . . . . . . . u 7.4.5 Performance vom Nested Set Modell 7.4.6 Ubungen . . . . . . . . . . . . . . . 7.4.6.1 Vater-Zeiger . . . . . . . . 7.4.6.2 Nested Set . . . . . . . . . IF-Ausdrcke in SQL . . . . . . . . . . . . . u 7.5.1 Beispiele . . . . . . . . . . . . . . . . 7.5.1.1 Firma Ausbeuter & Co KG 65 65 66 66 67 67 68 69 69 74 76 76 76 76 77 77 79 79
7.5
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
81
82 82 83 83 84 84 85 85 85 85 86 86 87 87 87 88 88 88 89 89 91
Christoph Reeg
Seite iii
Inhaltsverzeichnis
Inhaltsverzeichnis
Variable Variablen . . . . . . . . . . . . . . . Vergleichsoperatoren . . . . . . . . . . . . . . 8.2.9.1 Typensichere Vergleiche . . . . . . . 8.2.10 IF . . . . . . . . . . . . . . . . . . . . . . . . 8.2.11 ELSE . . . . . . . . . . . . . . . . . . . . . . 8.2.12 ELSEIF . . . . . . . . . . . . . . . . . . . . . 8.2.13 Alternative Syntax fr IF: IF(): . . . ENDIF; . u 8.2.14 Alternative Syntax fr IF: (?:) . . . . . . . . u 8.2.15 WHILE . . . . . . . . . . . . . . . . . . . . . 8.2.16 DO . . . WHILE . . . . . . . . . . . . . . . . . 8.2.17 FOR . . . . . . . . . . . . . . . . . . . . . . . 8.2.18 SWITCH . . . . . . . . . . . . . . . . . . . . 8.2.19 foreach . . . . . . . . . . . . . . . . . . . . . 8.2.20 continue . . . . . . . . . . . . . . . . . . . . . 8.3 include . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4 require . . . . . . . . . . . . . . . . . . . . . . . . . . 8.5 Beispiele zu include und require . . . . . . . . . . . . 8.6 include once, require once . . . . . . . . . . . . . . . 8.7 Funktionen . . . . . . . . . . . . . . . . . . . . . . . 8.7.1 Variablenparameter . . . . . . . . . . . . . . 8.8 Formatierte Textausgabe . . . . . . . . . . . . . . . . 8.8.1 printf . . . . . . . . . . . . . . . . . . . . . . 8.8.1.1 Argumente vertauschen/numerieren 8.8.2 sprintf . . . . . . . . . . . . . . . . . . . . . . 8.8.3 number format . . . . . . . . . . . . . . . . . 8.9 Guter Stil . . . . . . . . . . . . . . . . . . . . . . . . 8.9.1 Warn-/Fehlermeldungen anzeigen . . . . . . . 8.10 Rekursion . . . . . . . . . . . . . . . . . . . . . . . . 8.10.1 Die Trme von Hanoi . . . . . . . . . . . . . u 8.10.2 Speicherverbrauch und Stack . . . . . . . . . 8.10.3 Vorteile der Rekursion . . . . . . . . . . . . . 8.10.4 Rekursion und Iteration im Vergleich . . . . . 8.10.5 Ein Beispiel aus der Praxis: MIME parsen . . 9 PHP & HTML 9.1 Formulare . . . . . . . . . . . 9.2 Werte ubergeben . . . . . . . 9.3 Dynamische Formulare . . . . 9.4 Normalform von Formularen . 9.4.1 Wieso ist das Null?! . 10 PHP & MySQL 10.1 Syntax . . . . . . . . . 10.1.1 allgemein . . . 10.1.2 mysql connect 10.1.3 mysql close . .
8.2.8 8.2.9
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
92 93 93 93 94 94 95 95 95 96 96 98 100 101 102 102 103 105 105 106 106 106 109 110 110 111 112 114 114 117 118 118 119
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
123 . 123 . 124 . 125 . 129 . 130 132 . 132 . 132 . 132 . 133
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
Christoph Reeg
Seite iv
Inhaltsverzeichnis
Inhaltsverzeichnis
10.1.4 mysql select db . . . . . . . . . 10.1.5 mysql query . . . . . . . . . . . 10.1.6 mysql fetch array . . . . . . . . 10.1.7 mysql fetch row . . . . . . . . 10.1.8 mysql error . . . . . . . . . . . 10.1.9 mysql errno . . . . . . . . . . . 10.1.10 mysql insert id . . . . . . . . . 10.1.11 mysql num rows . . . . . . . . 10.2 Tips und Tricks . . . . . . . . . . . . . 10.2.1 Abfragen mit IN . . . . . . . . 10.3 Ubung . . . . . . . . . . . . . . . . . . 10.3.1 Ergebnis-Tabelle ausgeben I . . 10.3.2 Ergebnis-Tabelle ausgeben II . 10.3.3 Abfrage mit sprintf() . . . . . . 10.3.4 Einfgen mit automatischer ID u 11 PHP & HTTP 11.1 Header . . . . . . . . . . . . . . . . 11.1.1 Weiterleiten . . . . . . . . . 11.1.2 Nicht gefunden . . . . . . . 11.1.3 Authentizierung . . . . . . 11.1.4 Download . . . . . . . . . . 11.1.5 Content-Type . . . . . . . . 11.1.6 Cache . . . . . . . . . . . . 11.2 URLs parsen . . . . . . . . . . . . 11.2.1 Beispiel: PHP-Manual . . . 11.2.2 Anderes Beispiel: Akronyme 12 Regulre Ausdrcke a u 12.1 einfache Suchmuster . . . . 12.2 Quantizierer . . . . . . . . 12.3 Gruppierungen . . . . . . . 12.4 Optionen . . . . . . . . . . 12.5 Ubungen . . . . . . . . . . . 12.5.1 einfache Suchmuster 12.5.1.1 . . . . . . 12.5.1.2 . . . . . . 12.5.1.3 . . . . . . 12.5.1.4 . . . . . . 12.5.1.5 . . . . . . 12.5.2 Quantizierer . . . . 12.5.2.1 . . . . . . 12.5.2.2 . . . . . . 12.5.2.3 . . . . . . 12.5.2.4 . . . . . . 12.5.2.5 . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
133 133 134 134 135 135 135 135 135 136 136 136 139 139 140
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
141 . 141 . 141 . 142 . 142 . 144 . 146 . 147 . 149 . 149 . 150 151 . 151 . 153 . 153 . 154 . 154 . 154 . 154 . 154 . 155 . 155 . 155 . 155 . 155 . 155 . 155 . 155 . 155
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
Christoph Reeg
Seite v
Inhaltsverzeichnis
Inhaltsverzeichnis
12.5.2.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 12.5.3 Gruppierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 12.5.3.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 13 Fehlersuche 13.1 Ubungen . . . . . . . . . . . . . . . . 13.1.1 Ein komisches IF . . . . . . . 13.1.2 Fehler in einer leeren Zeile? . 13.1.3 Wieso fehlt ein ; wo eins ist? 157 159 159 160 160
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
IV Beispiele
15 Einfaches Gstebuch a 16 Spruch des Abrufs 16.1 Neuen Spruch einfgen . . . u 16.2 Zuflligen Spruch ausgeben a 16.3 Alle Sprche ausgeben . . . u 16.4 Spruch lschen . . . . . . . o 16.5 Spruch andern . . . . . . . 16.6 Schlubemerkung . . . . . . 16.7 Ubung . . . . . . . . . . . .
164
165 170 171 173 176 177 178 182 183
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
17 Kleines Bannerscript 184 17.1 Erste Realisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 17.2 Zweite Realisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Objektorientierung theorethisch
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
191
192 192 193 194 194 195 195 195 196 196
18 Bedeutung von Objektorientierung 18.1 Ein kleines Beispiel: Funkwecker . . . . . . . . . . 18.2 Jedem sein Auto . . . . . . . . . . . . . . . . . . . 18.3 Von Autos zu Objekten . . . . . . . . . . . . . . . 18.4 Vererbung . . . . . . . . . . . . . . . . . . . . . . . 18.4.1 Image-Beispiel . . . . . . . . . . . . . . . . 18.5 Konstruktor . . . . . . . . . . . . . . . . . . . . . . 18.6 Destruktor . . . . . . . . . . . . . . . . . . . . . . . 18.7 Klasse oder Objekt . . . . . . . . . . . . . . . . . . 18.8 Zugrisrechte: public, private oder doch protected?
Christoph Reeg
Seite vi
Inhaltsverzeichnis
Inhaltsverzeichnis
19 Bezeichnungen 19.1 Instanz . . . . . . 19.2 Objekt . . . . . . . 19.3 Klasse . . . . . . . 19.3.1 Basisklasse 19.4 Methode . . . . . . 19.5 Attribut . . . . . . 19.6 Konstruktor . . . . 19.7 Destruktor . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
VI Objektorientierung praktisch
20 Objektorientierung in PHP 20.1 Klassen in PHP . . . . . . . . . . . . . . . . . . . . 20.1.1 Konstruktoren . . . . . . . . . . . . . . . . 20.1.2 Vererbung in PHP . . . . . . . . . . . . . . 20.1.3 Attribute in PHP . . . . . . . . . . . . . . . 20.1.4 Methoden in PHP . . . . . . . . . . . . . . 20.1.5 Klassen dokumentieren . . . . . . . . . . . 20.2 Objekte und Referenzierung in PHP . . . . . . . . 20.2.1 Referenzierung . . . . . . . . . . . . . . . . 20.2.1.1 Kopier- vs. Referenzsemantik . . . 20.2.1.2 Ausweg aus dem Referenzdilemma 20.2.1.3 foreach mit Objektreferenzen . . . 20.3 Methoden-Aufrufe . . . . . . . . . . . . . . . . . . 20.3.1 static . . . . . . . . . . . . . . . . . . . . . 20.3.2 parent . . . . . . . . . . . . . . . . . . . . . 20.4 Das fertige Beispiel . . . . . . . . . . . . . . . . . . 20.5 Ausblick: PHP5 . . . . . . . . . . . . . . . . . . . . 20.6 Konzeptionelle Beispiele . . . . . . . . . . . . . . . 20.7 Ubung . . . . . . . . . . . . . . . . . . . . . . . . . 20.7.1 OOP . . . . . . . . . . . . . . . . . . . . . . 20.7.2 Referenzsemantik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
199
200 200 200 201 201 202 202 203 204 204 204 205 206 206 206 207 209 211 211 211 214
216
217 217 218 218 218 220 220 220
Christoph Reeg
Seite vii
Inhaltsverzeichnis
Inhaltsverzeichnis
Ubersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Bestellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Entfernen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 224 224 224 225 225 225 228 229
22 XML-Dokumente parsen 22.1 Was ist XML? . . . . . . . . . . . . 22.2 Parsen von XML-Daten . . . . . . . 22.3 Beispiel 1 . . . . . . . . . . . . . . . 22.3.1 Die XML-Datei (tutorial.xml) 22.3.2 Das PHP-Skript . . . . . . . 22.4 Nachteile . . . . . . . . . . . . . . . 22.5 Beispiel 2 . . . . . . . . . . . . . . . 23 Templates 23.1 Beispiel . . . . . 23.2 PEAR::IT[X] . . 23.2.1 Block-API 23.3 Smarty . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
VIII Anhang
238
A Unser Beispiel in SQL 239 A.1 Zu erstellende Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 A.2 Daten einfgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 u B Lsungen o B.1 Lsung zu Baumdarstellungen . . . . . . . o B.1.1 Vater-Zeiger . . . . . . . . . . . . . B.1.2 Nested Set . . . . . . . . . . . . . B.2 Lsung fr Ergebnis-Tabelle ausgeben I o u B.3 Lsung fr Ergebnis-Tabelle ausgeben II o u B.4 Lsung fr Abfrage mit sprintf() . . . . o u B.5 Lsung fr Einfgen mit automatischer ID o u u B.6 Lsungen zu Regulren Ausdrcken . . . o a u B.6.1 Lsung zu einfache Suchmuster . o B.6.1.1 . . . . . . . . . . . . . . B.6.1.2 . . . . . . . . . . . . . . B.6.1.3 . . . . . . . . . . . . . . B.6.1.4 . . . . . . . . . . . . . . B.6.1.5 . . . . . . . . . . . . . . B.6.2 Lsung zu Quantizierer . . . . . o B.6.2.1 . . . . . . . . . . . . . . B.6.2.2 . . . . . . . . . . . . . . B.6.2.3 . . . . . . . . . . . . . . B.6.2.4 . . . . . . . . . . . . . . 242 242 242 244 246 248 249 249 250 250 250 250 250 251 251 251 251 251 251 251
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
Christoph Reeg
Seite viii
Inhaltsverzeichnis
Inhaltsverzeichnis
B.7
B.6.2.5 . . . . . . . . . . . . . . . . . . B.6.2.6 . . . . . . . . . . . . . . . . . . B.6.3 Gruppierungen . . . . . . . . . . . . . . . B.6.3.1 . . . . . . . . . . . . . . . . . . Lsungen zur Fehlersuche . . . . . . . . . . . . . o B.7.1 Lsung fr ein komisches IF . . . . . . . o u B.7.2 Lsung fr Fehler in einer leeren Zeile? o u B.7.3 Lsung zu Wieso fehlt ein ; wo eins ist? o Lsung zu Spruch des Abrufs . . . . . . . . . . . o Lsung zum Image-Beispiel . . . . . . . . . . . . o Lsung zur Referenzsemantik . . . . . . . . . . . o
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
251 251 252 252 252 252 253 254 254 255 259
C Open Publication License 261 C.1 Englische Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 C.2 Deutsche Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 Literaturverzeichnis 266 C.3 zitierte Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 C.4 Weitere Informationsquellen . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 Abbildungsverzeichnis Tabellenverzeichnis D Danksagungen E Versionen E.1 19.06.2005 E.2 13.05.2002 E.3 22.06.2001 E.4 27.04.2001 E.5 05.12.2000 E.6 27.11.2000 E.7 19.10.2000 E.8 30.09.2000 E.9 27.07.2000 Index 267 268 270 271 271 272 272 272 273 273 273 273 273 274
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
Christoph Reeg
Seite ix
1 Vorwort
The dierence between theory and practice is that in theory there is no dierence between theory and practice but in practice there is.
Dieses Tutorial erklrt die Grundlagen einer Datenbank in Verbindung mit der Abfraa gesprache SQL und der Scriptsprache PHP. Dafr wird entlang eines Beispiels ein Datenu modell entwickelt und seine Umsetzung beschrieben. Anhand von einigen Beispielen wird dabei versucht, die Theorie etwas aufzulockern. Die Kenntnis der Sprache HTML wird vorausgesetzt und in dem vorliegenden Tutorial werden im Prinzip keine HTML-Befehle beschrieben. Wer meint, noch Nachholbedarf zu haben, kann sich unter [7] noch ein Nachschlagewerk besorgen. Dieses Tutorial wurde ursprnglich fr Skripte geschrieben, die auf dem Server der Sites u u http://m.junetz.de/ und http://of.junetz.de/ Verwendung nden. Sie kann aber auch fr u alle anderen Server benutzt werden, die dieselben Grundkomponenten nutzen, wie in Abschnitt 2.2 beschrieben. Etwaige Server-spezische Besonderheiten mssen dabei natrlich u u auer acht gelassen werden.
http://ml.junetz.de/list/listinfo/dsp-announce/
Christoph Reeg
Seite 1
1. Vorwort
Im SQL- und PHP-Teil werden die grundlegenden Befehle vorgestellt. Fr eine ausfhru u liche Befehlsbersicht mssen die Original-Anleitungen zu Rate gezogen werden. u u
1.4 Unterteilung
Dieses Dokument ist in verschiedene Teile unterteilt, wobei die ersten drei auch einzeln fr sich gelesen werden knnen. u o
2 3
Christoph Reeg
Seite 2
1. Vorwort
Unterteilung
Erster Teil
Im ersten Abschnitt dieses Teils der Anleitung soll kurz geklrt werden, welche Software a verwendet wird und welche Rolle sie spielt. Die Einzelheiten werden spter im Tutorial a behandelt. Anschlieend soll geklrt werden, was eine Datenbank ist, bzw. was mit Begrien wie a DB, DBS oder DBMS gemeint ist und wo die Unterschiede liegen. Dieser Teil ist nicht existentiell, um nachher Datenbanken zu entwickeln; allerdings sollte man es sich schon einmal durchlesen. Und sei es nur, um einmal die Begrie gehrt zu o haben ;-). Wenn wir dann wissen, was eine DB ist, kommen wir zu der Frage, wie man aus einer Aufgabenstellung die passenden Datenstrukturen entwickelt. Dieser Teil ist fr alle, die u auch fr den Entwurf der Datenstrukturen verantwortlich sind, besonders wichtig, weil man u sich sonst viel Arger einhandeln kann. Wer lediglich mit schon vorhanden Datenstrukturen arbeiten mu, kann diesen Teil uberspringen, obwohl er eigentlich sehr interessant ist ;-).
Zweiter Teil
Nachdem wir uns dann im ersten Teil schon einige Zeit mit der Datenstruktur beschftigt a haben, wird es langsam Zeit, unsere Kenntnisse am Computer zu testen. Als DBMS benutzen wir MySQL, das als Abfragesprache SQL4 benutzt. SQL ist eine nach bestimmten Normen festgelegte Sprache, die von der Mehrheit der Relationalen DBS5 benutzt wird; dazu zhlen unter anderem: PostgreSQL, IBM DB2, Oracle, Adabas-D, a MySQL, mSQL, Informix und Gupta. Im Prinzip ist SQL standardisiert, allerdings untersttzen nicht alle Hersteller den komu pletten Standard und jeder hat seine eigenen Erweiterungen. Soweit nicht explizit angegeben, sollten die in dieser Anleitung benutzten Befehle dem Standard entsprechen und auch mit anderen Datenbanken verwendet werden knnen. o All diejenigen, die mit Datenbanken arbeiten wollen, mssen diesen Teil vollstndig u a verstanden haben - andernfalls kommen sie spter mit Sicherheit stark ins Schleudern. a Du mut ubrigens nicht jeden Befehl auswendig kennen; viel wichtiger ist, da du weit, wo du nachschlagen kannst.
Dritter Teil
In diesem Teil wird die Scriptsprache PHP beschrieben. Mit ihr kann man sehr viel mehr machen als nur Datenbanken abzufragen, wobei der Schwerpunkt hier natrlich auf der u Datenbankabfrage liegt. Wer mehr uber diese Sprache lernen will, sollte sich einfach die Original-Dokumentation zu Gemte fhren. u u Seit einiger Zeit gibt es PHP in der Version 4. Bis alle umgestellt haben, wird jedoch sicherlich noch einige Zeit lang PHP3 verbreitet sein. Im Prinzip sollten alle PHP3-Scripte auch unter PHP4 laufen. Soweit nicht anders angegeben, sind die Befehle, die in dieser Anleitung verwendet werden, sowohl unter PHP3 als auch unter PHP4 verwendbar. Wenn ein Befehl erst in PHP4 hinzu gekommen ist, wird dies explizit erwhnt (sofern ich es nicht a vergessen habe ;-)).
4 5
Christoph Reeg
Seite 3
1. Vorwort
Typographische Konventionen
Dieser Teil ist unbedingt notwendig, um in PHP programmieren zu knnen und sollte o deshalb verstanden worden sein, was wie oben nicht heien soll, da du jeden Befehl auswendig kennen mut, sondern nur, da du im Zweifelsfall weit, wo du nachschlagen mut.
Vierter Teil
Hier werden ein paar Beispiele vorgestellt, in denen meines Erachtens Konstrukte benutzt werden, auf die man nicht immer auf Anhieb kommen wrde. Hoentlich helfen sie Dir. u Sollte noch etwas fehlen: Meine E-Mail-Adresse kennst du ja. ;-) Ein paar der Beispiele sind auch unter http://reeg.net/ in Aktion zu sehen (wenn ich es endlich mal schae, sie zu programmieren :-( ).
fett
Christoph Reeg
Seite 4
1. Vorwort
Autoren
1.6 Autoren
Inzwischen schreibe ich zum Glck nicht mehr alleine an diesem Tutorial. Auf den jeweiu ligen Seiten steht in der Fuzeile, wer das Kapitel im Prinzip geschrieben hat6 . Wenn du Anregungen zu dem Tutorial hast, kannst du gerne eine Mail an dspautor@ml.junetz.de schreiben; die wird dann automatisch an alle weitergeleitet und irgendjemand im Autorenteam7 wird dann schon antworten. Nun folgen ein paar Worte von den einzelnen Autoren (vielleicht werden es irgendwann ja auch noch mehr ;-) ).
Christoph Reeg
Ich war derjenige, der mit dem Ganzen angefangen hatte. Eigentlich war es nur im Rahmen des Jugendnetz FFM/OF gedacht gewesen und nach der ersten Version8 hatte ich auch keine groe Lust mehr gehabt, weiter zu schreiben und hatte sie einfach auf meiner Homepage9 liegen. Als ich dann aber in die Abrufstatistik von dem Server gesehen und festgestellt hatte, da das Tutorial etliche Zugrie verbuchen konnte, habe ich dann weiter geschrieben. In der Zwischenzeit habe ich dann auch noch etwas dazu gelernt, so da auch ein paar Anfangsfehler verschwunden sind. So geht es nun weiter: in gelegentlichen Abstnden ergreift mich der Ehrgeiz und ich setze mich wieder fr ein paar Wochen dran a u und es entstehen ein paar neue Kapitel, und dann ruht es wieder. Da ich bis jetzt leider noch nichts Vergleichbares im Internet gefunden habe, werde ich wohl auch noch die nchste Zeit damit beschftigt sein und weiterschreiben. Auch die a a Mails, die ich gelegentlich bekomme, zeigen mir immer wieder, da es einen gewissen Sinn hat, was ich da mache.
Jens Hatlak
Angefangen habe ich, wie Stefan Nagelschmitt, als Lektor. Anstatt jedoch einfach nur Christophs (zahlreiche ;-)) Rechtschreibfehler zu korrigieren, habe ich dann mit der Zeit immer mehr auch Neues hinzugefgt. Das ging dann irgendwann so weit, da erst ein paar u Abschnitte und schlielich ganze Kapitel (wie z. B. OOP) von mir geschrieben wurden. So langsam fllt mir aber gar nichts mehr ein, was noch dringend beschrieben werden a mte aber es steht ja noch die Umstellung auf neue Rechtschreibung an, also werde ich u zumindest als Lektor wohl immer was zu tun haben. ;-)
Tilman Brock
Ich bin einer von denen, die zusammen mit Christoph beim Jugendnetz Frankfurt ar beiten. Uber das Jugendnetz und meine Programmierarbeit mit PHP habe ich das DSP schtzen gelernt; jetzt fhle ich mich in der Lage, auch meinen Teil zu dieser PHP/MySQLa u Anleitung zu schreiben. Ich bin auch der Erste, der das DSP extensiv in die Schulen ge6
7 8 9
Ich lasse es mir natrlich nicht nehmen, uberall noch etwas zu korrigieren. Im Gegenzug ist Jens u immer noch so freundlich und korrigiert meine Sachen ;-) Team = Toll, ein anderer machts! die war irgendwann 1998 die eigentlich gar nicht so toll war
Christoph Reeg
Seite 5
1. Vorwort
Autoren
bracht hat, meine komplette Schule10 soweit sie mit PHP/MySQL arbeitet benutzt als erste Dokumentation http:// www.reeg.net.
David Peter
Ich bin im Herbst 2001 per Zufall auf DSP gestoen und hab kurz danach Christoph gefragt, ob ich helfen knnte, indem ich ein paar Artikel fr DSP schreibe. Er hatte nichts o u dagegen und so konnte ich kurz darauf das erste Kapitel (ber XML) fr DSP bereitstellen. u u Danach war eine Weile lang nichts los, da weder ich noch die anderen Autoren viel an DSP gearbeitet haben und so wurde ich erst im Frhling 2002 wieder durch eine Mail von u Christoph an DSP erinnert.
10
Christoph Reeg
Seite 6
Christoph Reeg
Seite 7
2 Bentigte Software o
2.1 Apache: Der Webserver
Fangen wir als erstes mit der einfachsten Form von Webseiten an: den statischen Webseiten. Sie werden mit Hilfe eines ASCII-/Homepage-Editors erstellt und liegen dann als .html auf der Festplatte. Von dort kannst du sie dir dann mit jedem beliebigen Browser abrufen und anzeigen lassen. Damit jetzt jeder beliebige Surfer im Internet sich die Seiten ansehen kann, mu auch er irgendwie darauf zugreifen knnen. Dafr ist der Webserver1 (z. B. o u Apache) da. Er erlaubt den Zugri aus dem Netz auf die lokalen Dateien. Normalerweise ldt man seine Homepage aber auf den Server seines Providers und uberlt den dortigen a a Webservern die Bereitstellung der Webseiten. Wenn es so einfach ist, eine Webseite zu erstellen, wozu braucht man dann noch Datenbanken, PHP und SQL? Weil es einige Bereiche gibt, wo statische Webseiten nicht ausreichen. Zum Beispiel bei Suchmaschinen: Die Ergebnisseite hngt von den Suchwrtern a o ab, die du eingegeben hast und es ist unmglich fr alle mglichen Suchwrter und deren o u o o Kombinationen statische Ergebnisseiten zu erstellen. Also mu es auf dem Webserver ein Programm geben, was die Benutzereingaben auswertet und dementsprechende Webseiten erstellt. Der Besucher der Webseite bekommt nichts davon mit, ob die Webseite statisch oder dynamisch ist: er bekommt immer HTML von dem Webserver zurck. u Der Webserver mu dafr sorgen, da bei dynamischen Webseiten das entsprechende u Programm ausgefhrt wird und dessen Ausgabe an den Besucher zurck gegeben wird. u u Eine mgliche Programmiersprache fr solche Programme ist PHP. o u
An dieser Stelle meine ich die Software, nicht den Rechner Oder? ;-)
Christoph Reeg
Seite 8
2. Bentigte Software o
MySQL
2.3 MySQL
Hug wird mit Hilfe von PHP auch Daten verarbeitet. Zum Beispiel bei einem Gstebuch a a werden die Eintrge gespeichert. Die einfachste Variante wre, das uber Dateien zu lsen, a a o allerdings mu sich dann der Programmierer um einige Sachen, wie zum Beispiel FileLocking3 , ezient Daten lschen, eziente Suche, usw. selbst kmmern. Viel einfacher ist o u es, diese Aufgaben einem Spezialisten zu uberlassen: Dem Datenbank Management System (kurz. DBMS). Im Web-Bereich ist MySQL sehr verbreitet.
z. B. Was passiert wenn zwei Besucher zur genau selben Zeit die Seite aufrufen?
Christoph Reeg
Seite 9
3 Datenbanksystem
3.1 Komponenten eines Datenbanksystems
Eine Datenbank (DB, engl. Data Base) ist eine systematische Sammlung von Daten. Zur Nutzung und Verwaltung der in der DB gespeicherten Daten bentigt der Anwender ein o Datenbank-Verwaltungssystem (DBMS, engl. Data Base Management System). Die Kombination aus DB und DBMS ist das Datenbanksystem (DBS, engl.: Data Base System), das jedoch hug flschlicherweise als Datenbank bezeichnet wird. a a
Anwendung 1
...
DBMS
Anwendung n
Datenbank
Abbildung 3.1: Struktur eines Datenbanksystems Das DBMS besteht aus einer Vielzahl von Werkzeugen und Generatoren ( Erzeugern). Auf der einen Seite stellt es dem Entwickler die Instrumente zu Verfgung, mit denen u er das Datenmodell beschreiben und einrichten kann. Auf der anderen Seite bietet es die Funktionen an, mit denen die einzelnen Anwender Daten eingeben, verndern, abfragen a und ausgeben knnen. o Alle Funktionen des DBMS werden durch was und nicht mehr wie speziziert; soll heien: Der Entwickler teilt dem Programm die Datenlogik mit und der Anwender formuliert seine Abfrage. Wie die Daten zu speichern und zu verwalten sind, ist Sache des
Christoph Reeg
Seite 10
3. Datenbanksystem
DBMS. Dieses ist also zustndig fr die technische Umsetzung der Anforderungen des a u Entwicklers und der Anwender.
User 1 Programm 1
... ...
User n Programm n
3.2.1 Betriebssystem/Hardware
Dies ist die unterste Ebene, auf der jede Computeranwendung basiert. Neben dem DBS bauen auch alle anderen Programme auf dieser Ebene auf. Man kann diese Ebene aber noch weiter unterteilen: Zum einen ist da die Hardware als absolut unterste Ebene, deren Mglichkeiten vom Betriebssystem (BS) verwaltet werden. Das Betriebssystem zum o anderen bietet Programmen die Hardwaremglichkeiten an, ohne da die Programme die o Hardware direkt ansprechen mten. u
Christoph Reeg
Seite 11
3. Datenbanksystem
braucht weder die interne noch die konzeptionelle Ebene zu kmmern, da er erst uber die u oberste, nmlich die externe Ebene, auf die DB zugreift. a
Relationenname
A1
... ...
An
} Relationsschema
...
Tupel
Relation
...
Abbildung 3.3: Tabellenstruktur Um das Ganze etwas konkreter zu machen, habe ich in Tabelle 3.1 ein kleines Beispiel dargestellt.
Christoph Reeg
Seite 12
3. Datenbanksystem
Mitarbeiter MNr 1 2 3 4 5 6
AbtNr 3 1 1 1 2 2
Tabelle 3.1: Beispiel fr Tabellenstruktur u Das Beispiel zeigt die Relation mit dem Namen Mitarbeiter. Jeder Mitarbeiter hat die Attribute MNr, Name, GebDat und Telefon. In der Relation stehen 6 Datenstze a bzw. Tupel. 3.2.3.2 Schlssel u Damit man jede Zeile gezielt ansprechen kann, wird ein Schlsselattribut eingefhrt. Der u u Schlssel mu immer eindeutig sein und wird auch als Primrschlssel bezeichnet. Der u a u Primrschlssel mu nicht immer aus nur einem Attribut bestehen. Es ist auch mga u o lich, mehrere Attribute zusammen als (zusammengesetzten) Primrschlssel zu verwenden. a u Teilweise hat man in einer Relation mehrere Attribute, die eindeutig sind, d. h. Schlssel u sein knnten; in diesem Fall werden die anderen Attribute als Schlsselkandidaten beo u zeichnet. Oder anders herum: Jeder Schlsselkandidat kann jederzeit als Primrschlssel u a u benutzt werden. Es kann aber fr eine Tabelle immer nur einen Primrschlssel gleichzeitig u a u geben. Zum Einrichten der DB mit ihren Tabellen bedient man sich der Data Denition Language (DDL).
Christoph Reeg
Seite 13
4 Datenbanken entwickeln
Umwege erhhen die Ortskenntnisse o unbekannt
4.1 Vorgehensweise
Die Entwicklung einer DB vollzieht sich in mehreren Schritten. Zunchst ist festzustela len, welche Informationen die Anwender vom DBS erwarten, bzw. welche Informationen gespeichert werden sollen. Aufgrund dieser Erhebung kann man sich dann uberlegen, wel che Tabellen bentigt werden. Ferner mu festgelegt werden, welche Datentypen fr die o u einzelnen Tabellenspalten bentigt werden. Diesen Proze bezeichnet man als Datenmodelo lierung. Erst wenn die Datenmodellierung abgeschlossen ist, knnen die Tabellen angelegt o werden. Man sollte sich fr diesen Schritt ruhig ein wenig Zeit nehmen, weil es nachher hug u a unmglich ist, ohne groen Aufwand Fehler zu beheben. o
4.2 Grundstze a
Um sich einigen Arger zu ersparen, empehlt es sich, ein paar Grundstze bei der Datena modellierung zu beachten:
Wie man leicht erkennen kann, kommt der jeweilige Vorname in zwei Spalten vor. Dies bringt zwei Nachteile mit sich: Zum einen kostet es mehr Speicherplatz, was bei einigen 1000 Datenstzen schon etwas ausmacht; zum anderen werden Anderungen schwieriger, a anflliger fr Fehler und auch aufwendiger, da ja zwei Attribute gendert werden mssen. a u a u Wenn dies nicht erfolgt, treten Inkonsistenzen auf. Wenn zum Beispiel Christof Meier feststellt, da ein Christoph, mit f geschrieben, einfach nicht so gut aussieht und er es gerne in Christoph gendert haben wrde, dabei a u
Christoph Reeg
Seite 14
4. Datenbanken entwickeln
Datenmodelle entwickeln
aber nur das Attribut Vorname gendert wird, knnten zum Beispiel die Briefe weiter a o an Christof Meier geschickt werden, weil hier das Attribut Vor-/Nachname verwendet wird. An einer anderen Stelle im Programm wrde aber wieder der korrigierte Christoph u auftauchen.
4.2.2 Eindeutigkeit
Eine DB enthlt Angaben zu den Eigenschaften einer Person oder Sache. Mittels dieser a Angaben mu eindeutig ein bestimmtes Tupel identizierbar sein. Das DBMS verfgt nicht uber einen denierten Zugrisweg auf einen bestimmten Datenu satz. Deshalb mu in jeder Zeile einer Tabelle ein Wert enthalten sein, der diesen Eintrag eindeutig kennzeichnet bzw. identiziert. Um die Eindeutigkeit der Tabellenzeilen zu gewhrleisten, erweitert man den Datensatz um ein Identikationsmerkmal, z. B. wird einem a Artikeldatensatz eine Artikelnummer zugeordnet. Dieses Merkmal nennt man Schlssel. u Beim Festlegen des Schlssels kann man einen Schlssel selbst denieren oder einen u u fremddenierten ubernehmen. Bei einem Buch wrde sich da die ISBN-Nummer anbieten. u Um nicht Gefahr zu laufen, da durch eine Anderung solcher fremddenierten Schlssel u im DBS Inkonsistenzen auftreten, zum Beispiel, weil der Schlssel nicht mehr eindeutig u ist, empehlt es sich hug, einen eigenen zu nehmen. a
Christoph Reeg
Seite 15
4. Datenbanken entwickeln
Festlegen, wie die Tabellen aussehen sollen. Um die bentigten Tabellen zu entwickeln, gibt es fr einfache DBs im Prinzip zwei o u Mglichkeiten: Entweder stur nach Schema-F uber die fnf Normalformen (Kapitel 4.4) o u oder etwas intuitiver uber das ER-Modell (Kapitel 4.6), evtl. anschlieend mit Kontrolle durch die fnf Normalformen (Kapitel 4.4). u Erst wenn man grere DBs entwickelt, mu man mit beiden Mglichkeiten gleichzeitig o o arbeiten. Das heit, erst mit dem ER-Modell eine Grundstruktur festlegen und diese dann mit den fnf Normalformen uberprfen. u u
AuftragNr 4711
Datum 03.10.1999
0815
01.03.1998
54321
Anzahl 5 2 3 1 2 5 9
Um die Wiederholungsgruppen zu vermeiden, wird die Relation Auftrag in zwei gesonderte Relationen aufgespalten. Dadurch wrden sich die folgenden beiden Tabellen u ergeben:
Christoph Reeg
Seite 16
4. Datenbanken entwickeln
best. Artikel ArtikelNr Anzahl 4692 5 0567 2 5671 3 0579 1 8971 2 5324 5 0579 9
Jetzt ist aber die Zuordnung verloren gegangen. Wer hat welche(n) Artikel bestellt? Dieses Problem ist einfach zu lsen: Wir mssen nur festhalten, welche Artikel zu welcher o u Bestellung gehren. Da die AuftragNr eindeutig ist, nehmen wir diese als Primrschlssel1 o a u fr Auftrag. Nun fgen wir noch dieser Spalte entsprechend ihrer Werte der Relation u u best. Artikel hinzu, und schon haben wir wieder unsere Zuordnung. In dieser Konstellation wird die Spalte AuftragNr in best. Artikel als Fremdschlssel u bezeichnet. Weiterhin wurde schon auf Seite 15 gefordert, da jede Zeile eindeutig ansprechbar sein mu. Wie aber ist das in unserem Fall der bestellten Artikel zu erreichen? Nun, die AuftragNr und die ArtikelNr kommen zwar mehrfach vor, trotzdem ist die Lsung aber ganz einfach: Die Kombination aus AuftragNr und ArtikelNr mu eindeuo tig sein. Wenn wir also diese Kombination whlen, ist die o. g. Forderung erfllt. Diese a u 2 bezeichnet. Kombination wird ubrigens als ,zusammengesetzter Primrschlssel a u Damit ergeben sich fr unser Beispiel die folgenden beiden Relationen: u best. Artikel # AufragNr # ArtikelNr 4711 4692 4711 0567 4711 5671 4711 0579 0815 8971 0815 5324 0815 0579 Auftrag # AuftragNr Datum 4711 3.10.1999 0815 1.3.1998
Anzahl 5 2 3 1 2 5 9
1 2
Primrschlssel werden im folgenden durch eine fhrende Raute (#) gekennzeichnet a u u bei zusammengesetzten Primrschlsseln wird im Folgenden jeder Teil mit einer fhrenden Raute (#) a u u gekennzeichnet
Christoph Reeg
Seite 17
4. Datenbanken entwickeln
Hersteller Blech-AG Keramik GmbH Bausto KG Keramik GmbH Keramik GmbH Bausto KG Keramik GmbH
In diesem Beispiel ist das Attribut Hersteller nur vom Teilschlssel ArtikelNr und u nicht auch von AuftragNr abhngig. Damit die Relation der 2. NF gengt, mu das a u Attribut Hersteller aus der Relation herausgenommen und der (neuen) Relation Artikel zugeordnet werden. Daraus wrden dann die folgenden zwei Relationen entstehen: u best. Artikel Artikel # AufragNr # ArtikelNr Anzahl # ArtikelNr Hersteller 4711 4692 5 4692 Blech-AG 4711 0567 2 0537 Keramik GmbH 4711 5671 3 5671 Bausto KG 4711 0579 1 0579 Keramik GmbH 0815 8971 2 8971 Keramik GmbH 0815 5324 5 5324 Keramik GmbH 0815 0579 9
Christoph Reeg
Seite 18
4. Datenbanken entwickeln
Sind A und B Attribute eines Relationstyps, so ist B funktional abhngig von a A, wenn fr jedes Vorkommen ein und desselben Wertes von A immer derselbe u Wert von B auftreten mu. Eine funktionale Abhngigkeit kann auch von einer Gruppe von Attributen a bestehen. [3] Ein kleines Beispiel: Zu den einzelnen Artikeln sollen die ArtikelNr, die Bezeichnung, der Hersteller und die HerstellerNr gespeichert werden. Als Primrschlssel wird die ArtikelNr verwendet. Wrde a u u man die zustzliche Spalte einfach in die vorhandene Tabelle Artikel einfgen, ergbe sich a u a damit folgende Tabelle: Artikel Bezeichnung HerstellerNr Putzeimer 5410 Waschbecken 5647 Gummi 6740 Teller 5647 Tasse 5647 Badewanne 5647
Hersteller Blech-AG Keramik GmbH Bausto KG Keramik GmbH Keramik GmbH Keramik GmbH
Wie man unschwer erkennen kann, ist der Herstellername von der ArtikelNr uber die HerstellerNr transitiv abhngig. Oder anders ausgedrckt: Der Herstellername ist funktioa u nal abhngig von der HerstellerNr, die wiederum abhngig von der ArtikelNr ist. Und diese a a funktionale Abhngigkeit der beiden Nicht-Schlssel-Attribute HerstellerNr und Hersteller a u ist nicht erlaubt. Was jetzt kommt, ist nicht schwer zu erraten: Die Tabelle Artikel wird in die beiden Tabellen Artikel und Hersteller aufgespalten. Das heit, es ergeben sich folgende Tabellen: # ArtikelNr 4692 0567 5671 0579 8971 5324 Artikel Bezeichnung Putzeimer Waschbecken Gummi Teller Tasse Badewanne HerstellerNr 5410 5647 6740 5647 5647 5647
Hersteller # HerstellerNr Hersteller 5410 Blech-AG 5647 Keramik GmbH 6740 Bausto KG
Christoph Reeg
Seite 19
4. Datenbanken entwickeln
Christoph Reeg
Seite 20
4. Datenbanken entwickeln
Streifendiagramm
4.5 Streifendiagramm
Um die Tabellen grasch dazustellen, gibt es verschiedene Methoden. Eine Methode, mit der man relativ schnell einen Uberblick uber die vorhandenen Relationen einschlielich deren Attribute und Beziehungen bekommt, ist das Streifendiagramm. Damit ist es dann mglich, anhand des Vorgangskatalogs zu uberprfen, ob alle Vorgnge mglich sind und o u a o die Relationen stimmen. Als Beispiel habe ich die Relationen Auftrag, best. Artikel, Artikel und Hersteller aus dem obigen Beispiel in der Abbildung 4.1 dargestellt.
Christoph Reeg
Seite 21
4. Datenbanken entwickeln
Das ER-Modell
Abbildung 4.1: Streifendiagramm Es gibt verschiedene Formen, das ER-Modell zu zeichnen. Ich benutze hier das sogenannte Krhenfu-Diagramm. a Was bedeutet eigentlich Entity-Relationship ?? Fr Entitt gibt es verschiedene gebruchliche Denitionen: u a a Entitt [mlat.]die, -/-en, Philosophie: die bestimmte Seinsverfassung (Wesen) a des einzelnen Seienden, auch diese selbst. [1] Entitt [lat.-mlat.] die, -, -en: 1. Dasein im Unterschied zum Wesen eines a Dinges (Philos.). 2. [gegebene] Gre [2] o Wer jetzt wei, was Entity bedeutet, kann diesen Absatz uberspringen; fr alle anderen u versuche ich, es anders zu erklren: Entity kann man mit Objekt oder Ding ins a Deutsche ubersetzen, letztlich sind es konkrete Objekte der Realitt. Beim ER-Modell a sind Entities Objekte, die uber Attribute weiter beschrieben werden knnen. o Nachdem wir jetzt hoentlich wissen, was Entity bedeutet, sind wir beim zweiten Begri angelangt: Relationship bedeutet so viel wie Beziehung oder Relation. Ein kleines Beispiel: Fr ein Unternehmen soll eine Datenbank entwickelt werden. Es sollen alle Mitarbeiter u der Firma gespeichert werden. Jeder Mitarbeiter hat einen Vorgesetzten und gehrt zu o einer Abteilung. Auerdem verfgt jede Abteilung uber einige oder keine PKWs aus dem u Fuhrpark fr die Mitarbeiter. Zustzlich soll die Antwort auf die Frage mglich sein, wer u a o wann mit welchem Wagen wie viele Kilometer gefahren ist. Diese Realtittsbeschreibung legt drei Entitten nahe: Mitarbeiter, Abteilung, PKW. a a Die Zeichnung 4.2 stellt die Beziehung der drei Entitten und deren Attribute dar. a Wie man unschwer erkennen kann, stellen die Rechtecke die Entitten da. Sowohl die a Entitten, als auch nachher die Tabellen werden grundstzlich in der Einzahl bezeichnet. a a Die Striche zwischen den Entitten stellen deren Beziehung da. Der durchgezogene Strich a bedeutet genau einer, der gestrichelte einer oder keiner. Der Krhenfu auf der ana deren Seite bedeutet einer oder mehrere. Die Wrter an den Strichen zeigen, was die o Beziehung aussagt. Also z. B.: Ein Mitarbeiter gehrt zu genau einer Abteilung. Eine Abteilung kann aus o keinem, einem oder mehreren Mitarbeiter bestehen. Da eine Abteilung keinen Mitarbeiter haben kann, mag auf den ersten Blick merkwrdig erscheinen. Was ist aber, wenn die u Abteilung gerade erst eingerichtet wurde?
Christoph Reeg
Seite 22
4. Datenbanken entwickeln
Das ER-Modell
Mitarbeiter #* MNr * Name o GebDat o Tel-Nr gehrt zu ist Vorgesetzter besteht aus
besitzt
Christoph Reeg
Seite 23
4. Datenbanken entwickeln
Relationen erstellen
Wie wir bei der Entitt Mitarbeiter sehen, kann es durchaus auch eine Beziehung zwia schen einer Entitt mit sich selber geben. Uber diese Beziehung wird ausgesagt wer Vora gesetzter von wem ist. Da diese Beziehung von beider Seite eine ,kann-Beziehung sein soll, mag auf den ersten Blick komisch erscheinen. Aber nicht jeder Mitarbeiter hat einen Vorgesetzten, der Chef hat keinen. In den Rechtecken stehen die wichtigsten Attribute. Primrschlssel werden durch ein a u # gekennzeichnet, Primrschlsselkandidaten dagegen durch (#) markiert. Attribute, bei a u denen ein Wert eingetragen werden mu, werden mit einem * versehen; bei den optionalen wird ein o verwendet. Wenn man sich dieses ER-Modell einmal genauer ansieht, stellt man fest, da es gegen die 1. NF verstt. Die Beziehung zwischen Mitarbeiter und PKW ist eine n:m Bezieo hung. Um dieses Problem zu lsen, wird eine sog. Link-Relation erstellt. Wie das dann o genau aussieht, ist in Abbildung 4.3 dargestellt.
Untergebener von
besitzt
Christoph Reeg
Seite 24
4. Datenbanken entwickeln
Datentypen
Die Relation Fahrtenbuch hat einen zusammengesetzten Primrschlssel. Die MNr a u oder auch das Datum fr sich alleine wren nicht eindeutig, da ein Mitarbeiter ja nicht u a nur einmal Auto fhrt und zu einem Zeitpunkt ja auch ggf. mehrere Leute gleichzeitig a Autos fahren. In der Kombination jedoch sind die drei Attribute eindeutig. Bei PKW wurde nicht das Kennzeichen als Primrschlssel genommen, obwohl es sich a u dafr eignen wrde (mit Sicherheit eindeutig). Allerdings kann es passieren, da ein PKW u u ein neues Kennzeichen bekommt. Um auch dann noch die Datenintegritt sicherstellen zu a knnen, habe ich ein neues Attribut eingefhrt. o u Nachdem man die Primrschlssel herausgesucht hat, mssen auch die Fremdschlssel a u u u gesucht werden, damit man die Beziehung zwischen den Relationen herstellen kann. Das ist mit dem Krhenfudiagramm relativ einfach. Alle Relationen, die einen Krhenfu a a haben, bekommen den Primrschlssel der anderen Relation. D. h. es mu z. B. die Aba u teilungsnummer in die Relation Mitarbeiter eingefgt werden. Das ist auch logisch, denn u eine Abteilung kann natrlich auch mehrere Mitarbeiter haben. Wrde man dagegen die u u Mitarbeiter-Nummer in Abteilung einfgen, htte man einen Versto gegen die 1. NF. u a Aus unserem Beispiel ergben sich also folgende Relationen (mit Angabe der zu den a Fremdschlsseln gehrigen Primrschlssel): u o a u Tabelle Mitarbeiter Abteilung Fahrtenbuch PKW Primrschlussel a MNr AbtNr MNr, PKWNr, Datum PKWNr Fremdschlussel AbtNr [Abteilung(AbtNr)] VNr [Mitarbeiter(MNr)] MNr [Mitarbeiter(MNr)] PKWNr [PKW(PKWNr)] AbtNr [Abteilung(AbtNr)]
Als letztes mssen noch die bentigten Attribute den Relationen hinzugefgt und fr u o u u alle Attribute die entsprechenden Datentypen festgelegt werden.
4.8 Datentypen
Nachdem jetzt die Datenstruktur feststeht, mu noch festgelegt werden, welche Datentypen fr die einzelnen Attribute verwendet werden sollen. Im Prinzip gibt es drei verschieu dene Datentypen: Zahlen, Text und groe Objekte. In den verschiedenen Datenbanken gibt es dazu dann entsprechend verschiedene Gren. Eine Auistung mglicher Datentypen o o fr MySQL bendet sich im Kapitel 6.2. u Bei der Uberlegung, welcher Datentyp zu verwenden ist, sollte man nicht vom Normalfall ausgehen, sondern grundstzlich alle mglichen Ausnahmen in Betracht ziehen. So ist es a o z. B. notwendig, fr Hausnummer einen Text-Typ zu whlen, da z. B. 5a auch eine u a gltige Hausnummer ist. u
Christoph Reeg
Seite 25
Christoph Reeg
Seite 26
5 SQL benutzen
Ein DBS kann auf drei Arten gesteuert werden: Entweder dialogorientiert, im BatchBetrieb oder durch andere Programme. Dialogorientiert bedeutet, da man am Bildschirm seine Befehle eingibt und innerhalb von Sekunden das Ergebnis oder die Fehlermeldung a erhlt. Das ganze ist vergleichbar mit der Konsole1 beim Betriebssystem. In beiden Fllen a 2 , an dem man seine Befehle eingibt und im selben Fenster erscheint gibt es einen Prompt dann die Ausgabe. Uber den MySQL-Prompt hat man natrlich die meisten Mglichkeiten, weil man jeden u o Befehl verwenden kann. Hug ist aber auch ein wenig Untersttzung durch ein Programm a u praktisch, das z. B. die Tabellenstruktur anzeigt oder beim Andern die alten Werte als Vorbelegung nimmt etc. Ein sehr schnes Programm dazu ist PHPMyAdmin, mehr dazu o im Kapitel 7.3. In diesem Kapitel werden wir dialogorientiert arbeiten. Alle Befehle, die hier direkt eingegeben werden, knnen aber auch in eine Text-Datei geschrieben werden, die dann o dialogorientiert abgearbeitet wird. Das nennt man dann Batch-Betrieb. Sehr empfeh lenswert ist dies z. B. fr die Erstellung von Tabellen. Dann kann man nmlich ohne u a groen Aufwand dieselbe Tabellenstruktur in verschiedenen DBs verwenden. Bei der Benutzung durch andere Programme merkt der Benutzer nicht, da eine oder welche Datenbank im Hintergrund arbeitet. So benutzen z. B. alle Suchmaschinen im Internet (Google, AltaVista, Yahoo) in irgendeiner Form eine Datenbank, um die Informationen zu speichern. Als normaler Nutzer sieht man aber nur die Suchmaske und bekommt dann die Ergebnisse schn formatiert angezeigt. Es mu dann ein Programm geben, das die o eingegebene Anfrage an die Datenbank weiterreicht und dann die Ergebnisse formatiert ausgibt. Dieses Programm kann im Prinzip in jeder beliebigen Sprache geschrieben werden. Hua g wird dafr PHP verwendet, was weiter unten noch beschrieben wird. u
5.1 MySQL
Um mit dem DBMS zu reden, mu das Programm mysql von einem Rechner, von dem aus man Zugri auf den Rechner mit dem DBMS hat, gestartet werden. Viele Provider erlauben dies leider aus Sicherheitsgrnden nicht, so da es empfehlenswert ist, sich zu u Hause ein DBMS zu installieren3 . Die mysql-Aufrufsyntax lautet: mysql -p<Pawort> -u <Benutzername> <DB-Name> Fr Pawort, Benutzername und DB-Name sind natrlich die entsprechenden Werte einu u zutragen (ohne die spitzen Klammern!). Diese bekommst du vom Provider, der dir die
1 2
unter Windows: MS-DOS Eingabeauorderung unter DOS z. B. c: > Das schliet natrlich das Starten des Datenbank-Servers mit ein! u
Christoph Reeg
Seite 27
5. SQL benutzen
MySQL
Datenbank zur Verfgung stellt, bzw., im Falle einer eigenen DB, gibst du dir selbst die u Daten. ;-) Alternativ kannst du auch dein Pawort abfragen lassen: mysql -u <Benutzername> -p <DB-Name> Enter password: ******** Wie du siehst, wird in diesem Fall das Pawort explizit abgefragt. Diese Methode ist der Mglichkeit, das Pawort hinter -p anzugeben, aus zwei Grnden vorzuziehen: Zum o u einen erscheint dann das Pawort weder (unter Unix/Linux) in der .bash history noch im Klartext auf dem Bildschirm und zum anderen hat man dann auch nicht das Problem, darauf achten zu mssen, da zwischen -p und dem Pawort kein Leerzeichen stehen darf, u weil nach einem solchen je nach Reihenfolge der Parameter der Datenbankname erwartet wird . . . Wenn du keinen DB-Namen angibst, bekommst du zwar auch den mysql-Prompt, kannst dann aber einige Befehle nicht nutzen, weil du mit keiner Datenbank verbunden wirst. Dann mut du noch mit Hilfe von USE <DB-Name> die Datenbank angeben. Wir beim Jugendnetz richten immer zwei Accounts fr dieselbe DB ein. Einen, fr den u u sowohl Lese- als auch Schreibzugrie erlaubt sind, und einen anderen, der nur lesend zugreifen darf. Der nur lesende Account bekommt ein ro an das Ende des Benutzernamens angehngt. Solltest du einen eigenen Server mit MySQL betreiben, solltest du dir auch a den Abschnitt 7.2 uber MySQL-Nutzer durchlesen. Wenn das alles geklappt hat, kommt folgende Ausgabe (oder ahnlich): Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 1561 to server version: 3.22.32 Type help for help. mysql> Immer dann, wenn in der letzten Zeile ein mysql> steht, kannst du deine Befehle eingeben. Die Gro-/Kleinschreibung ist bei den Befehlen egal, bei den Tabellen- und Spaltennamen (Attribute) sowie den eigentlichen Werten dagegen natrlich nicht! u
Christoph Reeg
Seite 28
5. SQL benutzen
MySQL
5.1.2 Kommentare
Kommentare knnen wie in der Programmiersprache C mit /* und */ umrahmt in die o Datei eingefgt werden. Die beiden folgenden Anweisungen sind identisch: u SELECT * FROM Mitarbeiter; SELECT * /* Was soll alles ausgewhlt werden? */ a FROM /* ... und aus welcher Tabelle? */ Mitarbeiter; Es gibt auch noch # und - - als Kommentarzeichen. Bei diesen wird alles, was hinter dem Zeichen bis zum Zeilenende steht, als Kommentar interpretiert. SELECT * FROM Mitarbeiter; # Wir wollen alles SELECT * FROM Mitarbeiter; -- Diese Zeile macht dasselbe wie die darber u # ist kein Kommentarzeichen nach ANSI-Norm, d.h. andere Datenbanken knnen diese o Kommentare nicht erkennen.
Christoph Reeg
Seite 29
6 SQL-Befehle
Das gute Gedchtnis ist wie ein Sack, a es behlt alles. a Das bessere Gedchtnis ist wie ein a Sieb; es behlt nur, worauf es a ankommt. Helmut Walters
Sobald man mit dem DBMS verbunden ist, kann man die Befehle eingeben, die auch von PHP aus aufgerufen werden. Bei der dialogorientierten Benutzung und im Batch-Betrieb mssen die Befehle immer u mit einem ; abgeschlossen werden. Man kann ohne Probleme einen Befehl in mehrere Zeilen schreiben, indem man erst am Ende das Semikolon setzt. Als theoretische Grundlage wird die Beispiel-Datenbank, die in Kapitel 4.6 beschrieben wurde, benutzt.
Christoph Reeg
Seite 30
6. SQL-Befehle
CREATE TABLE
Nach dem Einrichten der Datenbank ist diese bereits benutzbar, d. h. du kannst jetzt Tabellen mit entsprechenden Feldern anlegen (mehr dazu im nchsten Abschnitt: 6.2). a Arbeitest du hingegen auf dem mysql-Prompt, mut du zuerst die Datenbank wechseln: das geht mittels USE <DB-Name>.
Christoph Reeg
Seite 31
6. SQL-Befehle
CREATE TABLE
Tabelle 6.1: Verfgbare Datentypen in SQL u Typ TINYINT TINYINT UNSIGNED INT INT UNSIGNED BIGINT DECIMAL(length,dec) Beschreibung -128 .. 127 0 .. 255 -2.147.483.648 .. 2.147.483.647 0 .. 4.294.967.295 -3402823e+31 .. 3402823e+31 Kommazahl der Lnge length und mit dec Dezimala stellen; die Lnge betrgt: Stellen vor dem Komma + a a 1 Stelle fr Komma + Stellen nach dem Komma u Zeichenkette mit max NUM Stellen (1 N U M 255). Alle Leerstellen am Ende werden gelscht. Soo lange nicht BINARY angegeben wurde, wird bei Vergleichen nicht auf Gro-/Kleinschreibung geachtet. Text mit einer max. Lnge von 65535 Zeichen a Text mit einer max. Lnge von 16.777.216 Zeichen a Zeit; Format: HH:MM:SS, HHMMSS, HHMM oder HH Datum; Format: YYYY-MM-DD, wobei - jedes nicht numerische Zeichen sein kann setzt einen Datumswert beim Einfgen/Updaten einu zelner Felder automatisch auf das Systemdatum. Format: YYYYMMDDHHMMSS. Wenn mehrere Felder den Typ TIMESTAMP haben, wird immer nur das erste automatisch gendert! a
VARCHAR(NUM) [BINARY]
Tabelle 6.2: Bedeutung der YMHSDs D H M S Y Tag (engl. day) Stunde (engl. hour) Monat (engl. month) oder Minute (engl. minute) Sekunde (engl. second) Jahr (engl. year)
Christoph Reeg
Seite 32
6. SQL-Befehle
SHOW
Die Bedeutung der YMHSDs ist in der Tabelle 6.2 erlutert. a Wenn ein Buchstabe mehrmals vorkommt, so bedeutet das, da es mehrere Stellen gibt. Ein kleines Beispiel: Es soll eine Tabelle Mitarbeiter erstellt werden. Zu jedem Mitarbeiter sind Name, Telefonnummer und die Mitarbeiter-Nummer (kurz MNr) zu speichern. Die MNr ist Primra schlssel und soll automatisch um 1 erhht werden. Der Name ist nicht optional. Daraus u o ergibt sich folgender Befehl: CREATE TABLE Mitarbeiter ( MNr INT NOT NULL AUTO_INCREMENT, VNr INT, AbtNr INT NOT NULL, Name VARCHAR(30) NOT NULL, GebDat DATE, Telefon VARCHAR(30), PRIMARY KEY(MNr), FOREIGN KEY(VNr) REFERENCES Mitarbeiter(MNr), FOREIGN KEY(AbtNr) REFERENCES Abteilung(AbtNr) ); Die mehrfachen Leerstellen sind optional. Sie sind hier nur der Ubersichtlichkeit halber eingefgt. u Wenn man versucht, die Mitarbeiter-Tabelle bei einem DBMS, das Fremdschlssel unu tersttzt, als erstes anzulegen, wird man eine Fehlermeldung erhalten, weil die referenzierte u Tabelle noch nicht existiert. In diesem Fall kann man die Tabellenstruktur nachtrglich a mit ALTER TABLE (6.11) anpassen. Im Anhang A.1 sind noch einmal smtliche CREATE TABLE-Denitionen aufgefhrt, a u die fr unser Beispiel bentigt werden. Es sind der Vollstndigkeit halber auch smtliche u o a a Fremdschlssel mit angegeben, obwohl sie unter MySQL keine Bedeutung haben. u
6.3 SHOW
Man kann sich die nun erstellten Tabellen und deren Felder auch ansehen. Dazu dient der Befehl SHOW. Die Syntax lautet wie folgt (nur MySQL): SHOW TABLES oder SHOW COLUMNS FROM table SHOW TABLES zeigt alle angelegten Tabellen an. Mit SHOW COLUMNS FROM table lassen sich die Felder in der Tabelle table anzeigen. Nach unserem obigen CREATE TABLE wrde sich folgende Ausgabe ergeben: u mysql> SHOW TABLES; +--------------+ | Tables in cr |
Christoph Reeg
Seite 33
6. SQL-Befehle
DROP TABLE
+--------------+ | Mitarbeiter | +--------------+ 1 row in set (0.01 sec) mysql> SHOW COLUMNS FROM Mitarbeiter; +---------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+----------------+ | MNr | int(11) | | PRI | 0 | auto_increment | | VNr | int(11) | YES | | NULL | | | AbtNr | int(11) | | | 0 | | | Name | varchar(30) | | | | | | GebDat | date | YES | | NULL | | | Telefon | varchar(30) | YES | | NULL | | +---------+-------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) In der unteren Tabelle ist zu erkennen, welche Felder mit welchen Typen und Attributen in der jeweiligen Tabelle vorhanden sind. Nur fr VNr, GebDat und Telefon sind u NULL-Werte zugelassen. Der Primrschlssel ist MNr. Wie man sieht, wurden die Fremda u schlssel ignoriert. u
Christoph Reeg
Seite 34
6. SQL-Befehle
SELECT
Bei den Werten mssen Zeichenketten und Datum in Hochkommata (Anfhrungszeiu u chen) stehen, nur fr Zahlen gilt das nicht. u In unsere oben erstellte Tabelle sollen folgende Werte eingefgt werden: u Name Christoph Reeg junetz.de GebDat 13.5.1979 5.3.1998 Telefon 069/764758
Damit ergeben sich folgende Befehle und Ausgaben (wie man hier bereits sieht, gibt der MySQL-Prompt automatisch die Zeichenfolge -> aus, wenn ein mehrzeiliger Befehl eingegeben wird): mysql> INSERT INTO Mitarbeiter (Name,GebDat) -> VALUES (Christoph Reeg,1979-5-13); Query OK, 1 row affected (0.01 sec) mysql> INSERT INTO Mitarbeiter (Name,GebDat,Telefon) -> VALUES (junetz.de,1998-3-5,069/764758); Query OK, 1 row affected (0.00 sec) Wegen fehlender Fremdschlssel konnten die Mitarbeiter in die DB eingefgt werden, u u obwohl wir keine Abteilung fr sie angegeben haben. Deshalb mu man immer selbst fr u u die Einhaltung der Beziehung(en) sorgen! Um die Datenbasis fr unser Beispiel in die DB einzutragen, wird der Befehl noch ein u paarmal benutzt. Diese konkrete Anwendung kann im Anhang A.2 nachgeschlagen werden. Bei den folgenden Kurzbeispielen gehen wir von der Datenbasis unseres Beispiels aus.
6.6 SELECT
Der Befehl SELECT ist der mchtigste Befehl in SQL. Die Grund-Syntax lautet: a SELECT [WHERE [GROUP [ORDER [LIMIT [DISTINCT | ALL] select_expression,... FROM tables ... where_definition] BY feld_name,...] BY feld_name [ASC | DESC] ,...] [offset,] rows]
Die krzestmgliche SELECT-Anweisung lautet: u o SELECT * FROM table Es sollen z. B. alle Mitarbeiter ausgegeben werden: mysql> select * from Mitarbeiter; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL |
Christoph Reeg
Seite 35
6. SQL-Befehle
SELECT
| 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | | 3 | 1 | 1 | Uli | NULL | NULL | | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | +-----+------+-------+----------------+------------+--------------+ 6 rows in set (0.00 sec) mysql> DISTINCT und ALL sind exklusive, optionale Parameter; soll heien, es kann immer nur einer, mu aber keiner benutzt werden. DISTINCT sorgt dafr, da jede identische u Zeile nur einmal ausgegeben wird. Mit ALL werden die sich wiederholenden Werte auch mehrmals ausgegeben. Ohne Parameter verhlt sich das DBMS normalerweise, als ob man a ALL angeben wrde. u Es sollen alle Telefonnummern aus der Mitarbeiter-Tabelle ausgeben werden: mysql> SELECT Telefon from Mitarbeiter; +--------------+ | Telefon | +--------------+ | NULL | | 069/764758 | | NULL | | 069/764758 | | 06196/671797 | | 069/97640232 | +--------------+ 6 rows in set (0.01 sec) mysql> SELECT ALL Telefon from Mitarbeiter; +--------------+ | Telefon | +--------------+ | NULL | | 069/764758 | | NULL | | 069/764758 | | 06196/671797 | | 069/97640232 | +--------------+ 6 rows in set (0.00 sec) mysql> SELECT DISTINCT Telefon from Mitarbeiter; +--------------+ | Telefon | +--------------+
Christoph Reeg
Seite 36
6. SQL-Befehle
SELECT
| NULL | | 06196/671797 | | 069/764758 | | 069/97640232 | +--------------+ 4 rows in set (0.05 sec) mysql>
6.6.1 ORDER BY
Mit ORDER BY wird festgelegt, nach welcher Spalte bzw. welchen Spalten sortiert werden soll. Mit ASC 1 werden die Zeilen aufsteigend, mit DESC 2 absteigend sortiert. Ist nichts angegeben, wird aufsteigend sortiert. Als Beispiel alle Mitarbeiter, nach Namen sortiert: mysql> SELECT * from Mitarbeiter ORDER BY Name; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | | 3 | 1 | 1 | Uli | NULL | NULL | +-----+------+-------+----------------+------------+--------------+ 6 rows in set (0.00 sec) mysql> SELECT * from Mitarbeiter ORDER BY Name ASC; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | | 3 | 1 | 1 | Uli | NULL | NULL | +-----+------+-------+----------------+------------+--------------+ 6 rows in set (0.01 sec) mysql> SELECT * from Mitarbeiter ORDER BY Name DESC; +-----+------+-------+----------------+------------+--------------+
1 2
Christoph Reeg
Seite 37
6. SQL-Befehle
SELECT
| MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 3 | 1 | 1 | Uli | NULL | NULL | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | +-----+------+-------+----------------+------------+--------------+ 6 rows in set (0.00 sec) mysql> Als letztes soll nach dem Geburtsdatum sortiert werden. mysql> SELECT * from Mitarbeiter ORDER BY GebDat; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 3 | 1 | 1 | Uli | NULL | NULL | | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | +-----+------+-------+----------------+------------+--------------+ 6 rows in set (0.01 sec) mysql> Die ersten vier Mitarbeiter haben kein Geburtsdatum eingetragen. Um sie dennoch irgendwie zu sortieren, ist bei ihnen ein zweites Sortierkriterium notwendig. Das kann einfach mit einem Komma getrennt hinter ORDER BY geschrieben werden. Um zum Beispiel nach Geburtsdatum und, wenn das nicht eindeutig ist, dann nach Namen zu sortieren, ist folgende Anweisung notwendig: mysql> SELECT * from Mitarbeiter ORDER BY GebDat,Name; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | | 3 | 1 | 1 | Uli | NULL | NULL | | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | +-----+------+-------+----------------+------------+--------------+
Christoph Reeg
Seite 38
6. SQL-Befehle
SELECT
6.6.2 GROUP BY
Die GROUP BY-Anweisung wird nur in Verbindung mit den Gruppenfunktionen aus Kapitel 6.7.5 benutzt, um mehrere Tupel mit identischen Attributen zu Gruppen zusammenzufassen. Ein kleines Beispiel: Es soll ausgegeben werden, wie viele Mitarbeiter in den jeweiligen Abteilungen arbeiten. mysql> SELECT count(*), AbtNr from Mitarbeiter GROUP BY AbtNr; +----------+-------+ | count(*) | AbtNr | +----------+-------+ | 3 | 1 | | 2 | 2 | | 1 | 3 | +----------+-------+ 3 rows in set (0.00 sec) mysql> Die sechs Tupel (Datenstze), die wir in der Mitarbeiter-Tabelle haben, werden zu drei a Gruppen zusammengefat; anschlieend wird die Anzahl der Tupel pro Gruppe ausgeben. Eigentlich drfen in der select_expression nur Spalten angegeben werden, die in u GROUP BY auftauchen, MySQL ignoriert dies allerdings. Folgende Anweisung wre eigenta lich unzulssig, zudem ist sie vllig sinnlos - was besagt schon die Spalte Name? a o mysql> SELECT Name,count(*),AbtNr from Mitarbeiter GROUP BY AbtNr; +----------------+----------+-------+ | Name | count(*) | AbtNr | +----------------+----------+-------+ | junetz.de | 3 | 1 | | Maier | 2 | 2 | | Christoph Reeg | 1 | 3 | +----------------+----------+-------+ 3 rows in set (0.00 sec) mysql>
6.6.3 LIMIT
Mit LIMIT [offset,] rows kann angegeben werden, wie viele Zeilen man maximal von der SELECT-Anweisung zurckgeliefert haben will. Mit oset kann man festlegen, ab u welcher Zeile angefangen werden soll. Ohne Angaben wird 0 angenommen. Mit rows legt man fest, wie viele Zeilen man maximal ausgegeben haben mchte. o
Christoph Reeg
Seite 39
6. SQL-Befehle
SELECT
mysql> select * from table LIMIT 5,10; mysql> select * from table LIMIT 5; mysql> select * from table LIMIT 0,5;
# gibt die Zeilen 6-15 zurck u # gibt die ersten 5 Zeilen zurck u # dasselbe nochmal
Christoph Reeg
Seite 40
6. SQL-Befehle
SELECT
6.6.5 Alias
Alias bedeutet so viel wie ein anderer Name. Man kann sowohl fr Spalten als auch fr u u Tabellen Aliase denieren. 6.6.5.1 Tabellen-Alias Tabellen-Aliase knnen sowohl bei der select_expression als auch bei der wheo re denition zur eindeutigen Spaltenbeschreibung anstelle des Tabellennamens verwendet werden. Aliase werden verwendet, weil sie in der Regel krzer sind als der Spaltennau me. Aliase werden bei tables mit einer Leerstelle getrennt hinter dem Tabellennamen eingegeben. Die folgenden zwei Anweisungen sind vllig identisch, abgesehen davon, da erstere o krzer ist. Der einzige Unterschied liegt in den ersten beiden Zeilen. Im ersten Beiu spiel wird bei der FROM-Anweisung ein Alias deniert, welches in der ersten Zeile bei der select_expression benutzt wird. mysql> select M.Name, M.Telefon, M.AbtNr -> FROM Mitarbeiter M -> WHERE M.AbtNr = 1; +-----------+------------+-------+ | Name | Telefon | AbtNr | +-----------+------------+-------+ | junetz.de | 069/764758 | 1 | | Uli | NULL | 1 | | JCP | 069/764758 | 1 | +-----------+------------+-------+ 3 rows in set (0.00 sec) mysql> select Mitarbeiter.Name, Mitarbeiter.Telefon, -> Mitarbeiter.AbtNr -> FROM Mitarbeiter -> WHERE Mitarbeiter.AbtNr = 1; +-----------+------------+-------+ | Name | Telefon | AbtNr | +-----------+------------+-------+ | junetz.de | 069/764758 | 1 | | Uli | NULL | 1 | | JCP | 069/764758 | 1 | +-----------+------------+-------+ 3 rows in set (0.01 sec) mysql> 6.6.5.2 Spalten-Alias Wenn man die Uberschrift der Spalten ndern will, braucht man Spalten-Aliase. Auf den a ersten Blick mag es vielleicht egal sein, wie die Spalten heien. Spter in Verbindung mit a
Christoph Reeg
Seite 41
6. SQL-Befehle
SELECT
PHP ist das allerdings wichtig und sobald Funktionen in der Abfrage verwendet werden, sind die Spaltenberschriften auch nicht mehr so schn. u o Zum Umbenennen der Spalten wird einfach hinter den Spaltennamen bzw. Ausdruck der Aliasname geschrieben. Alternativ kann auch AS aliasname benutzt werden. Ein kleines Beispiel: Es soll die Anzahl der Mitarbeiter ausgegeben werden. Dazu wird eine Funktion bentigt o (siehe Kapitel 6.7, das soll uns aber nicht weiter stren). o mysql> SELECT count(*) -> FROM Mitarbeiter; +----------+ | count(*) | +----------+ | 6 | +----------+ 1 row in set (0.00 sec) mysql> Das einzige strende ist, da die Spalte count(*) heit. Viel schner wre es doch, wenn o o a sie z. B. Anzahl heien wrde: u mysql> SELECT count(*) Anzahl -> FROM Mitarbeiter; +--------+ | Anzahl | +--------+ | 6 | +--------+ 1 row in set (0.00 sec) mysql> SELECT count(*) AS Anzahl -> FROM Mitarbeiter; +--------+ | Anzahl | +--------+ | 6 | +--------+ 1 row in set (0.00 sec) mysql> Wie man unschwer erkennen kann, ist das AS optional.
Christoph Reeg
Seite 42
6. SQL-Befehle
SELECT
Die where denition wird auch beim Lschen (DELETE) und Andern (UPDATE) von o ausgewhlten Datenstzen gebraucht. a a Eine Bedingung kann aus mehreren Teilbedingungen, die mit AND und OR verknpft u werden mssen, bestehen. Eine Teilbedingung besteht aus einem Spaltennamen, einem u Operator sowie entweder einer Konstanten, einer weiteren Spalte oder einer Funktion. Die Teilbedingungen knnen auch mit NOT verneint werden. Schreibfaule knnen an Stelle o o von NOT auch einfach ! verwenden; das Ergebnis ist dasselbe. Die Reihenfolge der Teilbedingungen kann durch Klammern beeinut werden. Als Operatoren stehen die Vergleichsoperatoren sowie die Operatoren LIKE, BETWEEN und IN zur Auswahl. Alle Vergleichsoperatoren aus Tabelle 6.3 stehen zur Verfgung. Bei Vergleichen mit Strings u (=VARCHAR) wird im Normalfall nicht auf die Gro-/Kleinschreibung geachtet. Wenn man jedoch unterscheiden will, so mu beim Anlegen der Tabelle bei VARCHAR die Option BINARY angegeben werden. Tabelle 6.3: Verfgbare Vergleichsoperatoren in SQL u Operator = <> oder != > < >= <= Bedeutung gleich ungleich grer o kleiner grer gleich o kleiner gleich verneinender Operator <> bzw. != = <= >= < >
Es sollen alle Mitarbeiter ausgegeben werden, bei denen die Abteilungsnummer grer o als 2 ist: mysql> SELECT Name, AbtNr -> FROM Mitarbeiter -> WHERE AbtNr > 2; +----------------+-------+ | Name | AbtNr | +----------------+-------+ | Christoph Reeg | 3 | +----------------+-------+ 1 row in set (0.00 sec) mysql> SELECT Name, AbtNr -> FROM Mitarbeiter -> WHERE NOT (AbtNr < 3); +----------------+-------+ | Name | AbtNr | +----------------+-------+ | Christoph Reeg | 3 | +----------------+-------+ 1 row in set (0.00 sec)
Christoph Reeg
Seite 43
6. SQL-Befehle
SELECT
mysql> Es sollen alle Mitarbeiter ausgegeben werden, die der Abteilung 1 angehren und als o Vorgesetztennummer ebenfalls die 1 haben. mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE AbtNr = 1 -> AND VNr = 1; +-----------+-------+------+ | Name | AbtNr | VNr | +-----------+-------+------+ | junetz.de | 1 | 1 | | Uli | 1 | 1 | +-----------+-------+------+ 2 rows in set (0.02 sec) mysql> Es sollen alle Mitarbeiter ausgegeben werden, die keinen Vorgesetzten haben oder der Abteilung 1 angehren: o mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE AbtNr = 1 -> OR VNr IS NULL; +----------------+-------+------+ | Name | AbtNr | VNr | +----------------+-------+------+ | Christoph Reeg | 3 | NULL | | junetz.de | 1 | 1 | | Uli | 1 | 1 | | JCP | 1 | 3 | +----------------+-------+------+ 4 rows in set (0.01 sec) mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE NOT (AbtNr <> 1) -> OR VNr IS NULL; +----------------+-------+------+ | Name | AbtNr | VNr | +----------------+-------+------+ | Christoph Reeg | 3 | NULL | | junetz.de | 1 | 1 | | Uli | 1 | 1 |
Christoph Reeg
Seite 44
6. SQL-Befehle
SELECT
| JCP | 1 | 3 | +----------------+-------+------+ 4 rows in set (0.00 sec) mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE NOT (AbtNr <> 1 AND VNr IS NOT NULL); +----------------+-------+------+ | Name | AbtNr | VNr | +----------------+-------+------+ | Christoph Reeg | 3 | NULL | | junetz.de | 1 | 1 | | Uli | 1 | 1 | | JCP | 1 | 3 | +----------------+-------+------+ 4 rows in set (0.00 sec) mysql> Bei der Uberprfung auf keinen Vorgesetzten ist IS NULL verwendet worden, da bei Veru gleichen mit NULL-Werten nicht mit den normalen Operatoren gearbeitet werden kann. Statt dessen ist nur IS NULL oder, verneint, IS NOT NULL mglich. o Es gibt mit Sicherheit noch 1001 andere Mglichkeiten, auf diese Lsung zu kommen. o o Da der erste Korrekturleser mir das nicht glauben wollte, hier noch ein paar Mglichkeiten o mehr: mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE NOT (AbtNr < 1 OR AbtNr > 1) -> OR VNr IS NULL; +----------------+-------+------+ | Name | AbtNr | VNr | +----------------+-------+------+ | Christoph Reeg | 3 | NULL | | junetz.de | 1 | 1 | | Uli | 1 | 1 | | JCP | 1 | 3 | +----------------+-------+------+ 4 rows in set (0.00 sec) mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE (AbtNr <= 1 AND AbtNr >= 1) -> OR VNr IS NULL; +----------------+-------+------+ | Name | AbtNr | VNr | +----------------+-------+------+
Christoph Reeg
Seite 45
6. SQL-Befehle
SELECT
| Christoph Reeg | 3 | NULL | | junetz.de | 1 | 1 | | Uli | 1 | 1 | | JCP | 1 | 3 | +----------------+-------+------+ 4 rows in set (0.01 sec) mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE AbtNr & 1 = 1 -> OR VNr IS NULL; +----------------+-------+------+ | Name | AbtNr | VNr | +----------------+-------+------+ | Christoph Reeg | 3 | NULL | | junetz.de | 1 | 1 | | Uli | 1 | 1 | | JCP | 1 | 3 | +----------------+-------+------+ 4 rows in set (0.00 sec) mysql> SELECT Name, AbtNr, VNr -> FROM Mitarbeiter -> WHERE POW(13,AbtNr) = 13 -> OR POW(VNr,42) IS NULL; +----------------+-------+------+ | Name | AbtNr | VNr | +----------------+-------+------+ | Christoph Reeg | 3 | NULL | | junetz.de | 1 | 1 | | Uli | 1 | 1 | | JCP | 1 | 3 | +----------------+-------+------+ 4 rows in set (0.02 sec) mysql> Es sind noch nicht 1001 Mglichkeiten, aber wenn ich vieeel Zeit habe, werde ich daran o weiterarbeiten ;-). Wer sich die beiden letzten Lsungen genau angesehen hat, wird festo stellen, da dort unbekannte Befehle verwendet werden; in Kapitel 6.7 werden diese noch genauer beschrieben. Bei der letzten Mglichkeit sieht man, was passiert, wenn mit NULL-Werten gerechnet o wird: Das Ergebnis ist NULL.
Christoph Reeg
Seite 46
6. SQL-Befehle
SELECT
6.6.6.1 LIKE Immer dann, wenn man in Textfeldern im Suchmuster Platzhalter oder Jokerzeichen (auch o regulre Ausdrcke3 genannt) verwenden will, knnen die Vergleichsoperatoren nicht vera u wendet werden. Ein Beispiel zur Verdeutlichung: Es sollen alle Mitarbeiter ausgegeben werden, bei denen die Telefon-Vorwahl auf 96 endet. Falls du eine Mglichkeit ndest, das mit Vergleichsoperatoren (und ohne Funktioo nen) zu lsen, schicke mir bitte eine E-Mail an die Adresse dsp@reeg.net; ich bevorzuge o ubrigens den Operator LIKE. mysql> SELECT Name, Telefon -> FROM Mitarbeiter -> WHERE Telefon LIKE %96/%; +-------+--------------+ | Name | Telefon | +-------+--------------+ | Maier | 06196/671797 | +-------+--------------+ 1 row in set (0.00 sec) mysql> Es soll ein Mitarbeiter mit Namen Meier ausgegeben werden. Allerdings ist unbekannt, ob er sich mit ei oder ai schreibt. Es sollen also alle Mglichkeiten ausgegeben werden. o mysql> SELECT Name -> FROM Mitarbeiter -> WHERE Name LIKE M_ier; +-------+ | Name | +-------+ | Maier | | Meier | +-------+ 2 rows in set (0.00 sec) mysql> Wie wir sehen, gibt es zwei Jokerzeichen: % DOS-Pendant: * steht fr eine beliebige (auch 0) Anzahl beliebiger Zeichen u DOS-Pendant: ? steht fr genau ein beliebiges Zeichen u 6.6.6.2 BETWEEN Neben dem LIKE-Operator gibt es auch noch andere, zum Beispiel den BETWEENOperator. Er tut das, was man von ihm erwartet: er whlt alle Spalten aus, die zwischen a dem oberen und unteren Wert liegen.
3
Christoph Reeg
Seite 47
6. SQL-Befehle
SELECT
Beispiel: Es sollen alle Mitarbeiter ausgewhlt werden, deren Abteilungs-Nummer zwia schen 2 und 5 liegt. Mit dem bisherigen Wissen wrde man folgende Anweisung nehmen: u mysql> SELECT * FROM Mitarbeiter -> WHERE AbtNr >= 2 AND AbtNr <=5; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | +-----+------+-------+----------------+------------+--------------+ 3 rows in set (0.01 sec) mysql> Man kann es aber noch etwas vereinfachen: mysql> SELECT * FROM Mitarbeiter -> WHERE AbtNr BETWEEN 2 AND 5; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | +-----+------+-------+----------------+------------+--------------+ 3 rows in set (0.00 sec) mysql> BETWEEN kann bei Textspalten, Datumsspalten und numerischen Spalten verwendet werden. 6.6.6.3 IN Der Operator IN schlielich wird benutzt, wenn man nicht mit einem einzelnen Wert, sondern mit einer Wertemenge vergleichen will. Zur Verdeutlichung: Es sollen alle Mitarbeiter ausgegeben werden, deren Telefonnummer 06196/671797 oder 069/764758 ist. Mit den bisherigen Operatoren wrde sich folgende u Abfrage ergeben: mysql> SELECT * FROM Mitarbeiter -> WHERE Telefon = 06196/671797 OR Telefon = 069/764758; +-----+------+-------+-----------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+-----------+------------+--------------+ | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 |
Christoph Reeg
Seite 48
6. SQL-Befehle
SELECT
| 4 | 3 | 1 | JCP | NULL | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | +-----+------+-------+-----------+------------+--------------+ 3 rows in set (0.00 sec) mysql> Das funktioniert zwar, aber da diese Mglichkeit bei groen Mengen von Werten sehr o umstndlich und unbersichtlich wird, hier das ganze nochmal mit dem IN-Operator: a u mysql> SELECT * FROM Mitarbeiter -> WHERE Telefon IN (06196/671797,069/764758); +-----+------+-------+-----------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+-----------+------------+--------------+ | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | +-----+------+-------+-----------+------------+--------------+ 3 rows in set (0.00 sec) mysql> Der IN-Operator kann bei Textspalten, Datumsspalten und numerischen Spalten verwendet werden. Die Verneinung des IN-Operators ist NOT IN. Als Beispiel sollen alle Mitarbeiter ausgegeben werden, deren Telefonnummer nicht 06196/671797 oder 069/97640232 ist. Erst umstndlich mit OR und dann elegant. . . a mysql> SELECT * FROM Mitarbeiter -> WHERE NOT (Telefon = 06196/671797 OR Telefon = 069/764758); +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 3 | 1 | 1 | Uli | NULL | NULL | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | +-----+------+-------+----------------+------------+--------------+ 3 rows in set (0.00 sec) mysql> SELECT * FROM Mitarbeiter -> WHERE Telefon NOT IN (06196/671797,069/764758); +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 3 | 1 | 1 | Uli | NULL | NULL |
Christoph Reeg
Seite 49
6. SQL-Befehle
Funktionen
6.7 Funktionen
Bei select_expression und where_expression knnen neben Konstanten und Spalo tenwerten auch Funktionen verwendet werden. Es gibt zwei Arten von Funktionen, zum einen die sog. singlerow-Funktionen und zum anderen die Gruppenfunktionen. Singlerow Funktionen werden auf jede Zeile angewendet, whrend die Gruppenfunktionen immer auf a eine Gruppe von Zeilen angewendet werden. Es wre zum Beispiel Schwachsinn, zu versuchen, den Betrag von allen Stellenanzahlen a auf einmal zu berechnen; bei der Summe pat das schon eher.
Christoph Reeg
Seite 50
6. SQL-Befehle
Funktionen
logisches NOT. Wird genau dann wahr (gibt 1 zurck), wenn u das Argument 0 ist (sonst Rckgabe 0). NOT NULL gibt NULL zurck. u u logisches UND. Wird genau dann wahr (gibt 1 zurck), wenn u alle Argumente weder 0 noch NULL sind (sonst Rckgabe 0). u logisches ODER. Wird genau dann wahr (gibt 1 zurck), wenn u eines der Argumente weder 0 noch NULL ist (sonst Rckgabe 0). u Tabelle 6.5: Logische Funktionen in SQL
6.7.4 Datums-Funktionen
Mit Hilfe von DATE_FORMAT kann man Datumswerte aus Tabellen so formatieren, wie man sie gerne htte. Die Funktion erwarten zwei Parameter. Zum einen das Datumsfeld, zum a anderen den Formatierungs-String. Die Formatierungszeichen (siehe Tabelle 6.9) werden durch die entsprechenden Werte ersetzt. Alle anderen Zeichen werden so wie sie sind ausgegeben. In PHP gibt es auch eine Datum-Formatierungsfunktion. Ob man nun mit der MySQLFunktion das Datum formatiert und dann mit PHP ausgibt oder mit Hilfe der PHPFunktion das Datum formatiert, ist hug egal. Teilweise ist es praktischer, mit Hilfe der a MySQL-Funktion das Datum zu formatieren, weil man dann die Formatierungsanweisung in der SQL-Abfrage hat und mit PHP nur ausgeben mu. Andererseits kann es aber auch praktischer sein, mit PHP zu formatieren, wenn man zum Beispiel dasselbe Datum an
Christoph Reeg
Seite 51
6. SQL-Befehle
Funktionen
Gibt den Wochentag-Index des Datums zurck (1 u = Sonntag, 2 = Montag, . . . , 7 = Samstag) Gibt den Tag des Monats zurck u Gibt den Tag im Jahr zurck u Gibt die Woche des Datums zurck. u Wenn rst nicht angegeben wird bzw. 0 ist, fngt a die Woche mit Sonntag an. Ist rst z. B. 1, fngt a die Woche mit Montag an. Gibt den Monat zurck u Gibt das Jahr zurck u Formatiert das Datum date entsprechend dem ubergebenen format String. Gibt den Unix-Timestamp (Sekunden seit dem 1.1.1970) des Datums date zurck. u
Tabelle 6.8: Datum-Funktionen in SQL mehreren Stellen auf der Seite verschieden formatiert haben will. Wie gesagt, es kommt auf den Einzelfall und die Vorlieben des Programmierers an.
6.7.5 Gruppenfunktionen
Es knnen aber auch die sogenannten Gruppenfunktionen verwendet werden. Gruppeno funktionen heien so, weil sie immer auf eine Gruppe von Tupeln (Datenstzen) angewena det werden. In Verbindung mit Gruppenfunktionen darf streng genommen kein Spaltenname mehr mit in der select_expression stehen. Die einzige Ausnahme ist dann gegeben, wenn der Spaltenname in der GROUP BY-Anweisung steht. MySQL sieht das etwas lockerer und gibt keine Fehlermeldung bei Verwendung von Spaltennamen in der select_expression aus, allerdings hat dies im Normalfall wenig Sinn. In Tabelle 6.10 sind ein Teil der verfgbaren Gruppenfunktionen aufgefhrt. u u Fr expr ist immer der Name der Spalte einzusetzen, mit der diese Operation erfolgen u soll. Beispiel: Es soll ausgegeben werden, wie viele Mitarbeiter ihren Geburtstag angegeben haben. mysql> SELECT count(GebDat) -> FROM Mitarbeiter; +---------------+ | count(GebDat) | +---------------+ | 2 | +---------------+ 1 row in set (0.27 sec) mysql>
Christoph Reeg
Seite 52
6. SQL-Befehle
Funktionen
%W %w %d %e %j %U %u %M %m %c %Y %y %T %S %s %i %H %k %h %I %l %%
Wochentag Tag in der Woche (0 = Sonntag, . . . , 6=Samstag) Tag des Monats (00 - 31) Tag des Monats (0 - 31) Tag im Jahr (001 - 366) Woche, mit Sonntag als 1. Tag der Woche (00 - 52) Woche, mit Montag als 1. Tag der Woche (00 - 52) Monatsname Monat, numerisch (01 - 12) Monat, numerisch (1 - 12) Jahr (4stellig) Jahr (2stellig) Uhrzeit (24 Std.) (hh:mm:ss) Sekunden (00 - 59) Sekunden (00 - 59) Minuten (00 - 59) Stunde (00 - 23) Stunde (0 - 23) Stunde (00 - 12) Stunde (00 - 12) Stunde (0 - 12) % Tabelle 6.9: mgl. Formatierungen fr DATE FORMAT o u
zhlt die Zeilen, deren Werte ungleich NULL sind a durchschnittlicher Wert kleinster Wert grter Wert o Summe Tabelle 6.10: Gruppenfunktionen in SQL
Christoph Reeg
Seite 53
6. SQL-Befehle
Joins
6.8 Joins
Nachdem wir jetzt die Tabellen perfekt abfragen knnen, wollen wir mal ein paar Tao bellen miteinander verbinden. Nach unserer Normalisierung (siehe Kapitel 4.4) benden sich einige Informationen nmlich in verschiedenen Tabellen, obwohl sie eigentlich zusama mengehren. Zum Beispiel wrde ich gerne alle Mitarbeiter mit deren Abteilungen sehen. o u Hierbei aber nicht die Abteilungsnummer, sondern den Namen der Abteilung. Nach dem, was wir bis jetzt wissen, wrden wir folgende Abfrage starten: u mysql> SELECT m.Name, m.AbtNr, a.Name, a.AbtNr -> FROM Mitarbeiter m, Abteilung a; +----------------+-------+------------+-------+ | Name | AbtNr | Name | AbtNr | +----------------+-------+------------+-------+ | Christoph Reeg | 3 | EDV | 1 | | junetz.de | 1 | EDV | 1 | | Uli | 1 | EDV | 1 | | JCP | 1 | EDV | 1 | | Maier | 2 | EDV | 1 | | Meier | 2 | EDV | 1 | | Christoph Reeg | 3 | Verwaltung | 2 | | junetz.de | 1 | Verwaltung | 2 | | Uli | 1 | Verwaltung | 2 | | JCP | 1 | Verwaltung | 2 | | Maier | 2 | Verwaltung | 2 | | Meier | 2 | Verwaltung | 2 | | Christoph Reeg | 3 | Chefetage | 3 | | junetz.de | 1 | Chefetage | 3 | | Uli | 1 | Chefetage | 3 | | JCP | 1 | Chefetage | 3 | | Maier | 2 | Chefetage | 3 | | Meier | 2 | Chefetage | 3 | +----------------+-------+------------+-------+ 18 rows in set (0.07 sec) mysql>
6.8.1 Equi-Join
Die obige Abfrage ergibt allerdings nicht ganz das erwnschte Resultat. Bei dieser Art u der Abfrage entsteht nmlich das kartesische Produkt, was so viel bedeutet wie jeder a mit jedem. Wie wir oben jedoch unschwer erkennen knnen, gehren nur die Mitarbeiter o o und Abteilungen zusammen, deren AbtNr ubereinstimmen, deshalb hatten wir diese auch eingefgt. Eine entsprechend modizierte Abfrage wrde demnach folgendermaen lauten: u u mysql> SELECT m.Name, m.AbtNr, a.Name, a.AbtNr -> FROM Mitarbeiter m, Abteilung a
Christoph Reeg
Seite 54
6. SQL-Befehle
Joins
-> WHERE m.AbtNr = a.AbtNr; +----------------+-------+------------+-------+ | Name | AbtNr | Name | AbtNr | +----------------+-------+------------+-------+ | junetz.de | 1 | EDV | 1 | | Uli | 1 | EDV | 1 | | JCP | 1 | EDV | 1 | | Maier | 2 | Verwaltung | 2 | | Meier | 2 | Verwaltung | 2 | | Christoph Reeg | 3 | Chefetage | 3 | +----------------+-------+------------+-------+ 6 rows in set (0.00 sec) mysql> Vor den Spaltennamen mu jeweils die entsprechende Tabelle genannt werden, da die Namen nicht eindeutig sind. Um nicht jedes Mal den kompletten Tabellennamen benutzen zu mssen, wurden Aliase verwendet (siehe Kapitel 6.6.5). u Uber die AbtNr entsteht die Verbindung zwischen den beiden Tabellen. Falls bei der Verknpfung von mehreren Tabellen nicht das gewnschte Ergebnis erscheint, fehlt hug u u a eine WHERE-Bedingung, so da bei einigen Tabellen das kartesische Produkt entsteht. In solchen Fllen kann man einfach nachzhlen: Wenn n Tabellen miteinander verknpft a a u werden sollen, werden (n-1) WHERE-Bedingungen bentigt. o Diese Art der Verbindung wird Equi4 -Join5 genannt.
6.8.2 Self-Join
So, wie man mehrere Tabellen miteinander verbinden kann, ist es auch mglich, eine o Tabelle mit sich selbst zu verbinden. Notwendig ist dies in unserem Beispiel beim Vorgesetzten. Es ist zwar schn, da man zu jedem Mitarbeiter die Mitarbeiter-Nummer seines o Vorgesetzten abrufen kann, aber der Name wre doch noch viel schner: a o mysql> SELECT m.Name, m.VNr, v.Name, v.MNr -> FROM Mitarbeiter m, Mitarbeiter v -> WHERE m.VNr = v.MNr; +-----------+------+----------------+-----+ | Name | VNr | Name | MNr | +-----------+------+----------------+-----+ | junetz.de | 1 | Christoph Reeg | 1 | | Uli | 1 | Christoph Reeg | 1 | | Maier | 1 | Christoph Reeg | 1 | | JCP | 3 | Uli | 3 | | Meier | 5 | Maier | 5 | +-----------+------+----------------+-----+
4 5
Christoph Reeg
Seite 55
6. SQL-Befehle
Joins
5 rows in set (0.13 sec) mysql> Die Tabelle Mitarbeiter mu zwei Mal innerhalb der FROM-Anweisung auftauchen. Um nachher die Spalten eindeutig bezeichnen zu knnen, mssen Tabellen-Aliase6 vergeben o u werden. Der einzige Schnheitsfehler bei der Abfrage ist, da der Mitarbeiter Christoph Reeg o nicht aufgelistet wird. Im Prinzip ist das logisch, da er keinen Vorgesetzten hat. Dumm ist es trotzdem, und deshalb kommen wir zur nchsten Form des Joins: dem Outer-Join. a
6.8.3 Outer-Join
Um beim Join alle Tupel der Haupttabelle mit den dazu passenden Tupeln der nachgeordneten Tabelle zu bekommen, wenn nicht zu jedem Tupel der Haupttabelle ein passender Tupel existiert, wird der Outer-Join bentigt. o Die Syntax unter MySQL lautet (wo sonst die Tabellennamen stehen): haupttabelle LEFT JOIN tabelle2 ON bedingung oder haupttabelle LEFT JOIN tabelle2 USING (spalte) Bei der unteren Mglichkeit mssen die Spaltennamen in den beiden Tabellen, uber die o u die Verbindung entsteht, gleich sein. Bei der oberen Mglichkeit mu an die Stelle, wo o bedingung steht, das eingesetzt werden, was man beim Equi-Join als where condition schreiben wrde. In beiden Fllen kann auch LEFT OUTER JOIN anstelle von LEFT JOIN u a geschrieben werden das OUTER ist optional und nur aus Grnden der SQL-Kompatibilitt u a erlaubt. Um beim oben gezeigten Beispiel zu bleiben: Es sollen alle Mitarbeiter mit deren Vorgesetzten (sofern vorhanden) angezeigt werden. Da die Spaltennamen ungleich sind (in der Haupttabelle VNr und in der nachgeordneten Tabelle MNr), mu die obere Syntax benutzt werden. mysql> SELECT m.Name, m.VNr, v.Name, v.MNr -> FROM Mitarbeiter m LEFT JOIN Mitarbeiter v ON m.VNr = v.MNr; +----------------+------+----------------+------+ | Name | VNr | Name | MNr | +----------------+------+----------------+------+ | Christoph Reeg | NULL | NULL | NULL | | junetz.de | 1 | Christoph Reeg | 1 | | Uli | 1 | Christoph Reeg | 1 | | JCP | 3 | Uli | 3 | | Maier | 1 | Christoph Reeg | 1 | | Meier | 5 | Maier | 5 | +----------------+------+----------------+------+ 6 rows in set (0.03 sec) mysql>
6
Christoph Reeg
Seite 56
6. SQL-Befehle
Joins
Die Mitarbeitertabelle mit dem Alias m ist in unserem Fall die Haupttabelle. Wie man unschwer erkennen kann, wird bei den Attributen, bei denen keine Werte existieren (in diesem Beispiel die beiden rechten Spalten in der ersten Zeile), NULL als Wert genommen. Ubrigens: Man mu natrlich nicht sowohl VNr als auch MNr abfragen (also zwischen u SELECT und FROM angeben), da deren Werte wegen der ON-Bedingung (in der sie wiederum stehen mssen) immer gleich sind. Wenn man nur wissen mchte, in welcher Beziehung die u o einzelnen Mitarbeiter zu den Vorgesetzten stehen, kann man sogar ganz auf das Abfragen der Nummern verzichten. Um nicht vllig den Uberblick zu verlieren, vergeben wir dabei o noch Aliase: mysql> SELECT m.Name AS Mitarb, v.Name as Vorges -> FROM Mitarbeiter m LEFT JOIN Mitarbeiter v ON m.VNr = v.MNr; +----------------+----------------+ | Mitarb | Vorges | +----------------+----------------+ | Christoph Reeg | NULL | | junetz.de | Christoph Reeg | | Uli | Christoph Reeg | | JCP | Uli | | Maier | Christoph Reeg | | Meier | Maier | +----------------+----------------+ 6 rows in set (0.03 sec) mysql> Ein neues Beispiel: Ich will alle Abteilungen aufgelistet haben, aber mit deren Autos. mysql> SELECT a.Name, p.Kennzeichen, p.Typ -> FROM Abteilung a, PKW p -> WHERE a.AbtNr = p.AbtNr; +-----------+-------------+---------+ | Name | Kennzeichen | Typ | +-----------+-------------+---------+ | Chefetage | MTK-CR 1 | RR | | EDV | F-JN 1 | VW-Golf | +-----------+-------------+---------+ 2 rows in set (0.04 sec) mysql> Wie man sieht, fhrt der Equi-Join nicht zum gewnschten Ergebnis7 . Also das ganze u u nochmal als Outer-Join. Da die Schlsselspalten denselben Namen haben, knnen in diesem u o Fall beide Syntaxvarianten verwendet werden:
7
Sonst wrde dieses Beispiel auch nicht im Kapitel Outer-Join stehen . . . ;-) u
Christoph Reeg
Seite 57
6. SQL-Befehle
DELETE FROM
mysql> SELECT a.Name, p.Kennzeichen, p.Typ -> FROM Abteilung a LEFT JOIN PKW p ON a.AbtNr = p.AbtNr; +------------+-------------+---------+ | Name | Kennzeichen | Typ | +------------+-------------+---------+ | EDV | F-JN 1 | VW-Golf | | Verwaltung | NULL | NULL | | Chefetage | MTK-CR 1 | RR | +------------+-------------+---------+ 3 rows in set (0.00 sec) mysql> SELECT a.Name, p.Kennzeichen, p.Typ -> FROM Abteilung a LEFT JOIN PKW p USING (AbtNr); +------------+-------------+---------+ | Name | Kennzeichen | Typ | +------------+-------------+---------+ | EDV | F-JN 1 | VW-Golf | | Verwaltung | NULL | NULL | | Chefetage | MTK-CR 1 | RR | +------------+-------------+---------+ 3 rows in set (0.00 sec) mysql> Als kleine Herausforderung htte ich jetzt gerne die Abteilungen, die keinen PKW haben. a Die Lsung ist eigentlich einfach. Wenn man sich das obige Ergebnis ansieht, stellt man o fest (wie auch etwas weiter oben schon beschrieben), da bei der Abteilung ohne PKW (nmlich Verwaltung) Kennzeichen und Typ NULL sind. a mysql> SELECT a.Name, p.Kennzeichen, p.Typ -> FROM Abteilung a LEFT JOIN PKW p USING (AbtNr) -> WHERE Typ IS NULL; +------------+-------------+------+ | Name | Kennzeichen | Typ | +------------+-------------+------+ | Verwaltung | NULL | NULL | +------------+-------------+------+ 1 row in set (0.03 sec) mysql>
Christoph Reeg
Seite 58
6. SQL-Befehle
UPDATE
DELETE FROM table_name [WHERE where_definition] Fr table name ist selbstverstndlich der Name der Tabelle einzusetzen, in der gelscht u a o wird. Wenn keine where denition angegeben wird, werden alle Daten ohne Nachu frage gelscht! Die where denition mu denselben Anforderungen gengen wie bei der o SELECT-Anweisung. Um vor dem Lschen zu testen, ob auch wirklich nur die richtigen Daten gelscht werden, o o kann man erst ein SELECT * statt DELETE machen. Wenn nur Daten angezeigt werden, die gelscht werden sollen, ersetzt man das SELECT * durch DELETE. o
6.10 UPDATE
Als letztes fehlt noch das Andern von Daten. Es ist doch etwas unpraktisch, erst die Daten zu lschen, um sie danach wieder gendert einzufgen. Die Syntax des UPDATE-Befehls o a u lautet: UPDATE table_name SET feld_name=expression,... [WHERE where_definition] Fr table name ist selbstverstndlich der Name der Tabelle einzusetzen, in der einzelne u a Eintrge gendert werden sollen. feld name ist durch den Namen des Feldes zu erseta a zen, dessen Werte gendert werden sollen und expression durch den neuen Wert. Falls a Werte mehrerer Felder gendert werden sollen, knnen diese Zuweisungen einfach durch a o je ein Komma getrennt hintereinander geschrieben werden. Wenn keine where denition angegeben wird, werden alle Tupel gendert! Die where denition mu denselben Ana forderungen gengen wie bei der SELECT-Anweisung. u
Christoph Reeg
Seite 59
6. SQL-Befehle
ALTER TABLE
Um das Ganze etwas zu verdeutlichen, hier ein kleines praktisches Beispiel (der Befehl SHOW COLUMNS wird hierbei jeweils nur zur Verdeutlichung der Ergebnisse benutzt): Es wird folgende Tabelle angelegt: mysql> CREATE TABLE temp ( -> eins VARCHAR(10) NOT NULL, -> zwei VARCHAR(20), -> PRIMARY KEY(eins) -> ); Query OK, 0 rows affected (0.00 sec) mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | PRI | | | | zwei | varchar(20) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 2 rows in set (0.00 sec) mysql> Als nchstes soll die Spalte fuenf vom Typ VARCHAR(30) eingefgt werden. a u mysql> ALTER TABLE temp -> ADD fuenf VARCHAR(30); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | PRI | | | | zwei | varchar(20) | YES | | NULL | | | fuenf | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql> Nun wird die Spalte drei vom Typ VARCHAR(30) nach der Spalte zwei eingefgt. u mysql> ALTER TABLE temp -> ADD drei VARCHAR(30) AFTER zwei; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0
Christoph Reeg
Seite 60
6. SQL-Befehle
ALTER TABLE
mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | PRI | | | | zwei | varchar(20) | YES | | NULL | | | drei | varchar(30) | YES | | NULL | | | fuenf | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql> So gefllt uns das aber noch nicht ganz. Also benennen wir einfach die Spalte fuenf in a vier um: mysql> ALTER TABLE temp -> CHANGE fuenf vier VARCHAR(30); Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | PRI | | | | zwei | varchar(20) | YES | | NULL | | | drei | varchar(30) | YES | | NULL | | | vier | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 4 rows in set (0.01 sec) mysql> Jetzt soll die Spalte eins nicht mehr Primrschlssel sein (nicht die erste Spalte der a u SHOW-Anweisung!): mysql> ALTER TABLE temp -> DROP PRIMARY KEY; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra |
Christoph Reeg
Seite 61
6. SQL-Befehle
ALTER TABLE
+-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | | | | | zwei | varchar(20) | YES | | NULL | | | drei | varchar(30) | YES | | NULL | | | vier | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 4 rows in set (0.01 sec) mysql> Die Spalte drei soll nun Primrschlssel werden. Primrschlssel mssen aber als NOT a u a u u NULL deniert werden. Deshalb zuerst einmal eine kleine Anderung des Datentyps: mysql> ALTER TABLE temp -> CHANGE drei drei VARCHAR(30) NOT NULL; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | | | | | zwei | varchar(20) | YES | | NULL | | | drei | varchar(30) | | | | | | vier | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql> Und nun denieren wir den Primrschlssel neu: a u mysql> ALTER TABLE temp -> ADD PRIMARY KEY(drei); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | | | | | zwei | varchar(20) | YES | | NULL | | | drei | varchar(30) | | PRI | | | | vier | varchar(30) | YES | | NULL | |
Christoph Reeg
Seite 62
6. SQL-Befehle
ALTER TABLE
+-------+-------------+------+-----+---------+-------+ 4 rows in set (0.01 sec) mysql> Jetzt, wo wir alles mhsam erstellt haben, fangen wir wieder an, alle Spalten der Reihe u nach zu lschen. Als erstes soll die Spalte drei gelscht werden. o o mysql> ALTER TABLE temp -> DROP drei; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW columns FROM temp; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | | | | | zwei | varchar(20) | YES | | NULL | | | vier | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql> Der Name temp klingt aber irgendwie langweilig. Deshalb benennen wir die Tabelle in test um: mysql> ALTER TABLE temp -> RENAME test; Query OK, 0 rows affected (0.00 sec) mysql> SHOW columns FROM temp; ERROR 1146: Table cr.temp doesnt exist mysql> SHOW columns FROM test; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | eins | varchar(10) | | | | | | zwei | varchar(20) | YES | | NULL | | | vier | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql> Zum Schlu lschen wir wieder die ganze Tabelle: o
Christoph Reeg
Seite 63
6. SQL-Befehle
ALTER TABLE
mysql> DROP TABLE test; Query OK, 0 rows affected (0.00 sec) mysql> SHOW columns FROM test; ERROR 1146: Table cr.test doesnt exist mysql> Die letzte Fehlermeldung besagt, da die Tabelle nicht existiert. Diese Fehlermeldung bekommt man auch, wenn man sich beim Tabellennamen verschrieben hat (auch Gro/Kleinschreibung).
Christoph Reeg
Seite 64
Christoph Reeg
Seite 65
Nutzer in MySQL
| 4 | JCP | 0.8183 | | 2 | junetz.de | 0.1982 | +-----+----------------+--------+ 6 rows in set (0.00 sec) Der Summand 0 M N r beeinut das Endergebnis nicht, trotzdem funktioniert das Sortieren. Die Reihenfolge ist zufllig, auch wenn (komischerweise1 ) nicht nach sort sortiert wura de. Nachdem wir nun die zufllige Reihenfolge erhalten haben, knnen wir uns auch mit a o Hilfe von LIMIT (siehe auch 6.6.3) einen Datensatz ausgeben lassen. Eine andere Mglichkeit, genau einen zuflligen Datensatz auszuwhlen, wre z. B. mit o a a a Hilfe von PHP: erst mit Hilfe von COUNT (siehe auch 6.7.5) bestimmen, wie viele Datensta ze es gibt, dann mit PHP eine Zufallszahl bestimmen und genau den korrespondierenden Eintrag ausgeben. Funktioniert im Endeekt genauso, ist nur etwas mehr Programmieraufwand. Was im Endeekt schneller luft, hngt von dem jeweiligen Datenbankdesign a a ab.
7.3 PHPMyAdmin
PHPMyAdmin ist ein PHP-basiertes MySQL-Administrierungssystem, d. h. es stellt eine Mglichkeit dar, uber einen gewhnlichen Web-Browser eine MySQL-Datenbank zu vero o walten (Benutzer, Datenbanken, Tabellen und Datenstze anlegen, ndern und lschen). a a o Dabei besteht die Mglichkeit, das System so einzurichten, da alle MySQL-Nutzer eines o Systems sich einloggen und dabei natrlich nur ihre eigenen Datenbanken (genauer: die, u fr die sie Rechte besitzen) sehen und bearbeiten knnen. Auch fr den Systemverwalu o u ter gibt es einige Einstellungsmglichkeiten wie das Anlegen neuer Nutzer inklusive dem o obligatorischen MySQL-Neustart oder Rechtevergabe.
1 2
IMHO sollte schon richtig nach der Spalte sort sortiert werden Das solltest du aber, sobald es geht, ndern, d. h. eines setzen! a
Jens Hatlak
Seite 66
Bume in SQL a
Richtig eingesetzt, kann PHPMyAdmin fast vollstndig das ,mysql Kommandozeilena programm ersetzen, denn sogar Im- und Export von fertigen SQL-Dateien ist mglich. Von o Vorteil ist natrlich die Umsetzung des Systems in HTML: Whrend beim Kommandozeiu a lenprogramm schnell die Ubersichtlichkeit verloren geht, wenn groe Tabellen ausgegeben werden sollen, macht PHPMyAdmin von gewhnlichen HTML-Tabellen Gebrauch und o bietet dabei direkt sinnvolle Editiermglichkeiten und Vereinfachungen. o Fr Nutzer von Billig-Webhosting Angeboten ist ein Zugri auf die mysql-Komu mandozeile gar nicht mglich und dadurch PHPMyAdmin eine sehr einfache Alternative, o trotzdem entsprechende Mglichkeit zu bekommen. o Auf der PHPMyAdmin-Homepage http:// www.phpmyadmin.net/ nden sich immer die aktuellen Versionen des Pakets. Zu beachten ist, da es Versionen fr zwei verschiedene u Endungen (.php und .php3) gibt. Dies ist dann von besonderem Interesse, wenn, wie beim Jugendnetz Frankfurt/Oenbach, verschiedene Endungen verschiedenen PHP-Interpreter Versionen zugeordnet sind. Dem Paket liegt zwar ein Script zum Andern der Endungen in frei whlbare bei, dieses funktioniert aber leider nur mit der Bash, also i. A. nicht unter a Windows. Ist also z. B. die Verwendung der Endung .php4 gewnscht, so ist Zugang zu u einem Unix-basierten System von Vorteil. ;-) Wenn man nachfragt, ist bei Anbietern von serverseitiger PHP- und MySQL-Untersttzung oftmals PHPMyAdmin schon deshalb installiert, damit die Administratoren u selbst ezienter arbeiten knnen - so auch bei uns. Im Zweifelsfall lohnt es sich, einfach o mal zu fragen, ob dieses System angeboten wird und fr Mitglieder auch frei verfgbar u u ist. Fr letzteres ist nmlich nicht viel zu machen, lediglich ein Nutzer mit Leserechten u a auf die mysql-Datenbank mu angelegt werden, was auch kein Sicherheitsrisiko dar stellt angesichts der Tatsache, da die dort abgelegten Paworte durch den verwendeten Verschlsselungs-Algorithmus praktisch unknackbar sind3 . u Sollten noch Fragen oen bleiben, die auch die PHPMyAdmin-eigene Dokumentation nicht beantworten kann und die von allgemeinem Interesse sind, kannst du diese gerne an die vorne angegebene E-Mail-Adresse schicken. Auf diese Weise halten hug gestellte a Fragen (FAQ) sicher Einzug in sptere DSP-Versionen. a
Bei der Authentizierung wird ja nur mit demselben Algorithmus verschlsselt und dann verglichen u - eine Entschlsselung ist gar nicht vorgesehen (und sollte nicht mglich sein) u o
Christoph Reeg
Seite 67
Bume in SQL a
auch verschiedene Denitionen, was ein Baum ist. Ein Baum ist ein ungerichteter, zyklenfreier, zusammenhngender Graph. a Ein Baum ist ein azyklischer Graph, in dem genau ein Knoten keinen Vorgnger hat und alle anderen Knoten mindestens einen Vorgnger haben. a a Die letzte Denition kann man sogar fast verstehen, trotzdem versuche ich es anders zu erklren. a Bei dem guten alten DOS gab es ein Laufwerk C:, auf dem verschiedene Verzeichnisse (z. B. DOS und WIN) existierten, die wieder Unterverzeichnisse hatten (z. B. SYSTEM im WINVerzeichnis). Wenn man nun C: als Wurzel bezeichnet und DOS und WIN als Nachfolger von C:, sowie SYSTEM als Nachfolger von WIN hat man einen Baum. An Stelle von Nachfolger und Vorgnger wird auch hug Sohn und Vater gesagt. Die a a Elemente in der Mitte des Baumes werden Knoten genannt und die Elemente ohne Nachfolger heien Bltter. a
7.4.2 Beispieldaten
In diesem Kapitel werde ich von einem Beispielbaum ausgehen, wo ich die Elemente so benannt habe, da eigentlich klar sein sollte, wo sie sich im Baum benden sollten. In Abbildung 7.1 habe ich den Baum einmal grasch dargestellt.
Root
A1
B1
B2
C1
A1I
C1I
C1II
Christoph Reeg
Seite 68
Bume in SQL a
Tabelle 7.1: Baumdarstellung mit Vater-Zeiger Der Vorteil dieser Variante ist das relativ einfache Erstellen des Baumes. Auch ist er relativ robust gegen falsche Daten; eine falsche Vater-ID fhrt nur zu einem falsch angeu hngten Ast, aber ansonsten ist noch alles OK. Auch das Umhngen von einzelnen Asten a a ist relativ einfach. Das groe Problem kommt allerdings z. B. bei der Ausgabe des Gesamtbaumes, bei der Bestimmung der maximalen Tiefe4 , oder beim Feststellen, ob ein Knoten noch Shne hat. Siehe dazu auch die Ubung 7.4.6.1 o
Tiefe bei einem Baum heit, wie viele Knoten hintereinander hngen a
Christoph Reeg
Seite 69
Bume in SQL a
C1I
Abbildung 7.2: Baumdarstellung als Summe von Mengen ich nenne sie einfach mal ,l und ,r. Dann nehme ich wieder die Abbildung 7.1, fange bei der Wurzel an und schreibe in das Feld ,l eine ,1, gehe dann zu dem linken Nachfolger und schreibe dort in das Feld ,l eine ,2 usw. bis ich unten angekommen bin. Dort schreibe ich in das Feld ,r eine ,5 und gehe wieder zhlend hoch. Wer dem jetzt nicht folgen a konnte: Keine Angst, in Abbildung 7.3 ist das Ganze grasch dargestellt und dann sollte es eigentlich klar werden. Die an dieser Stelle wichtigste Abfrage, ob ein Element Nachfolger eines anderen ist (gleichbedeutend mit in der Menge enthalten), lt sich mit BETWEEN erledigen. a Aber nun genug der Theorie, setzen wir das Ganze in SQL um. Dazu als erstes unser CREATE TABLE mit den INSERT Anweisungen: CREATE TABLE NestedSet ( ID int not null primary key , Name varchar (100), l int , r int ); INSERT INSERT INSERT INSERT INTO INTO INTO INTO NestedSet NestedSet NestedSet NestedSet VALUES VALUES VALUES VALUES (1, Root ,1,22); (2, A ,2,7); (3, B ,8,13); (4, C ,14,21);
Christoph Reeg
Seite 70
Bume in SQL a
Root 1 22 A 2 A1 3 A1I 4 5 6 9 7 8 B1 10 11 B 13 B2 12 15 14 C1 20 C 21
C1I C1II 16 17 18 19
Abbildung 7.3: Baumdarstellung im Nested Set Modell INSERT INSERT INSERT INSERT INSERT INSERT INSERT INTO INTO INTO INTO INTO INTO INTO NestedSet NestedSet NestedSet NestedSet NestedSet NestedSet NestedSet VALUES VALUES VALUES VALUES VALUES VALUES VALUES (5, A1 ,3,6); (6, B1 ,9,10); (7, B2 ,11,12); (8, C1 ,15,20); (9, A1I ,4,5); (10, C1I ,16,17); (11, C 1 I I ,18,19);
Wie schn diese Struktur ist, zeigt folgende Abfrage: o mysql> SELECT v.Name AS Vater, s.Name AS Nachfolger -> FROM NestedSet v, NestedSet s -> WHERE s.l BETWEEN v.l AND v.r -> ORDER BY s.l, v.l; +-------+------------+ | Vater | Nachfolger | +-------+------------+ | Root | Root | | Root | A | | A | A | | Root | A1 | | A | A1 | | A1 | A1 |
Christoph Reeg
Seite 71
Bume in SQL a
| Root | A1I | | A | A1I | | A1 | A1I | | A1I | A1I | | Root | B | | B | B | | Root | B1 | | B | B1 | | B1 | B1 | | Root | B2 | | B | B2 | | B2 | B2 | | Root | C | | C | C | | Root | C1 | | C | C1 | | C1 | C1 | | Root | C1I | | C | C1I | | C1 | C1I | | C1I | C1I | | Root | C1II | | C | C1II | | C1 | C1II | | C1II | C1II | +-------+------------+ 31 rows in set (0.01 sec) Ich bekomme mit einer Abfrage das Ergebnis, wer alles Vorgnger zu einem bestimmten a Knoten ist. Durch eine entsprechende Eingrenzung kann man sich auch alle Vorgnger zu genau a einem Knoten ausgeben lassen: mysql> SELECT v.Name AS Vater, s.Name AS Nachfolger -> FROM NestedSet v, NestedSet s -> WHERE s.l BETWEEN v.l AND v.r -> AND s.Name = C1I -> ORDER BY v.l; +-------+------------+ | Vater | Nachfolger | +-------+------------+ | Root | C1I | | C | C1I | | C1 | C1I | | C1I | C1I | +-------+------------+ 4 rows in set (0.00 sec)
Christoph Reeg
Seite 72
Bume in SQL a
Die Abfrage, wie viele Nachfolger ein Knoten hat, lt sich ganz einfach uber die Diea renz von l und r feststellen. mysql> SELECT (r-l-1)/2 AS Nachfolger, Name -> FROM NestedSet -> ORDER BY l; +------------+------+ | Nachfolger | Name | +------------+------+ | 10.00 | Root | | 2.00 | A | | 1.00 | A1 | | 0.00 | A1I | | 2.00 | B | | 0.00 | B1 | | 0.00 | B2 | | 3.00 | C | | 2.00 | C1 | | 0.00 | C1I | | 0.00 | C1II | +------------+------+ 11 rows in set (0.00 sec) Auch sehr interessant: alle Knoten, mit ihrer Tiefe: mysql> SELECT s.Name, count(*) AS Level -> FROM NestedSet v, NestedSet s -> WHERE s.l BETWEEN v.l AND v.r -> GROUP BY s.l; +------+-------+ | Name | Level | +------+-------+ | Root | 1 | | A | 2 | | A1 | 3 | | A1I | 4 | | B | 2 | | B1 | 3 | | B2 | 3 | | C | 2 | | C1 | 3 | | C1I | 4 | | C1II | 4 | +------+-------+ 11 rows in set (0.00 sec) Bevor wir den Baum gleich durch Lschen & Co verndern, gibt es auch hier Ubungen o a
Christoph Reeg
Seite 73
Bume in SQL a
(7.4.6.2) zum Vertiefen. Keine Angst, falls das Ganze noch nicht klar geworden ist; mit der Zeit kommt das Verstndnis fr diese Struktur. a u 7.4.4.1 Lschen o So schn diese Methode auch zum Abfragen ist, so schwierig ist sie beim Andern der o Datenstruktur. Man mu immer aufpassen, keinen der Zeiger falsch zu belegen, weil sonst der ganze Baum durcheinander kommt. Im Prinzip sind folgende Schritte notwendig: Sperren der Tabelle l und r von dem zu lschenden Element auslesen o Element lschen o l und r der Nachfolger um 1 reduzieren l und r des rechten Nachbarbaums um 2 reduzieren ( r im bzw. in den Vater knoten nicht vergessen!) Tabelle wieder freigeben Um das zu verstehen, ist es hilfreich, sich einfach mal ein Blatt Papier und einen Stift zu nehmen und das exemplarisch durchzugehen. Wir machen das jetzt hier in SQL, indem wir aus dem Beispiel den Knoten B lschen. o Die beiden Nachfolger B1 und B2 sollen dann direkt unter Root hngen. a Als erstes verhindern wir, da durch einen evtl. parallelen Lschvorgang die Tabelle o durcheinander kommt, indem wir sie sperren. mysql> LOCK TABLE NestedSet WRITE; Query OK, 0 rows affected (0.00 sec) Nun brauchen wir l und r von unserem Element: mysql> SELECT l,r -> FROM NestedSet -> WHERE Name = B; +------+------+ | l | r | +------+------+ | 8 | 13 | +------+------+ 1 row in set (0.00 sec) Diese mssen wir uns nun merken. u Den Knoten zu lschen, sollte kein groes Problem sein: o mysql> DELETE FROM NestedSet -> WHERE Name = B; Query OK, 1 row affected (0.01 sec)
Christoph Reeg
Seite 74
Bume in SQL a
Kommen wir nun zu den Nachfolgern (hier B1 und B2), zu erkennen an deren l bzw. r, die zwischen 8 und 13 (siehe letzte Abfrage) liegen. mysql> UPDATE NestedSet -> SET l=l-1, r=r-1 -> WHERE l BETWEEN 8 AND 13; Query OK, 2 rows affected (0.00 sec) Rows matched: 2 Changed: 2 Warnings: 0 Nicht zu vergessen der rechte Nachbarbaum (in diesem Fall alle C* Elemente). mysql> UPDATE NestedSet -> SET l=l-2 -> WHERE l > 13; Query OK, 4 rows affected (0.00 sec) Rows matched: 4 Changed: 4 Warnings: 0 mysql> UPDATE NestedSet -> SET r=r-2 -> WHERE r > 13; Query OK, 5 rows affected (0.00 sec) Rows matched: 5 Changed: 5 Warnings: 0 Und als letzter wichtiger Schritt mu natrlich noch die Tabelle wieder freigegeben u werden. mysql> UNLOCK TABLES; Query OK, 0 rows affected (0.00 sec) Das war jetzt das Lschen eines Elements. Wer das Prinzip des Baumaufbaus verstanden o hat, kann sich auch noch davon uberzeugen, da es tatschlich funktioniert hat. a mysql> SELECT * -> FROM NestedSet -> ORDER BY l; +----+------+------+------+ | ID | Name | l | r | +----+------+------+------+ | 1 | Root | 1 | 20 | | 2 | A | 2 | 7 | | 5 | A1 | 3 | 6 | | 9 | A1I | 4 | 5 | | 6 | B1 | 8 | 9 | | 7 | B2 | 10 | 11 | | 4 | C | 12 | 19 | | 8 | C1 | 13 | 18 | | 10 | C1I | 14 | 15 | | 11 | C1II | 16 | 17 | +----+------+------+------+ 10 rows in set (0.00 sec)
Christoph Reeg
Seite 75
Bume in SQL a
Lschen von ganzen Teilbumen funktioniert analog. Schritt 3 kann dabei allerdings o a entfallen (es gibt ja keinen Nachfolger mehr) und bei Schritt 4 mu man nicht 2, sondern entsprechend der entfernten Elemente subtrahieren. 7.4.4.2 Einfgen u Das Einfgen funktioniert im Prinzip analog zum Lschen, man mu die Schritte nur in u o der umgekehrten Reihenfolge durchlaufen.
7.4.6 Ubungen
7.4.6.1 Vater-Zeiger Um das Folgende machen zu knnen, mu erstmal die Tabelle 7.1 angelegt werden. o Nachdem die Tabelle nun existiert, kommen wir zu den hugsten Abfragen (Lsung a o im Anhang B.1.1): Gib die Wurzel aus Wie viele Shne hat die Wurzel? o Welches sind die Shne der Wurzel? o Gib die Wurzel mit den Shnen aus o Gib den kompletten Baum aus, so da etwa das Folgende herauskommt: +-------+------+ | Tiefe | Name | +-------+------+ | 0 | Root | | 1 | A | | 2 | A1 | | 3 | A1I | | 1 | B | Also immer der Vaterknoten, gefolgt von den Shnen mit Angabe der Tiefe. Die o Abfrage soll allgemein sein, also auch bei greren Tiefen funktionieren. o
Christoph Reeg
Seite 76
IF-Ausdrcke in SQL u
7.4.6.2 Nested Set Etliche Abfragen wurden schon gezeigt, von daher kommt hier noch eine richtig schne o (und damit nicht ganz einfache). Aber zur Einstimmung erstmal was einfaches. Nicht immer braucht man den gesamten Baum. Gib alle Knoten, die unterhalb des Knotens C liegen, mit ihrer Tiefe im Baum aus. Das Ergebnis sollte so aussehen: +------+-------+ | Name | Level | +------+-------+ | C | 2 | | C1 | 3 | | C1I | 4 | | C1II | 4 | +------+-------+ Um einen Baum sauber in HTML ausgeben zu knnen, braucht man folgende Ino formationen: Name des Elements, Anzahl der Nachfolger (bzw. gibt es einen oder keinen), die Tiefe des Elements und ob es auf gleicher Tiefe noch ein Element gibt. Oder andersherum ausgedrckt, folgende Tabelle enthlt alle notwendigen Informau a tionen. Wie sieht die dazu passende Abfrage aus? +------+------------+-------+--------+ | Name | Nachfolger | Tiefe | Bruder | +------+------------+-------+--------+ | Root | 10.00 | 1 | 0 | | A | 2.00 | 2 | 1 | | A1 | 1.00 | 3 | 0 | | A1I | 0.00 | 4 | 0 | | B | 2.00 | 2 | 1 | | B1 | 0.00 | 3 | 1 | | B2 | 0.00 | 3 | 0 | | C | 3.00 | 2 | 0 | | C1 | 2.00 | 3 | 0 | | C1I | 0.00 | 4 | 1 | | C1II | 0.00 | 4 | 0 | +------+------------+-------+--------+
Tilman Brock
Seite 77
IF-Ausdrcke in SQL u
+-----+-----+ | 6 | 1 | | 2 | 11 | | 5 | 5 | | 233 | 123 | +-----+-----+ 4 rows in set (0.00 sec) Die Fragestellung lautet: Ist a grer oder gleich b? o Die traditionelle Methode wre jetzt folgende: man zieht sich alle Werte aus der Tabelle a und geht dann mittels einer geeigneten Programmiersprache (z. B. PHP) daran, die Werte zu extrahieren es geht aber auch einfacher: mittels der IF-Anweisung in MySQL kann man der Ergebnistabelle leicht Felder hinzufgen, die die gewnschten Informationen enthalten. u u Die IF-Anweisung ist folgendermaen deniert: IF(Bedingung, wenn_wahr, wenn_falsch) wenn_wahr und wenn_falsch sind hierbei Zeichenketten, Spaltennamen, Funktionen auf Spalten oder andere IF-Anweisungen (siehe unten). Um die Fragestellung zu beantworten, ist also folgendes IF ntig: o IF(a>=b,a grer gleich b,a kleiner b). o Dieses IF mssen wir jetzt noch in eine SELECT-Anweisung verpacken, damit wir auch u mit dem Ergebnis arbeiten knnen. Dem IF mu in der SELECT-Anweisung ein Alias o (siehe 6.6.5.1) gegeben werden, da sonst der Wert nicht benutzt werden kann. mysql> select a,b, -> IF(a>=b,a groesser gleich b,a kleiner b) as vergleich -> from if_test1; +-----+-----+---------------------+ | a | b | vergleich | +-----+-----+---------------------+ | 6 | 1 | a groesser gleich b | | 2 | 11 | a kleiner b | | 5 | 5 | a groesser gleich b | | 233 | 123 | a groesser gleich b | +-----+-----+---------------------+ 4 rows in set (0.00 sec) Eine dreifach geschachtelte Aussage ist so jedoch nicht mglich. Wir knnen mit dieser o o Zwei-Wege-Form des IF nicht fragen: Ist a grer, gleich oder kleiner als b?. Um diese o Frage zu beantworten, brauchen wir eine geschachtelte IF-Anweisung, modellhaft also etwas in dieser Art: Ist a grer b? o - ja --> a grer b o - nein Ist a gleich b? - ja
Tilman Brock
Seite 78
IF-Ausdrcke in SQL u
--> a gleich b - nein --> a kleiner b Wie oben gesehen ist eine IF-Anweisung aus einer Bedingung, einem Teil wenn wahr und einem Teil wenn falsch aufgebaut. Um das eben gezeigte Modell als IF nachzubauen, brauchen wir 2 geschachtelte IF-Anweisungen. Wir schreiben also (laut Modelldarstellung) in den wenn falsch-Teil des ueren IF ein neues IF, das die Frage Ist a gleich b stellt. a IF(a>b,a groesser b, IF(a=b,a gleich b, a kleiner b) ) MySQL liefert nun das gewnschte Resultat: u mysql> select a,b, -> IF(a>b,a groesser b, -> IF(a=b,a gleich b, a kleiner b) -> ) as vergleich -> from if_test1; +-----+-----+--------------+ | a | b | vergleich | +-----+-----+--------------+ | 6 | 1 | a groesser b | | 2 | 11 | a kleiner b | | 5 | 5 | a gleich b | | 233 | 123 | a groesser b | +-----+-----+--------------+ 4 rows in set (0.05 sec)
7.5.1 Beispiele
7.5.1.1 Firma Ausbeuter & Co KG Die Firma Ausbeuter & Co KG mchte geschlechtsspezische Zulagen vergeben, da es ja o allgemein bekannt sein drfte, da Mnner schlechter arbeiten als Frauen5 . Die Firma hat u a eine Mitarbeitertabelle, in der folgendes steht: mysql> select * from mitarbeiter; +---------------+----------+------------+--------+ | MitarbeiterNr | Name | Geschlecht | Gehalt | +---------------+----------+------------+--------+ | 351 | Meier | m | 2000 | | 729 | Mller u | m | 1142 | | 921 | Guglhupf | w | 4500 | | 523 | Schmitt | w | 3488 | | 331 | Reeg | m | 2 |
5
Tilman Brock
Seite 79
IF-Ausdrcke in SQL u
+---------------+----------+------------+--------+ 5 rows in set (0.00 sec) Die Zulage fr Frauen soll 250 EUR betragen, die fr Mnner 180 EUR. Da nur Mann u u a und Frau unterschieden werden sollen, brauchen wir ein klassisches 2-Wege-IF: IF(Geschlecht = w,250,180) Eingebunden in das SELECT ergibt sich dieses: mysql> select MitarbeiterNr, Name, Geschlecht, Gehalt, -> IF(Geschlecht = w, 250, 180) as Zulage -> from mitarbeiter; +---------------+----------+------------+--------+--------+ | MitarbeiterNr | Name | Geschlecht | Gehalt | Zulage | +---------------+----------+------------+--------+--------+ | 351 | Meier | m | 2000 | 180 | | 729 | Mller u | m | 1142 | 180 | | 921 | Guglhupf | w | 4500 | 250 | | 523 | Schmitt | w | 3488 | 250 | | 331 | Reeg | m | 2 | 180 | +---------------+----------+------------+--------+--------+ 5 rows in set (0.00 sec)
Christoph Reeg
Seite 80
Christoph Reeg
Seite 81
8 PHP Grundlagen
8.1 Einleitung
Programming today is a race between software engineers striving to buy bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. Rich Cook
PHP1 ist eine serverseitige, in HTML2 eingebettete Scriptsprache - oder mit anderen Worten: PHP-Scripte werden auf dem Server ausgefhrt, im Gegensatz z. B. zum ublichen u 3 . Der Programmcode wird in die HTML-Quelldatei geschrieben und JavaScript und Java somit i. A. nicht in einer extra PHP-Datei abgelegt. Als Script werden Programme be zeichnet, die keine eigenstndigen Programme sind, weil sie nicht kompliziert genug sind a und andere Programme bentigen, um ausgefhrt zu werden. o u Im Gegensatz zu HTML und JavaScript erscheint der eigentliche PHP-Code i. A. nicht auf der Clientseite, d. h. der Quellcode, den man sich im Browser auch ansehen kann, enthlt fr gewhnlich keinen PHP-Code4 . Der HTML-Code wird beim Abruf der Webseite, a u o wie bei normalen Seiten auch, 1:1 an den Client geschickt; der PHP-Code wird durch den Server ausgefhrt und die Ausgabe dann, zusammen mit den nicht interpretierten u HTML-Scriptanteilen, an den Client gesandt. Es ist auch mglich, PHP-Scripte ganz ohne o (sichtbare) Ausgabe laufen zu lassen - was durchaus sinnvoll sein kann. Immer wenn man sich fragt, ob etwas mglich ist, ist zu uberlegen, ob dazu eine Aktion o auf dem Server (wo die Webseite liegt) oder auf dem Client (wo die Webseite angezeigt wird) notwendig ist. Z. B.: Ist es mglich, mit einem PHP-Befehl die aktuelle Webseite o auszudrucken? Die Antwort ist ganz einfach: Damit auf dem Client die Seite ausgedruckt wird, mu dem Browser ein Befehl ubermittelt werden. Da PHP aber auf dem Server ausgefhrt wird, kann es diesen Befehl folglich nicht selbst in die Tat umsetzen. Auf dem u Client wird aber z. B. JavaScript ausgefhrt, das einen Befehl anbietet, der die Seite ausu druckt (sofern JavaScript aktiviert ist). Fr viele andere Aktionen ist aber nicht einmal u JavaScript ntig. o Im vorigen Kapitel wurde SQL beschrieben, jetzt wird PHP erklrt. Dies sind zwei vona
1 2 3
PHP Hypertext Preprocessor Hypertext Markup Language Beides kann zwar auch serverseitig laufen, wobei sie dann PHP ersetzen. I. A. wird es aber clientseitig eingesetzt Falls doch PHP-Code beim Client gelandet ist, hat der Server, der Serveradministrator oder der Scriptautor einen Fehler gemacht
Christoph Reeg
Seite 82
8. PHP Grundlagen
Grundbefehle
einander unabhngige Sprachen, die erst einmal nichts miteinander zu tun haben5 ! Auch a wenn es Funktionen in beiden Sprachen gibt, die hnlich heien, knnen sie sich doch a o deutlich unterscheiden. Im weiteren Verlauf wird gezeigt, wie man die beiden Programmiersprachen zusammenfhrt. Auch dann mu man sich weiter im Klaren darber sein, u u was SQL und was PHP ist.
8.2 Grundbefehle
PHP wird einfach in den HTML-Quellcode geschrieben. Damit der Server wei, in welcher Datei er nach PHP-Scripten suchen soll, mssen die Dateien die richtige Endung (Extenu sion) haben. Bei PHP3 waren .php3 und .phtml ublich, bei PHP4 ist dagegen .php gebruchlicher. Man kann natrlich den Webserver so kongurieren, da er jede beliebige a u Endung als PHP-Script identiziert. Damit der Server darber hinaus noch wei, welche u Ausdrcke er in der Datei interpretieren soll, mssen jeweils der Anfang und das Ende des u u PHP-Teils gekennzeichnet werden. Dafr gibt es drei Mglichkeiten: u o 1 <? echo H e l l o w o r l d ! ; ?> 2 <? php echo H e l l o w o r l d ! ; ?> 3 < script language = php > echo H e l l o w o r l d ! ; </ script > Die erste Mglichkeit ist die krzeste und damit bei vielen die beliebteste (wer ist nicht o u gerne faul?). Sie ist allerdings nicht XML6 -konform, so da spter Probleme auf den Proa grammierer zukommen knnen, wenn man sie benutzt. Auerdem gibt es Server, die diese o Variante nicht erkennen. Ich benutze immer die zweite Variante; sie ist kurz, aber dennoch XML-konform. Die Sprache PHP ist hauptschlich von C, aber auch von Java und Perl (die ihrerseits a von C beeinut wurden) geprgt. Aber auch fr Pascal/Delphi-Programmierer ist die a u Sprache nicht schwer zu erlernen. Grundstzlich gilt (merken!): Eine Anweisung wird immer mit einem ; abgeschlossen und a eine Funktion bzw. einen Funktionsaufruf erkennt man an runden Klammern ().
SQL ist dem Sinn und Zweck nach viel weniger eine Programmiersprache als PHP Extensible Markup Language, eine HTML nicht unhnliche Ausdruckssprache zum einheitlichen und a doch hochexiblen Speichern von Daten aller Art von engl. to quote: zitieren
Christoph Reeg
Seite 83
8. PHP Grundlagen
Grundbefehle
Anfhrungsstrichen versucht der Server, den Text zu interpretieren; bei den einfachen hinu gegen behandelt er ihn nicht speziell, sondern gibt ihn z. B. direkt aus. Weitere Erklrungen a zu Anfhrungszeichen nden sich in Kapitel 8.2.6.4. u $ v a r = 123; echo D i e V a r i a b l e $ v a r h a t den Wert 1 2 3 ! \ n ; echo D i e V a r i a b l e $ v a r h a t den Wert 1 2 3 ! \ n ; Das erste echo gibt Die Variable $var hat den Wert 123!\n aus, das zweite hingegen Die Variable 123 hat den Wert 123! mit folgendem Zeilenumbruch. echo Say \ Hello World \ my f r i e n d ; Die Ausgabe bei diesem echo ist Say Hello World! my friend. Wie man sieht, msu sen doppelte Anfhrungsstriche, die ausgegeben werden sollen, besonders gekennzeichnet u werden. Dieses Vorgehen nennt man Escapen8 . Es ist insbesondere fr das Ausgeben von u HTML-Quelltext in Verbindung mit echo und print ntig und kann u. U. zu Problemen o fhren, wenn man vergit, in allen Teilstrings zu escapen. Siehe auch Kapitel 13 (Fehleru suche).
8.2.3 Zuweisungen
Wenn man der Variablen $a den Wert der Variablen $b zuweisen will, mu man dies mithilfe des Gleichheitszeichens machen. Das bedeutet aber auch, da man Vergleiche in PHP nicht mit dem einfachen Gleichheitszeichen machen kann; wie man dies erreicht, erfahren wir daher noch spter. a
8
engl. to escape: entkommen. Hintergrund: Das escapete Zeichen entkommt der vorgesehenen Inter pretierung durch die Sprache, in diesem Fall PHP.
Christoph Reeg
Seite 84
8. PHP Grundlagen
Grundbefehle
$a = $b ;
8.2.4 Operatoren
Nur irgendwelche Werte in irgendwelche Variablen zu schreiben, wird irgendwann langweilig. Deshalb gibt es auch ein paar Operatoren. Dabei mu man zwischen den arithmetischen (Zahlen), String- (Text), Bit-, logischen (booleschen) und Vergleichs-Operatoren unterscheiden. 8.2.4.1 Arithmetische Operatoren Beispiel $a + $b $a - $b $a * $b $a / $b $a % $b Name Addition Subtraktion Multiplikation Division Modulo Ergebnis Summe von $a und $b Dierenz von $a und $b Produkt von $a und $b Quotient von $a und $b Rest der Division von $a und $b
Tabelle 8.1: Arithmetische Operatoren in PHP Wenn beide Operanden bei der Division vom Typ integer (ganzzahliger Wert) sind, ist das Ergebnis ebenfalls integer. Ist ein Operand eine Kommazahl, wird das Ergebnis auch eine Kommazahl. Befehle wie $a = $a + 5 kann man etwas abkrzen: u $a += 5; $a *= 2; $ i ++; $ i --: // // // // entspricht entspricht entspricht entspricht $a $a $i $i = = = = $a $a $i $i + + 5; 2; 1; 1;
8.2.4.2 String-Operatoren Es gibt nur einen echten String-Operator: den Verbindungsoperator (.). $a = H e l l o ; $b = $a . World ! ; // j e t z t i s t $b = H e l l o World !
Auch hier lassen sich Befehle der Form $a = $a . noch etwas Text abkrzen: u $a = H e l l o ; $a .= World ! ;
8.2.4.3 Bit-Operatoren Bitweise Operatoren erlauben es, bestimmte Bits in einer Integervariablen zu setzen.
Christoph Reeg
Seite 85
8. PHP Grundlagen
Grundbefehle
Ergebnis Bits, die in $a und $b gesetzt sind, werden gesetzt Bits, die in $a oder $b gesetzt sind, werden gesetzt Bits, die in $a gesetzt sind, werden nicht gesetzt und umgekehrt Tabelle 8.2: Bit-Operatoren in PHP
8.2.4.4 Logische Operatoren Logische bzw. boolesche Operatoren werden zum Beispiel zum Verknpfen von mehreren u Vergleichen bei einer Bedingung bentigt. true ist ubrigens der Wahrheitswert; dessen o Verneinung lautet false. Beispiel $a and $b $a or $b $a xor $b !$a $a && $b $a || $b Name UND ODER Exklusiv-ODER NICHT UND ODER Ergebnis true, wenn true, wenn true, wenn true, wenn true, wenn true, wenn beide ($a und $b) true sind mind. einer ($a oder $b) true ist genau einer ($a oder $b) true ist $a false ist beide ($a und $b) true sind mind. einer ($a oder $b) true ist
Tabelle 8.3: Logische Operatoren in PHP Der Unterschied zwischen den beiden UND und ODER liegt in deren Prioritt verglichen a mit anderen Operatoren. 8.2.4.5 Kurzschlulogik Wichtig ist auch zu wissen, da logische Verknpfungen mittels der angegebenen Operau toren immer von links nach rechts evaluiert (ausgewertet) werden; d. h. bei einer UNDVerknpfung (&&) werden alle booleschen Ausdrcke (alles, was zu true oder false evaluu u ieren kann), die rechts von einem stehen, der zu false evaluiert, gar nicht erst abgefragt. Der Fachbegri hierfr lautet Kurzschlulogik, weil die Sprache (in diesem Fall PHP) u erkennt, da der boolesche Ausdruck nicht mehr wahr werden kann und deshalb gleich abbricht (nur die Evaluierung, nicht das Skript!). Das ist wichtig zu wissen, denn so kann man bestimmte Voraussetzungen abfragen, bevor man z. B. eine Funktion aufruft oder eine Abfrage startet, die von einem Parameter abhngt und das alles z. B. in einem Ausdruck! a Ganz analog verhlt es sich bei ODER-Verknpfungen: Wird hier einer der booleschen a u Ausdrcke wahr, werden die anderen nicht mehr bearbeitet. Diesen Umstand kann man u z. B. ausnutzen, indem man huger wahr werdende oder weniger Laufzeit in Anspruch a nehmende Ausdrcke weiter vorne (links) hinschreibt. u Zu beachten ist, da diese Kurzschlulogik nicht bei den bitweisen Operatoren & und | zum Einsatz kommt.
Christoph Reeg
Seite 86
8. PHP Grundlagen
Grundbefehle
8.2.5 Kommentare
Fr Kommentare gibt es zwei Mglichkeiten der Schreibweise: u o echo Noch k e i n Kommentar ! ; / D i e s i s t e i n Kommentar , d e r a uch u e b e r m e h r e r e Z e i l e n g e h e n kann / // D i e s i s t w i e d e r e i n Kommentar , // d e r j e w e i l s b i s zum Ende d e r Z e i l e g e h t echo Kein Kommentar mehr ! ; Die erste und die letzte Zeile sind Befehle, der Rest sind Kommentare.
8.2.6 Variablen
God is real, unless declared integer
Alle Variablen werden durch ein vorangestelltes $ gekennzeichnet. Die Variablen msu sen nicht vorher deniert oder deklariert werden. PHP verwendet den Typ, den es fr u richtig hlt. Der Variablentyp kann auch bei der ersten Benutzung festgelegt werden, ina dem er davor in Klammern angegeben wird. In Tabelle 8.4 sind die verfgbaren Typen u aufgelistet. Neben Variablen gibt es noch Konstanten, die in Kapitel 8.2.7 beschrieben werden. Tabelle 8.4: Typen in PHP int, integer real, double, oat boolean string array object Integer Double Boolean String Array Objekt
8.2.6.1 Integer Eine Integer-Variable kann (auf 32-Bit-Maschinen) alle ganzen Zahlen im Bereich von -2.147.482.648 bis +2.147.482.647 (entspricht 231 1 bis +231 ) als Wert annehmen. Wird einer Integervariablen ein Wert auerhalb des oben genannten Wertebereichs zugewiesen, erfolgt die Umwandlung in den Typ ,double. Man kann Zahlen nicht nur in dem uns gelugen Dezimalsystem (Basis: 10) eingeben. a Es gibt auch noch das hexadezimale System (Basis: 16) und Oktalsystem (Basis: 8). Damit PHP wei, was wir meinen, wird bei Hexadezimalzahlen ein 0x und bei Oktalzahlen eine 0 vorangestellt. Diese Zahlensysteme werden hug wegen ihrer strkeren Hardwarea a Nhe benutzt. Fr uns sind sie im Moment eher weniger interessant. a u
Christoph Reeg
Seite 87
8. PHP Grundlagen
Grundbefehle
8.2.6.2 Double/Float Fr reelle Zahlen gibt es den Datentyp ,Double bzw. ,Float. Der Wertebereich geht (auf u 32-Bit-Maschinen) von ca. -1,7E308 bis ca. 1,7E308 (entspricht 21024 1 bis 21024 1) mit einer Genauigkeit von grob 14 Stellen. 8.2.6.3 Boolean Mit PHP4 ist auch der Datentyp ,Boolean eingefhrt worden. PHP3 hat den booleschen u Wert true als Integer mit dem Wert ,1 interpretiert. 8.2.6.4 String In Strings werden Buchstaben-/Zeichenketten gespeichert. Wenn man Strings deniert, mu ihr Inhalt in Anfhrungszeichen geschrieben werden (siehe auch Kapitel 8.2.1). u Betrachten wir als erstes die doppelten Anfhrungszeichen (). Um z. B. innerhalb der u Anfhrungszeichen eines echo- oder print-Befehls ein Anfhrungszeichen zu schreiben u u (so, da es ausgegeben wird), mu dieses mit einem Backslash (\) versehen werden, weil es sonst den String beenden wrde. Buchstaben mit einem vorangestellten Backslash werden u als escaped characters9 bezeichnet. Eine Ubersicht der escaped characters gibt es in Tabelle 8.5. \n \r \t \\ \$ \ line feed: neue Zeile (Abk. LF, ASCII-Code 0x0A) carriage return (Abk. CR, ASCII-Code 0x0D) horizontaler Tabulator (Abk. HT, ASCII-Code 0x09) Backslash Dollar-Zeichen doppeltes Anfhrungszeichen u Tabelle 8.5: escaped characters Auch bei den einfachen Anfhrungszeichen gibt es escaped characters: Es knnen genau u o zwei verwendet werden, nmlich \ (einfaches Anfhrungszeichen) und \\ (Backslash). a u Ein String ist im Prinzip ein Array aus Zeichen. Dadurch kann man problemlos auf einzelne Zeichen zugreifen. Z. B. wird mit $string[n] auf das n-te Zeichen im String zugegrien. Erfordert eine Funktion als Parameter nun unbedingt ein richtiges Array, mu der String erst konvertiert werden. Die einfachste mir bekannte Mglichkeit ist mit Hilfe o einer kleinen for-Schleife (hier in eine Funktion verpackt): / Die F u n k t i o n w a n d e l t e i n e n S t r i n g i n e i n Array um und g i b t d i e s e s z u r u c k @param string Der T e x t , d e r u m g e w a n d e l t werd en s o l l @return array Array m i t den e i n z e l n e n B u c h s t a b e n des Textes
9
Christoph Reeg
Seite 88
8. PHP Grundlagen
Grundbefehle
/ function str2array ( $ t e x t ){ $ a r = array (); for ( $ i =0; $ i < strlen ( $ t e x t ); $ i ++){ $ a r [] = $ t e x t [ $ i ]; } return $ a r ; } Die bisher unbekannten Befehle sollen erstmal nicht weiter stren; sie werden weiter o unten erklrt. Mehr zu Funktionen im Kapitel 8.7. a 8.2.6.5 Beispiel Ein kleines Beispiel zu den bis hierher beschriebenen Datentypen: Die Funktion int floor(float number) schneidet die Nachkommastellen ab und gibt einen Integer zurck. u $a $b $a $a // $a i s t e i n I n t e g e r // $b i s t e i n D o u b l e m i t dem Wert 1 2 3 4 // O k t a l z a h l ( e n t s p r i c h t 8 3 d e z i m a l ) // H e x a d e z i m a l z a h l // ( e n t s p r i c h t 2 9 8 9 d e z i m a l ) echo floor ((0.1+0.7)*10); // A u s g a b e i s t 7 = = = = 1234; ( double ) $a ; 0123; 0 xbad ;
Die ersten Werte sind noch einfach nachzuvollziehen. Beim letzten ist es allerdings schon schwieriger. Eigentlich wrde man als Ausgabe doch eher eine 8 erwarten. Wieso aber u eine 7 ausgegeben wird, kann man in Worte gefat einfach Rundungsfehler nennen. Die lngere Version: Computer rechnen intern im Dualsystem; bei ganzen Zahlen ergeben a sich keine Rundungsfehler beim Wandeln von Dezimalsystem in das Dualsystem. Bei der Darstellung als Fliekommazahl (float) kommt es aber gelegentlich schon zu Rundungsfehlern. In diesem Beispiel tritt gerade so einer auf. Wenn es zu keinen Rundungsfehlern kommen darf, mu man Integer als Datentyp nehmen. In der Regel ist das auch kein groes Problem, zum Beispiel kann man Preise einfach in Euro Cent abspeichern und schon braucht man keine Nachkommastellen mehr. Das soll uns aber nicht weiter stren. Ich habe dieses Beispiel hier nur zur allgemeinen o Verwirrung eingefgt ;-) . u 8.2.6.6 Array Ein Array ist eine n-dimensionale Liste. Was soll das heien? Nehmen wir folgendes Beispiel: $monat [1] $monat [2] $monat [3] $monat [4] $monat [5] = = = = = J a n u a r ; F e b r u a r ; Maerz ; A p r i l ; Mai ;
Christoph Reeg
Seite 89
8. PHP Grundlagen
Grundbefehle
$monat [6] = J u n i ; $monat [7] = J u l i ; $monat [8] = August ; $monat [9] = S e p t e m b e r ; $monat [10] = O k t o b e r ; $monat [11] = November ; $monat [12] = Dezember ; Oder als Tabelle: Zeile 1 2 3 4 5 6 7 8 9 10 11 12 Name Januar Februar Maerz April Mai Juni Juli August September Oktober November Dezember
Bei dieser Tabelle spricht man von einer 1-dimensionalen Tabelle, weil eine Koordinate (die Zeilennummer) ausreicht, um jedes Feld eindeutig zu bestimmen. Der Index (=Zeilennummer) wird in eckigen Klammern hinter dem Array-Namen angegeben. Wenn man neben dem Monatsnamen auch die Anzahl der Tage10 im jeweiligen Monat abspeichern will, braucht man eine 2-dimensionale Tabelle: Zeile 1 2 3 4 5 6 7 8 9 10 11 12 Und das
10
Name Tage Januar 31 Februar 28 Maerz 31 April 30 Mai 31 Juni 30 Juli 31 August 31 September 30 Oktober 31 November 30 Dezember 31 ganze in PHP:
Christoph Reeg
Seite 90
8. PHP Grundlagen
Grundbefehle
$monat [1][ Name ] = J a n u a r ; $monat [2][ Name ] = F e b r u a r ; $monat [3][ Name ] = Maerz ; $monat [4][ Name ] = A p r i l ; $monat [5][ Name ] = Mai ; $monat [6][ Name ] = J u n i ; $monat [7][ Name ] = J u l i ; $monat [8][ Name ] = August ; $monat [9][ Name ] = S e p t e m b e r ; $monat [10][ Name ] = O k t o b e r ; $monat [11][ Name ] = November ; $monat [12][ Name ] = Dezember ;
$monat [1][ Tage ] = 31; $monat [2][ Tage ] = 28; $monat [3][ Tage ] = 31; $monat [4][ Tage ] = 30; $monat [5][ Tage ] = 31; $monat [6][ Tage ] = 30; $monat [7][ Tage ] = 31; $monat [8][ Tage ] = 31; $monat [9][ Tage ] = 30; $monat [10][ Tage ] = 31; $monat [11][ Tage ] = 30; $monat [12][ Tage ] = 31;
In diesem Beispiel sehen wir zwei wichtige Eigenschaften von Arrays in PHP: Zum einen werden bei mehreren Dimensionen die Indizes einzeln in eckigen Klammern hinter dem Array-Namen angegeben. Zum anderen kann man bei PHP, im Gegensatz zu gewhnlichen o Programmiersprachen, fr die Indizes beliebige skalare11 Datentypen verwenden. Dadurch u werden sogenannte assoziative Arrays mglich. o Wie kann man sich n-dimensionale Arrays vorstellen? Bei 3 Dimensionen ist es noch relativ einfach: Nimmt man mehrere 2-dimensionale Arrays und legt diese aufeinander, hat man die 3. Dimension. Ab der 4. Dimension wird das schon etwas schwerer. Aber im Zweifelsfall mu man sich die Arrays nicht vorstellen, sondern nur Daten in ihnen speichern.
8.2.7 Konstanten
In PHP gibt es neben Variablen auch Konstanten. Bei Konstanten ist, wie der Name schon sagt, der Wert nicht vernderlich, auerdem sind sie uberall abrufbar. Weitere Una terschiede zu Variablen sind, da Konstanten nur Werte von Primitivtypen12 annehmen knnen und uberhaupt grundstzlich von Variablen unterschieden werden mssen: Kono a u stantennamen bestehen nur aus alphanumerischen Zeichen, werden also nicht mit einem Dollarzeichen eingeleitet, und sind vllig unabhngig von Variablen gleichen Namens. Um o a die Unterscheidung deutlich zu machen, nimmt man daher fr die Namen von Konstanten u hug nur Grobuchstaben. a Konstanten werden auch grundstzlich anders deniert, nmlich mittels einer PHPa a Funktion namens define(). Als ersten Parameter ubergibt man dieser Funktion den Na men der zu denierenden Konstante und als zweiten deren Wert. Folgendes Beispiel sollte das Gesagte verdeutlichen: 1 $ v a r = v a r i a b l e r Wert ; 2 define ( CONST, k o n s t a n t e r Wert ); 3 $ v a r = CONST ; 4 echo $ v a r ;
11 12
Skalare Datentypen sind u. a.: integer, string Primitivtypen sind z. B. String oder Integer, im Gegensatz z. B. zu Arrays oder Objektreferenzen, dazu spter mehr a
Christoph Reeg
Seite 91
8. PHP Grundlagen
Grundbefehle
5 $CONST = Problem ? ; Der Beispielcode wrde zu keinem Problem fhren, denn CONST hat weiterhin den Wert, u u der der Konstante mittels define() zugewiesen wurde; gleichzeitig hat durch die Zuweisung in Zeile 3 auch $var den Wert der Konstante angenommen13 . Durch die letzte Zeile wird einfach eine neue Variable angelegt, die denselben Namen hat wie die Konstante. So sollte man jedoch ausdrcklich nicht programmieren! Es sollte u nicht allzu schwierig sein, einen anderen Namen zu nden . . . Zu einem Fehler wrde es erst dann kommen, wenn man in der letzten Zeile das Dollaru zeichen entfernte. Dann nmlich wrde etwas versucht, was per denitionem nicht erlaubt a u ist: einer Konstanten einen neuen Wert zuweisen. Da in PHP fr die Denition einer u Konstanten jedoch eine Funktion benutzt wird, kommt man auch nicht so schnell durcheinander.
Natrlich darf man bei diesem Vorgehen nicht gegen die Regeln fr die Vergabe von u u Variablennamen verstoen, d. h. der String, den man als Namen einer anderen Variable benutzen mchte, darf z. B. nicht mit einer Zahl beginnen, da der Name der neuen Variable o dann ungltig wre. u a Wirklich interessant wird es aber erst, wenn man den Wert bestehender Variablen mit festen Strings kombiniert um damit einen neuen Variablennamen zu erzeugen:14 $ o b j e k t = Auto ; $ { m e i n $ o b j e k t } = C a b r i o ; // $meinAuto = C a b r i o ;
Innerhalb der geschweiften Klammern macht man hier von dem Umstand Gebrauch, da Variablen in Strings, die in doppelten Anfhrungsstrichen stehen, ausgewertet werden. u Natrlich kann man auch mit dem Stringoperator . arbeiten. u Bei Arrays ist schlielich noch zu beachten, da $$array[0] vielleicht nicht immer das macht, was man erwartet will man nun den ersten Eintrag aus $array als Namen fr u eine andere Variable benutzen oder nicht vielmehr den ersten Eintrag aus dem Array
13 14
Das heit aber nicht, da nun die Variable wirklich einen konstanten Wert htte . . . ;-) a Man kann tatschlich sogar soweit gehen, die Namen von Funktionen bzw. Methoden (in der OOP) a mittels Variablen zusammenzusetzen. Das wollen wir aber nicht vertiefen . . .
Christoph Reeg
Seite 92
8. PHP Grundlagen
Grundbefehle
betrachten, dessen Name derselbe ist wie der Wert von $array? Diese Problematik lt a sich leicht umschien, indem man die alternative Syntax mit den geschweiften Klammern benutzt, d. h. entweder die eckigen Klammern mitsamt der Indexangabe (z. B. [0]) in die geschweiften Klammern schreiben oder dahinter.
8.2.9 Vergleichsoperatoren
Weiter oben (8.1) wurden bereits die gnigen Operatoren fr Zuweisungen behandelt. Fr a u u das folgende Kapitel, in dem zum ersten Mal Vergleiche vorkommen, bentigt man jedoch o zustzlich Vergleichsoperatoren: a == != > < >= <= Gleich Ungleich Grer o Kleiner Grer gleich o Kleiner gleich Tabelle 8.6: Vergleichsoperatoren in PHP
8.2.9.1 Typensichere Vergleiche PHP ist von sich aus erst einmal eine nicht streng typisierte Sprache. Das bedeutet im Zusammenhang mit Vergleichen, da Variablen verschiedener Typen i. A. schon dann gleich im Sinne von == sind, wenn ihr Inhalt gleich aussieht. Zwei Variablen sind demnach z. B. gleich bezglich ==, wenn sie dieselbe Zahl als Wert abgespeichert haben egal, ob einmal u als Integer und einmal als String oder beidesmal z. B. als Integer. So kommt es auch, da jeder String bezglich == gleich der Integerzahl 0 ist, auch der u Leerstring . Da das zu unerwnschten Eekten fhren kann (Intentionsfehler), zeigt ein u u Codebeispiel in Kapitel 9.4.1. Umgehen lt sich dieses Problem, indem man zustzlich auf Typgleichheit uberprft. a a u Dazu gibt es in PHP die Operatoren === (gleicher Wert und Typ) und !== (weder gleicher Wert noch Typ).
8.2.10 IF
Die IF-Abfrage ist eines der wichtigsten Elemente der Programmierung; die Syntax ist identisch zu C: if (expr) statement expr ist Platzhalter fr die Bedingung und statement fr den Befehl, der bei erfllter u u u Bedingung ausgefhrt werden soll. Als Beispiel: u if ( $a > $b ) print a i s t g r o e s s e r a l s b ;
Christoph Reeg
Seite 93
8. PHP Grundlagen
Grundbefehle
Falls man mehr als einen Befehl hat, der ausgefhrt werden soll, so ist auch das mglich. u o Man mu die Befehle nur in geschweifte Klammern einschlieen: if ( $a > $b ) { print a i s t $b = $a ; } g r o e s s e r a l s b ;
Man kann natrlich auch nur einzelne Befehle in geschweifte Klammern einschlieen u (und sei es nur, um einheitlich zu programmieren, oder Fehler beim Erweitern zu vermeiden).
8.2.11 ELSE
Wenn man zwei Anweisungen hat, die abhngig von einer Bedingung alternativ ausgefhrt a u werden sollen, so kann man entweder zwei IF-Abfragen mit gegenstzlichen Bedingungen a nehmen. . . if ( $a > $b ) print a i s t g r o e s s e r a l s b ; if ( $a <= $b ) print a i s t n i c h t g r o e s s e r a l s b ; . . . oder aber den ELSE-Zweig verwenden: if ( $a > $b ) print a i s t g r o e s s e r a l s b ; else print a i s t n i c h t g r o e s s e r a l s b ; Analog zu IF kann man auch hier mehrere Befehle pro Zweig (IF, ELSE) angegeben; in diesem Fall mssen diese Anweisungen in geschweiften Klammern geschrieben werden. u Kombinationen (z. B. geschweifte Klammern bei IF, aber keine bei ELSE) sind mglich. o
8.2.12 ELSEIF
ELSEIF ist eine Kombination aus ELSE und IF. Am Beispiel wird dies hoentlich deutlich: if ( $a > $b ) print a } elseif ( $a print a } else { print a } { i s t g r o e s s e r a l s b ; == $b ) { i s t g l e i c h b ; ist k l e i n e r a l s b ;
Christoph Reeg
Seite 94
8. PHP Grundlagen
Grundbefehle
Es gibt Stellen, wo sich diese Syntax sehr gut eignet (z. B. bei der Funktion printf, die in Kapitel 8.8.1 beschrieben wird). Zu huge Anwendung fhrt aber oft auch zu sehr a u schlecht lesbarem Code.
8.2.15 WHILE
WHILE-Schleifen sind die einfachsten Schleifen in PHP. Die Grundform der WHILE-Schleife ist die folgende: WHILE (expr) statement Die Bedeutung der WHILE-Schleife ist einfach: Solange die Bedingung expr erfllt ist, wird u die Anweisung statement ausgefhrt. Falls die Bedingung von Anfang an nicht erfllt ist, u u wird die Anweisung uberhaupt nicht ausgefhrt. Analog zu IF mssen mehrere Anweiu u sungen, die zur selben WHILE-Schleife gehren, in geschweifte Klammern eingeschlossen o werden. Man kann auch die alternative Syntax nehmen: WHILE (expr) : statement ... ENDWHILE; Das Ergebnis der folgenden Beispiele ist identisch; beide geben die Zahlen von 1 bis 10 aus.
Christoph Reeg
Seite 95
8. PHP Grundlagen
Grundbefehle
/ B e i s p i e l 1 / $ i =1; while ( $ i <=10) { print $ i ++; // $ i w i r d e r s t a u s g e g e b e n und // dann i n k r e m e n t i e r t // I n k r e m e n t = N a c h f o l g e r // = um e i n s e r h o e h t ( b e i Z a h l e n ) } / B e i s p i e l 2 / $ i =1; while ( $ i <=10): print $ i ; $ i ++; endwhile ;
8.2.16 DO . . . WHILE
DO ...WHILE-Schleifen sind den WHILE-Schleifen hnlich, es werden allerdings erst die Ana weisungen ausgefhrt und dann wird die Bedingung uberprft. Ein kleines Beispiel: u u $ i =0; do { print $ i ; } while ( $ i >0);
// $ i w i r d g e n a u e i n m a l a u s g e g e b e n
Fr Pascal/Delphi-Kenner: u Die DO ...WHILE-Schleife ist vom Prinzip her identisch mit REPEAT UNTIL.
8.2.17 FOR
FOR-Schleifen15 sind die kompliziertesten Schleifen in PHP. Die Syntax ist identisch mit der in C: FOR (expr1; expr2; expr3) statement Der erste Ausdruck expr1 wird genau einmal, am Anfang, ausgefhrt. Damit initialisiert u man in der Regel die Variable. Der zweite Ausdruck expr2 wird am Anfang jedes Schleifendurchlaufs uberprft. Wenn u die Bedingung erfllt ist, wird die Schleife ausgefhrt; wenn nicht, wird abgebrochen. Das u u ist die Laufbedingung. Am Ende jedes Schleifendurchlaufs wird der dritte Ausdruck expr3 ausgefhrt. u Jeder Ausdruck kann auch leergelassen werden.
15
Wer sich uber den Namen wundert: das englische for hat neben dem allgemein bekannten fr auch u noch eine andere, zeitliche Bedeutung und wird dann z. B. mit fr die Dauer von ubersetzt! u
Christoph Reeg
Seite 96
8. PHP Grundlagen
Grundbefehle
Analog zu IF mssen mehrere Anweisungen, die zur selben FOR-Schleife gehren, in u o geschweifte Klammern eingeschlossen werden. Die FOR-Schleife kann im Prinzip auch mit einer WHILE-Schleife nachgebildet werden. Allgemein ausgedrckt (mit den oben verwendeten Bezeichnern) she das dann so aus: u a expr1; while (expr2){ statement expr3; } Die folgenden Beispiele geben jeweils die Zahlen von 1 bis 10 aus: / B e i s p i e l 1 / for ( $ i =1; $ i <=10; $ i ++) { print $ i ; } / B e i s p i e l 2 / for ( $ i =1;; $ i ++) { if ( $ i > 10) { break ; } print $ i ; } / B e i s p i e l 3 / $ i =1; for (;;) { if ( $ i >10) { break ; } print $ i ; $ i ++; } / B e i s p i e l 4 / $ i =1; while ( $ i <=10){ print $ i ; $ i ++; } Das erste Beispiel ist natrlich das geschickteste. Im vierten Beispiel ist die FOR-Schleife u mit Hilfe von WHILE nachgebildet worden. Wir sehen als erstes die Initialisierung ($i wird auf 1 gesetzt). Die WHILE-Schleife luft so lange, wie die Laufbedingung erfllt ist a u ($i mu kleiner/gleich 10 sein). Dann kommt die eigentliche Anweisung (Ausgabe der
Christoph Reeg
Seite 97
8. PHP Grundlagen
Grundbefehle
Variablenwerte) und als letztes wird $i inkrementiert16 . Mit break wird die aktuelle Schleife verlassen.
8.2.18 SWITCH
Die Funktion der SWITCH-Anweisung ist identisch mit der der CASE OF-Anweisung in Pascal/Delphi. Die Anweisung ist hnlich einer Serie von IF-Anweisungen mit denselben Ausa drcken. In vielen Situationen will man eine Variable oder einen Ausdruck mit vielen u Werten vergleichen und abhngig von den Werten der Variablen unterschiedliche Befehle a ausfhren. Genau das erreicht eine SWITCH-Anweisung. u Hier zwei Beispiele, die dasselbe Ergebnis ausgeben - einmal mit einer Reihe von IFAnweisungen und einmal mit einer SWITCH-Anweisung gelst: o / B e i s p i e l 1 / if ( $ i == 0) { print i i s t g l e i c h 0 ; } if ( $ i == 1) { print i i s t g l e i c h 1 ; } / B e i s p i e l 2 / switch ( $ i ) { case 0: print i i s t g l e i c h 0 ; break ; case 1: print i i s t g l e i c h 1 ; break ; } Es ist wichtig zu wissen, wie die SWITCH-Anweisung arbeitet, um Fehler zu vermeiden. Bei der SWITCH-Anweisung wird Zeile fr Zeile (wirklich, Anweisung fr Anweisung!) abu u gearbeitet. Am Anfang wird kein Code ausgefhrt. Nur dann, wenn eine CASE-Anweisung u mit einem Wert gefunden wird, der gleich dem Wert des SWITCH-Ausdruckes ist, fngt a PHP an, die Anweisungen auszufhren. PHP fhrt fort, die Anweisungen bis an das Ende u a des SWITCH-Blockes auszufhren oder bis es das erste Mal auf eine BREAK-Anweisung stt. u o Wenn man keine BREAK-Anweisung an das Ende einer CASE-Anweisung schreibt, fhrt PHP a fort, Anweisungen uber den folgenden Fall auszufhren. Z. B.: u / B e i s p i e l 3 / switch ( $ i ) { case 0: print i i s t case 1: print i i s t
16
g l e i c h 0 ; g l e i c h 1 ;
Um eins erhht o
Christoph Reeg
Seite 98
8. PHP Grundlagen
Grundbefehle
} Falls $i gleich 0 sein sollte, wrden beide Anweisungen ausgegeben, was in diesem Fall u nicht erwnscht wre. u a Dieses Verhalten kann aber auch bewut genutzt werden, wie man in den folgenden Fllen sieht. Solcher Code wird allerdings sehr schnell unleserlich, weil er sich anders a verhlt, als man es auf den ersten Blick erwartet. Von daher sollte man das entweder a vermeiden oder ein paar Kommentare einfgen. u / B e i s p i e l 4 / switch ( $ i ) { case 0: print i i s t break ; case 1: case 2: print i i s t } / B e i s p i e l 5 / switch ( $ i ) { case 0: print i i s t case 1: case 2: print i i s t }
g l e i c h 0 ;
g l e i c h 1 o d e r 2 ;
g l e i c h 0 ;
g l e i c h 0 o d e r 1 o d e r 2 ;
Ein spezieller Fall ist der default-Fall. Dieser Fall trit auf alles zu, was nicht von den anderen Fllen abgedeckt wird. a / B e i s p i e l 6 / switch ( $ i ) { case 0: print i i s t g l e i c h 0 ; break ; case 1: print i i s t g l e i c h 1 ; break ; default : print i i s t u n g l e i c h 0 , 1 ; } Gibt man hinter einem break eine Zahl (int) grer Null an, wird versucht, diese Ano zahl an Ebenen hinauf (hinaus) zu springen. Anwendung ndet dies bei verschachtelten Schleifen, wie z. B. im folgenden: $ i = 0;
Christoph Reeg
Seite 99
8. PHP Grundlagen
Grundbefehle
while ( $ i < 10) { $ i ++; $ j = 0; while ( $ j < 10) { $ j += 2; if ( $ i > $ j ) break 2; } } Das Beispiel stoppt mit den Werten 3 und 2 in $i und $j, weil $j in der inneren Schleife immer mindestens den Wert 2 hat.
8.2.19 foreach
Wenn man alle Eintrge eines Arrays durchgehen will, nimmt man dazu meist eine fora Schleife. Handelt es sich um ein assoziatives Array oder spielt der Index keine Rolle bei der Auswertung der Daten, bietet sich eine alternative Schleife an, die sog. foreach-Schleife. Eine Gegenberstellung der beiden zeigt, wie viel einfacher letztere zu handhaben ist: u $ t a g e = array ( Montag , D i e n s t a g , Mittwoch , D o n n e r s t a g , F r e i t a g , Samstag , S o n n t a g ); // m i t f o r for ( $ i =0; $ i < count ( $ t a g e ); $ i ++) echo $ t a g e [ $ i ]; // m i t f o r e a c h foreach ( $ t a g e as $ t a g ) echo $ t a g ; Obiges Skript gibt nacheinander und ohne Leerzeichen oder Zeilenumbrche zweimal u alle Wochentage aus. Wie man leicht sieht, weist man bei foreach nacheinander die Eintrge eines Arrays a (erste Variable) einer lokalen Variable zu, die in der Syntax hinter dem Schlsselwort as u angegeben wird. Bei jedem Schleifendurchlauf enthlt diese lokale Variable eine Kopie a (!) der Daten des jeweiligen Array-Eintrags; hierbei kann es sich z. B. auch wieder um ein Array handeln (wobei das eigentliche Array dann multidimensional wre). a Besonders problematisch ist die erwhnte Kopiersemantik im Zusammenhang mit Oba jektreferenzen, siehe auch 20.2.1.3. Zu beachten ist auerdem, da foreach ein Array als ersten Eingabe erwartet, sonst gibt es eine Fehlermeldung. Um sicherzustellen, da die Eingabe immer ein Array ist, kann man die Variable im Zweifelsfall initialisieren (auch auf Funktionen mit hnlicher a Problematik, wie z. B. in_array, anwendbar): if (! isset ( $myArray )) $myArray = array ();
Christoph Reeg
Seite 100
8. PHP Grundlagen
Grundbefehle
foreach ( $myArray as $ k e y => $ v a l ) echo $ k e y : $ v a l \ n ; Richtig interessant wird es aber erst mit der erweiterten Syntax, die es erlaubt, auch den Index (auch key, engl.: Schlssel) des Arrays einer lokalen Variable zuzuweisen das u geht nmlich mit einer for-Schleife nicht so ohne weiteres: a $ p r o d u k t e = array ( Auto => 2 2 3 9 9 , F a h r r a d => 349 , S k a t e s => 229 ); $ g u t h a b e n = 1325; foreach ( $ p r o d u k t e as $ t y p => $ p r e i s ) { printf ( %s : % s EUR\ n , $ t y p , number_format ( $ p r e i s , 2, , , . )); if ( $ p r e i s <= $ g u t h a b e n ) { $ g u t h a b e n -= $ p r e i s ; $ g e k a u f t [ $ t y p ] = true ; } } Die obige Funktion kauft selbstndig so lange Produkte, bis entweder die Liste durcha laufen ist wie im Beispiel oder das Guthaben nicht mehr reicht. Wenn man es nur an dieser Stelle verwendet, knnte man $produkte auch direkt als Eingabe fr foreach o u hinschreiben, sich also die Variable ersparen. Im konkreten Fall liest sich die Ausgabe wie folgt: Auto: 22.399,00 EUR Fahrrad: 349,00 EUR Skates: 229,00 EUR Wenn man sich vorstellt, da Arrays nicht nur statisch wie im Beispiel, sondern auch als Resultat einer Datenbank-Abfrage oder durch sonstige Berechnungen (z. B. POST /GET-Ubergabe) gefllt werden knnen, macht der Zugri auf den Index gleich mehr u o Sinn; bei komplexeren Arrays wird auch das Programmieren wesentlich ubersichtlicher, wenn Namen statt Indizes benutzt werden, um mehrdimensionale Arrays zu strukturieren (vgl. auch mysql_fetch_array(): 10.1.6 und mysql_fetch_row(): 10.1.7).
8.2.20 continue
Fr alle oben beschriebenen Schleifen (do, while, switch, for und foreach) gibt es analog zu u break die Mglichkeit, die Schleife fortzusetzen, also zum Anbfrageteil der Schleife (den o sog. Kopf) zu springen. Beispiel: $ i = 10; while ( $ i > 0) { $ i --; if (!( $ i %2)) continue ;
Christoph Reeg
Seite 101
8. PHP Grundlagen
include
echo $ i ; } Die obige Schleife gibt genau die ungeraden Zahlen bis 10 aus, in absteigender Reihenfolge. Optional kann hinter continue ein Integerwert angegeben werden, der analog zu break die Anzahl der hinauszugehenden Ebenen angibt.
8.3 include
Der Befehl include("dateiname"); fgt an dieser Stelle den Inhalt der Datei dateiname ein. Dadurch ist es mglich, Quellcou o de, der in mehreren Dateien bentigt wird, zentral zu halten, so da Anderungen einfacher o werden. Die Datei, die eingefgt wird, wird als HTML-Code interpretiert, deshalb mu, wenn u in der Datei nur PHP-Code steht, diese Datei mit <?php anfangen und mit ?> aufhren o (bzw. mit anderen PHP-Code-Markierungen, siehe Kapitel 8.2). Wenn include() in Verbindung mit Bedingungen oder Schleifen eingesetzt wird, mu es immer in geschweiften Klammern geschrieben werden. / So i s t e s f a l s c h / if ( $ B e d i n g u n g ) include ( D a t e i . i n c ); / So i s t e s r i c h t i g / if ( $ B e d i n g u n g ){ include ( D a t e i . i n c ); }
8.4 require
Ganz analog zu include() funktioniert require(). Es wird aber von PHP3 etwas anders behandelt. Seit PHP4 gibt es keinen Unterschied mehr. Bei PHP3 wird der require()-Ausdruck beim ersten Aufruf durch die Datei ersetzt. Wie bei include() wird erst einmal aus dem PHP-Modus gesprungen. Es gibt drei wesentliche Unterschiede zu include(): Zum einen wird require() immer ausgefhrt, also auch dann, u wenn es eigentlich abhngig von einer IF-Bedingung nicht ausgefhrt werden drfte; zum a u u anderen wird es innerhalb einer Schleife (FOR, WHILE) nur ein einziges Mal ausgefhrt u egal, wie oft die Schleife durchlaufen wird (trotzdem wird der Inhalt der eingefgten Datei u mehrmals abgearbeitet). Der dritte Unterschied liegt in der Reaktion auf nicht vorhandene Dateien: include() gibt nur ein Warning aus und PHP luft weiter, bei require() bricht a PHP mit einem Fatal error: ab.
Christoph Reeg
Seite 102
8. PHP Grundlagen
Christoph Reeg
Seite 103
8. PHP Grundlagen
Kleine for-Schleife fr require u Dies ist ein einfacher Text. Die Variable i hat den Wert 1. Dies ist ein einfacher Text. Die Variable i hat den Wert 2. Dies ist ein einfacher Text. Die Variable i hat den Wert 3. Als letztes ein Beispiel, wo man einen Unterschied zwischen include() und require() sehen kann: echo \ n K l e i n e f o r S c h l e i f e f u e r i n c l u d e<b r>\n ; for ( $ i =1; $ i <=3; $ i ++){ include ( t e s t $ i . t x t ); } echo \ n K l e i n e f o r S c h l e i f e f u e r r e q u i r e<b r>\n ; for ( $ i =1; $ i <=3; $ i ++){ require ( t e s t $ i . t x t ); } Hier gibt es einen Unterschied zwischen PHP3 und PHP4. Die Ausgabe bei PHP3 ist folgende: Kleine for-Schleife fr include<br> u Datei test1.txt Datei test2.txt <br> <b>Warning</b>: Failed opening test3.txt for inclusion in <b>.... Kleine for-Schleife fr require<br> u Datei test1.txt Datei test1.txt Datei test1.txt Die Ausgabe von PHP4: Kleine for-Schleife fr include<br> u Datei test1.txt Datei test2.txt <br> <b>Warning</b>: Failed opening test3.txt for inclusion (include_.... Kleine for-Schleife fr require<br> u Datei test1.txt Datei test2.txt <br> <b>Fatal error</b>: Failed opening required test3.txt (include_....
Christoph Reeg
Seite 104
8. PHP Grundlagen
Die include()-Anweisung wird bei PHP3 und PHP4 identisch behandelt. In beiden Fllen wird versucht, die Dateien test1.txt, test2.txt und test3.txt einzufgen, wobei a u letzteres mangels vorhandener Datei nicht funktioniert. Bei require() sieht das ganze etwas anders aus. PHP3 verhlt sich, wie ich es erwartet a habe. Die Datei wird einmal eingefgt und dann wird der Inhalt der Datei mehrmals u aufgerufen. Bei PHP4 verhlt sich die require() wie die include() Anweisung. a
8.7 Funktionen
Funktionen dienen dem Zusammenfassen mehrerer Befehle zu einem Aufruf. Dadurch werden Programme einfacher lesbar, weil klar ist, wozu ein Befehlsblock dient. Bei einigen Programmiersprachen ndet eine Unterscheidung zwischen Funktionen statt, die einen Wert zurckgeben und solchen, die keinen Wert zurckgeben. Z. B. in Pascal/u u Delphi gibt es neben den sog. Funktionen, die einen Wert zurckgeben, sog. Prozeduren, u die keinen Wert zurckgeben. PHP macht hier, genau wie C und C++, keinen Unterschied. u Die Syntax lautet wie folgt: function foo($arg_1, $arg_2, ..., $arg_n) { echo "Example function.\n"; return $retval; } Die Funktion bekommt die Argumente Arg 1 bis Arg n ubergeben und gibt den Wert der Variablen retval zurck. Wird kein return in der Funktion benutzt, hat man dasselbe u Verhalten wie bei einer Prozedur in Pascal/Delphi. Rckgabewerte mssen (im Gegensatz u u zu Pascal/Delphi) nicht abgefragt werden. Ein kleines Beispiel: function my_sqr ($num) { return $num * $num; } echo my_sqr (4); my_sqr (4); // g i b t d a s Q u a d r a t von $num z u r u e c k
// // // //
Christoph Reeg
Seite 105
8. PHP Grundlagen
Formatierte Textausgabe
8.7.1 Variablenparameter
Normalerweise werden in PHP Werteparameter17 ubergeben18 . Will man jedoch die An derungen der Parameter in der Funktion auch in der aufrufenden Funktion haben, mu man mit Variablenparametern19 bzw. Referenzparametern20 arbeiten21 . Variablenparameter werden mit einem & im Funktionskopf gekennzeichnet. Ein kleines Beispiel: function foo1 ( $ s t ) { $ s t .= und e t w a s mehr . ; // g l e i c h b e d e u t e n d m i t $ s t = $ s t . und e t w a s mehr . ; } function foo2 (& $ s t ) { $ s t .= und e t w a s mehr . ; } $str echo foo1 echo foo2 echo = Dies i s t ein String ; $str ; // A u s g a b e : D i e s i s t e i n S t r i n g ( $ s t r ); $str ; // A u s g a b e : D i e s i s t e i n S t r i n g ( $ s t r ); $str ; // A u s g a b e : // D i e s i s t e i n S t r i n g und e t w a s mehr .
In der Funktion wird mit einer Kopie der Variablen gearbeitet wird auch als Call by valuebezeichnet Es wird mit den Originalvariablen gearbeitet, weil nur die Adresse ubergeben wird Zwei Namen fr dasselbe u wird auch als Call by reference genannt
Jens Hatlak
Seite 106
8. PHP Grundlagen
Formatierte Textausgabe
Form weiterer Argumente angegeben wird. Im Normalfall22 gibt es also genau so viele zustzliche Argumente, wie Platzhalter in den String eingebaut wurden der ubrigens auch a vollstndig in einer Variable enthalten sein kann. a Die genannten Platzhalter werden grundstzlich mit einem Prozentzeichen ,% eingeleia tet, alles andere wird 1:1 ausgegeben ausgenommen natrlich Ausgabe-Formatierungsu zeichen wie \n. Will man nun das Prozentzeichen selbst ausgeben, mu man es durch zwei Prozentzeichen ausdrcken23 . Reines Ersetzen allein ist jedoch noch lange nicht alles, was u uns diese Funktion bietet. Vielmehr kann man mithilfe der Platzhalter genau vorgeben, wie der Wert, der an entsprechender Stelle eingefgt wird, formatiert werden soll. Hierbei u reichen die Mglichkeiten von der Festlegung auf einen bestimmten Typ wie Zahlen zu o verschiedenen Basen bis hin zum Zurechtschneiden, Ausrichten und Aullen von Daten. u Die beiden wichtigsten Platzhalter sind ubrigens %s und %d. Whrend erstgenannter a einen beliebigen String als Wert akzeptiert, wird bei letzterem der Wert als Integer interpretiert und als Dezimalzahl ausgegeben. Alle Werte, die kein Integer sind, werden kurzerhand in eine 0 verwandelt. Auf diese Weise kann man also sicher stellen, da an einer bestimmten Stelle im auszugebenden String auch wirklich eine Zahl steht. Die Tabelle Platzhalter listet alle erlaubten Platzhalter auf. Platzhalter %b %c %d %u %f %o %s %x %X Behandlung Integer Integer Integer Integer Double Integer String Integer Integer Darstellung Binrzahl a Zeichen mit entsprechendem ASCII-Code Dezimalzahl, ggf. mit Vorzeichen Dezimalzahl ohne Vorzeichen Fliekommazahl Oktalzahl String Hexadezimalzahl mit Kleinbuchstaben Hexadezimalzahl mit Grobuchstaben
Tabelle 8.7: printf: Platzhalter Zwischen dem einen Platzhalter einleitenden Prozentzeichen und dem Buchstaben, der die Typisierung des Platzhalters bestimmt, knnen noch weitere Angaben zur Formao tierung gemacht werden. Diese sind allesamt optional und mssen, wenn sie angegeben u werden, in folgender Reihenfolge auftreten: 1. Das Fllzeichen. Dieses wird benutzt, um den eingefgten Wert auf der rechten Seite u u bis zur weiter unten angegebenen Lnge aufzufllen. Standardmig wird hierbei a u a mit Leerzeichen aufgefllt, es kann aber auch eine 0 (Null) oder ein beliebig anderes u Zeichen angegeben werden, wobei letzteres dann mit einem einfachen Hochkomma () gekennzeichnet werden mu. 2. Die Ausrichtung. Normalerweise wird rechtsbndig ausgereichtet; stellt man den folu genden Angaben jedoch ein Minuszeichen (-) voran, wird linksbndig formatiert. u
22 23
Ausnahmen besttigen die Regel, aber dazu weiter unten mehr a Im Gegensatz zum sonst in PHP ublichen Escapen mithilfe des Backslashes
Jens Hatlak
Seite 107
8. PHP Grundlagen
Formatierte Textausgabe
3. Die Lnge. Die Zahl, die man hier angibt, bestimmt die Anzahl der Zeichen, die die a formatierte Ausgabe des jeweiligen Wertes mindestens umfassen soll. 4. Die Nachkommastellen. Hier gibt eine Zahl, gefolgt von einem Dezimalpunkt, die Anzahl der Dezimalstellen fr Fliekommazahlen an. Ist der gewhlte Typ fr die u a u Darstellung nicht double, so hat diese Angabe keine Bedeutung. Fr komplexere u Zahlenformatierungen sollte die Funktion number_format() (Kapitel 8.8.3) genutzt werden. Insgesamt ergibt sich somit folgende Syntax fr Platzhalter: u %[Fllzeichen][Ausrichtung][Lnge][Nachkommastellen]Typisierung u a Im Allgemeinen braucht man die optionalen Angaben aber recht selten, so da man dann ja immer noch mal nachlesen kann und es sich nicht wirklich merken mu. Folgende Beispiele demonstrieren, wie sich das eben gelernte nun tatschlich einsetzen a lt: a $ t a g = 13; $monat = 5; $ j a h r = 2009; $ f o r m a t = %02d .%02 d .%04 d \ n ; printf ( $ f o r m a t , $ t a g , $monat , $ j a h r ); printf ( $ f o r m a t , 2* $ t a g , $monat , $ j a h r +2); Gibt 13.05.2009 und ein anderes wichtiges Datum aus. $ b e t r a g 1 = 12.95; $ b e t r a g 2 = 57.75; $betrag = $betrag1 + $betrag2 ; echo $ b e t r a g . ; printf ( %01.2 f , $ b e t r a g ); Gibt 70.7 70.70 aus. Ist nun eine der Variablen fr die Platzhalter im Formatierungs-String von anderen abu hngig, mte normalerweise eine if-Abfrage vor dem Funktionsaufruf gettigt werden. a u a Ist diese Abfrage jedoch simpel, so kann man praktischerweise auch die alternative Kurzvariante benutzen, die in Kapitel 8.2.14 vorgestellt wurde. Um klar zu machen, da es sich um eine solche handelt und ggf. auch, um Ergnzungen mittels des Punkt-Operators zu a 24 , sollte man diese in runden Klammern schreiben. erlauben Das folgende Beispiel gibt fr die Zahlen von 0 bis 9 jeweils aus, ob sie gerade oder u ungerade ist: for ( $ i =0; $ i <10; $ i ++) printf ( %d i s t % s g e r a d e . \ n , $i ,
24
U. U. kann es ja sein, da man den Formatierungs-String nicht verndern mchte, z. B. weil er schon a o in Form einer Variablen vorliegt
Jens Hatlak
Seite 108
8. PHP Grundlagen
Formatierte Textausgabe
( $ i %2==0 ? : un ) ); An dieser Stelle mchte ich die Gelegenheit nutzen, darauf hinzuweisen, da die printfo Syntax nur dann als guter Stil bezeichnet werden kann, wenn man sie auch sinnvoll einsetzt. Kommt z. B. eine einzelne Variable am Ende eines Strings vor, kann man sie einfach direkt mit dem Punktoperator anhngen und mu nicht gleich zu printf greifen. a Ahnlich verhlt es sich bei Variablen, die selbst vom Typ String sind und deren Name aus a einem einzelnen Wort besteht. Dann kann man nmlich von der Eigenschaft der doppelt a gequoteten Strings Gebrauch machen, da Variablen in ihnen ersetzt werden. 8.8.1.1 Argumente vertauschen/numerieren Seit PHP-Version 4.06 kann man die Argumente (zur Erinnerung: das sind die zustzlichen a Parameter, deren Werte anstelle der Platzhalter ausgegeben werden) durchnumerieren und entsprechend referenzieren. Dadurch ergibt sich nicht nur die Mglichkeit, Argumente in o der Reihenfolge ihres Auftretens bei der Ersetzung zu vertauschen, sondern auch bestimmte Argumente mehrfach zu plazieren, dabei jedoch ggf. verschieden zu formatieren. Eine Referenz auf ein Argument deniert man nun mittels folgender Syntax: %[Argumentnummer\$][sonstige Formatierung]Typisierung Folgendes Beispiel einer Sprach-angepaten Datumsausgabe verdeutlicht die Vorteile25 : // Angenommen , i n $ l a n g s t e h e d i e S p r a c h e if ( $ l a n g == de ) { $ t e x t = Heute i s t d e r %1\ $d .%2\ $d .%3\ $04d . ; } elseif ( $ l a n g == en ) { $ t e x t = Today s d a t e i s %3\ $04d%2\$02d%1\$02d . ; } else { // u n b e k a n n t e S p r a c h e > w i r g e b e n nur d i e Z a h l e n a u s $ t e x t = %3\ $04d%2\$02d%1\$02d ; } $ t a g = 13; $monat = 5; $ j a h r = 2009; // A u s g a b e j e nach S p r a c h e : // de > H e u t e i s t d e r 1 3 . 5 . 2 0 0 9 // en > Today s d a t e i s 20090513 printf ( $ t e x t , $ t a g , $monat , $ j a h r ); Aber nocheinmal der Hinweis: Gerade durch die Mglichkeit des Numerierens kann die o Komplexitt des Codes schnell ansteigen (und das in wenigen Zeilen Code) und dadurch, a a a u a a hnlich wie bei regulren Ausdrcken, die spter noch behandelt werden, das Verstndnis
25
Man htte hier auch an Stelle der ganzen if und elseif auch eine switch-Anweisung nehmen knnen a o
Jens Hatlak
Seite 109
8. PHP Grundlagen
Formatierte Textausgabe
erheblich erschwert werden. Zugunsten der Lesbarkeit sollte man also auf allzu verspielte Konstruktionen verzichten und dann, wenn es doch einmal der Ubersichtlichkeit dienen sollte, zumindest gut kommentieren. Ein Beispiel fr schlechten Stil wre es demnach, schon beim Vorkommen eines einzelnen u a doppelten Arguments mit der Kanone Referenzierung auf den Spatz String zu schieen. . .
8.8.2 sprintf
Im Prinzip arbeitet sprintf genauso wie printf und folgt exakt derselben Parametrisierung. Verwendung ndet es im Gegensatz zu letzterem jedoch besonders gerne bei Zuweisungen oder als Parameter fr andere Funktionsaufrufe sprintf liefert ja den erzeugten u String zurck, anstatt ihn direkt auszugeben. u sprintf() lt sich besonders gut im Zusammenhang mit SQL-Abfragen (siehe auch a nchstes Kapitel) besonders gut einsetzen, weil man damit den eigentlichen Abfrage-String a sauber von den Werten trennen kann, die in PHP-Variablen stecken. Das Prinzip ist immer das gleiche: Der eigentlichen Abfragefunktion mysql_query() ubergibt man als einzigen Parameter das sprintf()-Konstrukt, das den SQL-String zusammenbaut und zurckgibt u (der Empfnger ist, bedingt durch die Schachtelung, die Abfragefunktion). a Innerhalb dieses Konstruktes herrscht eine einfache Zweiteilung: der erste Parameter deniert das String-Grundgerst, also i. A. alle SQL-Befehle und Vergleichsoperatoren; die u restlichen Parameter geben die Quellen (Variablen) fr die Werte an, die die im Stringu Grundgerst einzubauenden Platzhalter ersetzen. Fr diese Variablen gilt ubrigens, da u u man sie mittels addslashes() behandeln sollte26 , falls ihre Werte mglicherweise Hocho kommata enthalten knnten diese werden durch die erwhnte Funktion mittels Backso a lash escaped. Fr die Rckwandlung bei der Ausgabe an anderer Stelle steht die Funku u tion stripslashes() zur Verfgung, die mit htmlentities() kombiniert gerade bei der u HTML-Ausgabe ntzlich ist. u Eine schne Aufgabe zum Gesagten ndet sich in Kapitel 10.3.3. o
Das allerdings nur auf Servern, bei denen PHP dies nicht schon implizit macht! Komma als Tausender-Trennzeichen und Punkt als Dezimaltrenner
Jens Hatlak
Seite 110
8. PHP Grundlagen
Guter Stil
Bei zwei Parametern wird die Zahl mit decimals Stellen hinter dem Komma ausgegeben. Auch hier trennt wieder ein Komma jede Tausenderstelle; die Nachkommastellen werden vom ganzzahligen Wert durch einen Punkt getrennt. Die Ausgabe bei Angabe aller vier Parameter schlielich unterscheidet sich durch die bei nur zweien dadurch, da sie die Zeichen fr den Dezimaltrenner und das Tausenderu Trennzeichen (in dieser Reihenfolge) verwendet. Achtung: Es wird nur das erste Zeichen des Tausender-Trennzeichens fr die Ausgabe u benutzt! Das Beispiel verdeutlicht, wofr diese Funktion vornehmlich benutzt werden kann: u function Euro ( $ p r e i s ) { return sprintf ( %s EUR\ n , number_format ( $ p r e i s , 2, , , . ) ); } echo Euro (17392.48365); // A u s g a b e : 1 7 . 3 9 2 , 4 8 EUR
Programmieren ist eine Sache. So zu programmieren, da nicht nur man selbst, sondern auch andere den Code spter in mglichst kurzer Zeit verstehen und daraufhin auch era o weitern knnen, eine andere. Man darf auch nicht vergessen, da es passieren kann, da o man selbst irgendwann mal ein Jahr spter oder so noch etwas ndern will oder mu. a a Dabei ist es im Prinzip gar nicht so schwer: Das Ziel lt sich erreichen, indem man struka turiert, kommentiert und abstrahiert (d. h. einen Codeabschnitt im Kontext betrachtet) sowie auf Wiederverwendbarkeit achtet. Fr letzteres hat sich die objektorientierte Denku weise als hilfreich erwiesen und auch in PHP Einzug gehalten (siehe Kapitel 18). Selbst Kommentieren will gelernt sein hier bietet sich PHPDOC (Kapitel 14) an. Wenn es jedoch zur Struktur kommt, fragt sich mancher angesichts ellenlanger Codekonstrukte schnell, wie man dem beikommen soll. Da wird geschachtelt, was das Zeug hlt, a Zeichen werden wild escaped und Parameter derart in Strings eingebettet, da selbst der Autor des Scripts sich nicht mehr an eine Anderung heranwagen mchte. Viele Autoren o scheinen einfach nicht zu wissen, welch uberaus hilfreiche und strukturierende Funktionen PHP von Hause aus bietet. Vielleicht tragen ja diese Zeilen dazu bei, da sich das ndert a ich hoe es jedenfalls. Ahnlich wie in der Sprache C, von der PHP bekanntlich stark beeinut ist, bietet unsere Scriptsprache die Funktionen printf() und sprintf(). Whrend printf() von print() a abgeleitet ist, also letztlich Text direkt ausgibt, dient sprintf() dazu, die Ausgabe der
Jens Hatlak
Seite 111
8. PHP Grundlagen
Guter Stil
Funktion als Argument (Parameter) einer weiteren Funktion oder einfach als Wert einer Zuweisung zu benutzen28 .
Worauf schon das ,s hindeutet: Es steht fr silent, zu deutsch: still. u Eine Variable, der noch kein Wert zugewiesen wurde, ist nicht initialisiert
Jens Hatlak
Seite 112
8. PHP Grundlagen
Guter Stil
// E i n f a c h e L a u f z e i t f e h l e r m e l d e n error_reporting ( E_ERROR | E_WARNING | E_PARSE ); // A l l e F e h l e r a u s s e r E NOTICE m e l d e n // D i e s i s t d i e S t a n d a r d e i n s t e l l u n g i n php . i n i error_reporting ( E_ALL ^ E_NOTICE ); // A l l e PHPF e h l e r m e l d e n // IMHO zum E n t w i c k e l n e m p f o h l e n error_reporting ( E_ALL ); // S e i t PHP5 g i b t e s noch e i n e S t e i g e r u n g error_reporting ( E_ALL | E_STRICT ); Als Parameter nimmt man einfach die gewnschten Fehler und verknpft sie mit einem u u bitweisen ODER.
Jens Hatlak
Seite 113
8. PHP Grundlagen
Rekursion
8.10 Rekursion
Um Rekursion verstehen zu knnen, o mu man erst Rekursion verstanden haben.
Rekursion ist ein beliebtes Mittel der Programmierpraxis, um Probleme zu lsen, die an o mindestens einer Stelle zu einem weiteren Problem fhren, das aber in Wirklichkeit nichts u anderes ist als das ursprngliche Problem, ggf. mit leicht vernderten Voraussetzungen. u a Das klingt kompliziert, ist im Prinzip aber ganz einfach wie das folgende beliebte Beispiel zeigt. In der Mathematik gibt es den Begri der Fakultt. Die Fakultt einer natrlichen Zahl a a u ist deniert als das Produkt aller Zahlen von 1 bis zu dieser Zahl, also z. B. 5! = 12345 = 120. Mittels Rekursion kann man das wie folgt ausdrcken30 : 1! = 1, n!n=1 = n (n 1)!, u d. h. die Fakultt von 1 wird deniert als 1 und fr n = 1 ist die Fakultt von n gleich n mal a u a der Fakultt von n1. Um das Ergebnis zu berechnen, mu man also erst die Fakultt von a a n 1 berechnen. Diese ist aber deniert als n 1 mal die Fakultt von (n 1) 1 = n 2 a usw. Irgendwann wird man dahin kommen, die Fakultt von n x zu berechnen, wobei a dieser Ausdruck den Wert 1 haben wird. Dann ist es mit der Rekursion vorbei und die erste Denition greift: Die Fakultt von 1 ist gleich 1. An dieser Stelle hat man also ein a erstes Teilergebnis, das mit all den gemerkten Faktoren (der erste war n) multipliziert werden mu man geht also den ganzen Weg wieder zurck und sammelt dabei das u auf, was man liegen gelassen hat. Am Ende hat man auf diese Weise die eigentliche Fakultt berechnet. Das folgende kleine Beispiel sollte das Prinzip verdeutlichen: a
1! = 1 2! = 2 1 3! = 3 2 1 4! = 4 3 2 1 5! = 5 4 3 2 1 6! = 6 5 4 3 2 1 = 2 (1) = 3 (2 1) = 4 (3 2 1) = 5 (4 3 2 1) = 6 (5 4 3 2 1) = 2 1! = 3 2! = 4 3! = 5 4! = 6 5!
Die Frage, welchen Vorteil diese Methode gegenber dem einfachen Zhlen und Multiu a plizieren hat, lt sich an diesem Beispiel noch nicht klren; mehr dazu spter. Doch nun a a a zu dem Paradebeispiel fr Rekursion schlechthin: u
Da auch deniert wurde, das 0! = 1, kann man die Rekursion auch bis n = 0 laufen lassen. Von der Vorgeschichte scheint es Dutzende von Abwandlungen zu geben. Letztlich ist sie aber eh nur erfunden . . .
Jens Hatlak
Seite 114
8. PHP Grundlagen
Rekursion
Erschwert wird das Ganze dadurch, da immer nur eine Scheibe gleichzeitig bewegt werden und auerdem nie eine grere auf einer kleineren Scheibe liegen darf. Der Legende o nach geht die Welt unter, wenn die Mnche ihr Werk vollendet haben . . . o Schnell wird klar, da diese Aufgabe mit nur zwei Stapeln nicht lsbar ist: Ein dritter o Stapel mu her. Mit diesen Vorgaben ist es nun mglich, die Aufgabe theoretisch fr jede o u beliebige Anzahl von Scheiben zu lsen. Der zugehrige Algorithmus (das Problem wurde o o von einem Mathematiker deniert . . . ) lautet nun in Pseudo-Syntax: Hanoi(n, Start, Temp, Ziel) { Wenn n=0, dann Ende Hanoi(n-1, Start, Ziel, Temp) Bewege von Start nach Ziel Hanoi(n-1, Temp, Start, Ziel) } Oder als normaler Text: Um einen Turm der Hhe n vom Start-Stapel auf den Zielo Stapel zu bewegen, bewegt man erstmal einen Turm der Hhe n-1 (also alles bis auf die o letzte Scheibe) auf einen Temp-Stapel, legt dann die letzte Scheibe auf den Ziel-Stapel und schichten danach den Turm vom Temp-Stapel auch noch auf den Ziel-Stapel. Abgesehen davon, da so eine Scheibe bei entsprechender Hhe des Stapels doch recht o schwer werden drfte (nach unten hin wirds ja immer grer!) gibt es noch ein Problem u o ganz anderer Natur: Das Umschichten der Stapel kostet Zeit. Gehen wir einmal gemeinsam den Algorithmus durch fr drei Scheiben (vgl. Bild 8.1): u 1. Bewege die kleine Scheibe von Stapel 1 nach Stapel 3. 2. Bewege die mittlere Scheibe von Stapel 1 nach Stapel 2. 3. Bewege die kleine Scheibe von Stapel 3 nach Stapel 2. 4. Bewege die groe Scheibe von Stapel 1 nach Stapel 3. 5. Bewege die kleine Scheibe von Stapel 2 nach Stapel 1. 6. Bewege die mittlere Scheibe von Stapel 2 nach Stapel 3. 7. Bewege die kleine Scheibe von Stapel 1 nach Stapel 3. Man knnte meinen, da das doch noch akzeptabel wre. Das Problem wird aber schnell o a deutlich, wenn man die Flle vier, fnf oder sechs Scheiben durchspielt. Dabei ergibt a u sich nmlich ein Aufwand von 15, 31 und 63 Schritten. Wer sich jetzt an eine gewisses a Schachbrett32 erinnert, der liegt richtig: Der Aufwand wchst exponentiell mit der Anzahl a der Scheiben. Genauer gesagt sind 2n 1 Schritte notwendig, um n Scheiben vom Start32
Der Legende nach versprach der Knig dem Ernder des Schachspiels ihm einen Wunsch zu erfllen. o u Dieser dachte lange nach und wnschte sich dann: Er wollte Reis auf ein Schachbrett. Und zwar auf u das erste Feld 1 Korn, auf das zweite Feld 2 Krner, auf das dritte Feld 4 Krner, auf das vierte o o Feld 8 Krner usw. bis zum 64. Feld. Der Knig lachte und versprach dem Ernder den Wunsch zu o o erfllen. Trotz aller Anstrengungen war es ihm aber unmglich. Der Knig htte mehr als die 800fache u o o a Jahresproduktion an Reis von 1994 gebraucht.
Jens Hatlak
Seite 115
8. PHP Grundlagen
Rekursion
Jens Hatlak
Seite 116
8. PHP Grundlagen
Rekursion
auf den Zielstapel zu bewegen. Schon bei nur 20 Scheiben sind 220 1 = 1048575 Schritte notwendig! Die Mnche wren also fr hundert Scheiben ihr Leben lang und darber o a u u hinaus beschftigt . . . a Doch was mag das nun mit Rekursion zu tun haben? Nun, wie man sich denken kann, machen die Mnche im Prinzip immer wieder dasselbe: Sie bewegen Scheiben von einem o Stapel zum anderen. Das einzige, was sich dabei ndert, ist die jeweilige Ausgangssituaa tion, d. h. es liegen jedesmal weniger Scheiben auf dem Startstapel und mehr auf dem Zielstapel. Sieht man sich nun einmal den Algorithmus nher an, sieht man, da die Funka tion sich selbst aufruft. Das mag auf den ersten Blick komisch aussehen, aber wenn man sicherstellt, da die Funktion sich irgendwann nicht mehr aufruft, sondern statt dessen beendet, wird das Prinzip dahinter klar: Rekursion bedeutet, da eine Funktion sich selbst eine bestimmte, zuvor meist nicht bekannte Anzahl mal selbst aufruft und nach etlichen Aufrufen irgendwann beendet. Damit ist dann zwar diese Funktion beendet, aber diese wurde ja von einer anderen Funktion aufgerufen, die nun an der Stelle nach dem Funktionsaufruf fortgesetzt wird. Das Besondere ist nun, da beide Funktionen eigentlich gleich sind. Eigentlich deshalb, weil sie sich doch unterscheiden, und zwar nur in den Werten ihrer lokalen, selbstdenierten Variablen und ggf. in der jeweiligen Stelle, wo die jeweilige Funktion fortgesetzt wird, wenn die von ihr aufgerufene Funktion sich beendet. Anstatt jeweilige Funktion sagt man ubrigens Rekursionsstufe, um zu verdeutlichen, da es sich um einen wiederholten Aufruf derselben Funktion mit bestimmten Eingabedaten handelt. Damit sind wir auch schon, ahnlich wie bei Schleifen, bei den beiden zentralen Dingen, die ein Programmierer sicherstellen mu, wenn er Rekursion benutzt: Durch die Anwendung der Rekursion mu sich die Situation derart ndern, da in endlicher Zeit (d. h. a irgendwann einmal, in der Praxis natrlich je frher desto besser) der Algorithmus fr u u u jede Aufgabenstellung terminiert, d. h. sinnvoll beendet wird man kann auch sagen, die Abbruchbedingung mu irgendwann erfllt werden. Das Beispiel der Trme von Hanoi33 u u zeigt eindrucksvoll, da rekursiv denierte Funktionen ein z. T. sehr hohes Wachstum auch schon fr niedrige Eingabewerte haben. u
Und mehr noch die Ackermann-Funktion, s. u. Last in rst out Dies ist Teil der sog. von Neumann-Architektur
Jens Hatlak
Seite 117
8. PHP Grundlagen
Rekursion
her wchst. a
Davon ausgenommen sind natrlich Klassenvariablen und Referenzen auf Objekte in der OOP u
Jens Hatlak
Seite 118
8. PHP Grundlagen
Rekursion
8. ack(2, 0) = ack(1, 1) 9. ack(1, 1) = ack(0, ack(1, 0)) 10. ack(1, 0) = ack(0, 1) 11. ack(0, 1) = 1 + 1 = 2 = ack(1, 0) 12. ack(0, 2) = 2 + 1 = 3 = ack(1, 1) = ack(2, 0) 13. ack(1, 3) = ack(0, ack(1, 2)) 14. ack(1, 2) = ack(0, ack(1, 1)) 15. . . .
Jens Hatlak
Seite 119
8. PHP Grundlagen
Rekursion
fr jedes Blatt den Part-Typ fest. Im Falle eines Attachments wird einfach dessen Struktur u analysiert und die relevanten Daten wie Name und Kodierung vermerkt. Handelt es sich jedoch um einen anzuzeigenden Text-Part, wird dieser an den String angehngt, der spter a a als Nachrichtentext ausgegeben wird. Zur Extraktion des Textes aus einem solchen Part mu wie gesagt der spezielle Identizierungsstring bekannt sein. Diesen String erhlt man a wie folgt: 1. Beim erstmaligen Aufruf der Funktion ist der ID-String im Array $data noch nicht gesetzt, d. h. leer. 2. Innerhalb einer Rekursionsstufe setzt sich der lokale ID-String aus dem ID-String im Array $data sowie der Nummer des aktuellen Parts zusammen, getrennt durch einen Punkt, auer, der ID-String im Array ist leer (erste Rekursionsstufe). Die gesuchte Part-Nummer ist dabei immer um eins grer als der Array-Zhler. o a 3. Vor dem Starten einer neuen Rekursionsstufe wird der lokale ID-String statt des ID-Strings, der sich im Array $data bendet, ubergeben. 4. Bei der Rckkehr aus jeder Rekursionsstufe ist der ID-String im Array $data unveru andert. Der folgend dargestellte Aufbau einer komplexen Mail sollte das verdeutlichen (beru setzte Version von http:// www.ietf.org/ rfc/ rfc2060.txt): HEADER TEXT 1 2 3 3.HEADER 3.TEXT 3.1 3.2 4 4.1 4.1.MIME 4.2 4.2.HEADER 4.2.TEXT 4.2.1 4.2.2 4.2.2.1 4.2.2.2 ([RFC-822] Header der Nachricht) MULTIPART/MIXED TEXT/PLAIN APPLICATION/OCTET-STREAM MESSAGE/RFC822 ([RFC-822] Header der Nachricht) ([RFC-822] Textkrper der Nachricht) o TEXT/PLAIN APPLICATION/OCTET-STREAM MULTIPART/MIXED IMAGE/GIF ([MIME-IMB] Header des IMAGE/GIF) MESSAGE/RFC822 ([RFC-822] Header der Nachricht) ([RFC-822] Textkrper der Nachricht) o TEXT/PLAIN MULTIPART/ALTERNATIVE TEXT/PLAIN TEXT/RICHTEXT
Solch komplexe Mails knnen z. B. dann entstehen, wenn man eine Nachricht, die bereits o Attachments enthlt, komplett eingebettet weiterleitet und dann noch eigene Attachments a anhngt. Auerdem werden auch HTML-Mails intern mit MIME-Parts realisiert, da sie a neben dem HTML-Teil meist auch einen reinen ASCII-Textteil enthalten.
Jens Hatlak
Seite 120
8. PHP Grundlagen
Rekursion
Das folgende Code-Beispiel zeigt den Rahmen einer entsprechenden Implementierung innerhalb einer geeigneten Klasse (siehe Kapitel 20), die bereits einen Mailbox-Stream und die Message-ID bereitstellt. Die Funktion imap_fetchbody extrahiert hierbei den Nachrichtenkrper, also den eigentlichen Text. Sie erwartet als Parameter einen geneo o ten Mailbox-Stream, eine Message-ID, eine Part-ID sowie (optional) ein Flag zum Benutzen von eindeutigen Message-IDs. Auf ihren Rckgabewert werden dann noch einu mal einige Funktionen angewendet, um Leerzeichen vorne und hinten sowie Escapezeichen zu entfernen sowie Sonderzeichen (Umlaute, . . . ) zu konvertieren. Die Funktion imap_fetchstructure schlielich liefert ein Objekt zurck, das Aufschlu uber die Struku tur der E-Mail gibt und u. a. alle MIME-Parts in verschachtelten Arrays enthlt. a function parseParts ( $ p a r t s , $ d a t a ) { // S c h l e i f e u b e r a l l e P a r t s d e r a k t u e l l e n Ebene for ( $ i =0; $ i < count ( $ p a r t s ); $ i ++) { // IDS t r i n g : < IDS t r i n g >.<Partnummer> $ i d s t r = sprintf ( %s%s%d , $ d a t a [ i d s t r ], ( $ d a t a [ i d s t r ]!= ? . : ), $ i +1); if ( is_array ( $ p a r t s [ $ i ]-> parts )) { // R e k u r s i o n , f a l l s a k t u e l l e r P a r t U n t e r p a r t s e n t h l t a $tmp = $ d a t a [ i d s t r ]; $ d a t a [ i d s t r ] = $ i d s t r ; $ d a t a = $ t h i s -> parseParts ( $ p a r t s [ $ i ]-> parts , $ d a t a ); $ d a t a [ i d s t r ] = $tmp ; } ... // Code f u r A t t a c h m e n tErkennung if (! $ i s a t t a c h m e n t ) { // k e i n A t t a c h m e n t > Body $msgbody = htmlspecialchars ( stripslashes ( trim ( imap_fetchbody ( $ t h i s -> mbox , $ t h i s -> msgid , $ i d s t r , FT_UID )))); ... // w e i t e r e B e h a n d l u n g } } return $ d a t a ; } $ s t r u c t u r e = im ap_fetchstructure ( $ t h i s -> mbox , $ t h i s -> msgid , FT_UID ); $ d a t a = $ t h i s -> parseParts ( $ s t r u c t u r e -> parts , $ d a t a ); ... // DatenA u s w e r t u n g In diesem Beispiel ist die Abbruchbedingung in Form der return-Anweisung noch recht einfach erkennbar. Wichtig ist dabei weniger, da es eine Anweisung zur Beendigung von Rekursionsstufen gibt, sondern vielmehr, da eine solche Anweisung im Verlauf der Rekursion immer in endlicher Zeit erreicht werden mu. Andernfalls gibt es schnell eine sich unendlich weiterverschachtelnde Rekursion: das Pendant zur Endlosschleife. Da bei einer
Jens Hatlak
Seite 121
8. PHP Grundlagen
Rekursion
Rekursion rechnerintern die Rcksprungadressen immer auf dem Stack gespeichert werden u (s. o.) und dieser irgendwann den normalen Speicher uberschreiben wrde, gibt es dann u mehr oder weniger schnell den gefrchteten stack overow, d. h. der Rechner riegelt ab, u bevor schlimmeres passiert. Da PHP nur eine Scriptsprache ist, wird aber schlimmstenfalls nur das Script unsanft beendet. Vermeiden sollte man sowas aber trotzdem auf jeden Fall!
Christoph Reeg
Seite 122
Im sog. URL-encoded Format, s. u. Gilt allgemein fr alle POST/GET-Ubergaben! u Das Gesagte gilt nur, wenn in der PHP-Konguration register_globals aktiviert wurde, was standardmig bis Version 4.1.X der Fall ist. Danach ist es standardmig ausgeschaltet (und das ist auch a a gut so).
Christoph Reeg
Seite 123
Werte ubergeben
< i n p u t t y p e = r e s e t v a l u e = Abbrechen > </ form > </ d i v > </ body > </ html > Quelltext der Datei ausgabe.php: <! DOCTYPE HTML PUBLIC //W3C//DTD HTML 3 . 2 / / EN> < html > < head > < title > Ausgabe </ title > </ head >< body > <? php printf ( F e l d 1 : % s<b r>F e l d 2 : % s , $ POST[ f e l d 1 ], $ POST[ f e l d 2 ]); ?> </ body > </ html > Natrlich kann die Seite zum Senden und die zum Auswerten auch genau dieselbe sein; u in diesem Fall mu sie logischerweise eine PHP-Endung haben. Diese Variante (Formular und Auswertung in einer Seite) hat sich in vielen Situationen als praktisch erwiesen und wird deshalb auch hug empfohlen. Siehe auch Kapitel 9.4. a
Christoph Reeg
Seite 124
Dynamische Formulare
Code + %21 %22 %23 %24 %25 %26 %27 %28 %29 * %2B %2C .
Code %2F %3A %3B %3C %3D %3E %3F @ %5B %5C %5D %5E %60
Zeichen { | } a o u A O U
Code %7B %7C %7D %7E %E4 %F6 %FC %C4 %D6 %DC %DF
Tabelle 9.1: urlencoded umgekehrt urldecode. Tabelle ,urlencoded listet die wichtigsten Sonderzeichen und ihre Kodierung auf. Wenn man die ubergebenen Werte verwendet, darf man nicht vergessen, da jeder4 die Werte beim Aufruf verndern kann. Deshalb sollten die Variablen vor der Weiterverarbeia tung auf korrekte Werte hin uberprft werden. u
Jens Hatlak
Seite 125
Dynamische Formulare
d) RADIO: Radiobutton. Mehrere Radiobuttons mit demselben Namen aber unterschiedlichem Wert werden zu einer Gruppe zusammengefat, aus der nur ein (oder am Anfang ggf. kein) Knopf gleichzeitig gedrckt sein kann. Der stanu dardmig gewhlte Knopf wird mit dem Attribut checked (bei HTML ohne a a Wert) deniert. e) CHECKBOX: Checkbox. Soll sie standardmig gesetzt sein, wird dies mit dem a Attribut checked (bei HTML ohne Wert) angegeben. Haben mehrere Checkboxen den gleichen Namen und endet dieser mit den Zeichen [], so werden sie bei der Umwandlung durch PHP in ein Array umgewandelt, das genau die Eintrge a (mit den jeweils entsprechenden Werten) enthlt, deren zugehrige Checkboxen a o beim Verschicken gesetzt waren. Auf diese Weise lassen sich besonders gut logisch zusammengehrende Mehrfachauswahlen realisieren. o f) HIDDEN: verstecktes Feld. Dient der Ubermittlung von Informationen, die weder vernderbar noch sichtbar sein sollen 5 . a 2. SELECT-Boxen erlauben die Auswahl eines Eintrags aus einer Dropdown-Liste. Ist das value-Attribut bei den option-Tags nicht vorhanden, so dienen die Eintrge a selbst als Werte. Das folgende Beispiel zeigt, wie die genannten Formularelemente eingesetzt werden kno nen und wie ihre Vorbelegung implementiert wird. Interessant ist dabei u.a. die Verwendung zweier neuer PHP-Funktionen: is_array() prft, ob eine Variable ein Array ist6 , u und mit in_array() kann man feststellen, ob ein Wert (Parameter 1) in einem Array (Parameter 2) enthalten ist.7 Anstelle der statischen Arrays in den beiden Funktionen fr die Ausgabe der Optionen u knnen, in Verbindung mit einer Datenbank-Anbindung, natrlich auch Arrays, die aus o u einer SQL-Abfrage resultieren, verwendet werden. Solche Arrays erstellt man i. A. mittels einer while-Schleife. functions.inc: <? php / B e r u fO p t i o n e n a u s g e b e n @param $ b e r u f B i s h e r i g e r Wert / function pri nt_beru f_options ( $ b e r u f =0) { $ b e r u f e = array ( A n g e s t e l l t e r , O e d i e , S t u d e n t / S c h u e l e r ); for ( $ i =0; $ i < count ( $ b e r u f e ); $ i ++) { printf ( <o p t i o n v a l u e =\ % d \ %s>%s </ o p t i o n >\n ,
5 6 7
Hindert natrlich keinen, sie trotzdem zu verndern u a Es gibt z. B. auch is_numeric() fr Zahlen u Als dritten, booleschen Parameter kann man optional noch angeben, ob auch auf Typgleichheit geprft u werden soll.
Jens Hatlak
Seite 126
Dynamische Formulare
( $ i +1), ( $ b e r u f ==( $ i +1) ? s e l e c t e d : ), htmlentities ( $ b e r u f e [ $ i ]) ); } } / HobbyO p t i o n e n a u s g e b e n @param $hobby Array b i s h e r i g e r Werte / function pri nt_hobb y_options ( $hobby ) { if (! is_array ( $hobby )) $hobby = array (); $ h o b b i e s = array ( L e s e n , R a d f a h r e n , Schwimmen ); for ( $ i =0; $ i < count ( $ h o b b i e s ); $ i ++) { printf ( <i n p u t t y p e =\ checkbox \ name=\ hobby []\ . v a l u e =\ % s \ %s > % s \ n , htmlentities ( $ h o b b i e s [ $ i ]), ( in_array ( $ h o b b i e s [ $ i ], $hobby ) ? c h e c k e d : ), $hobbies [ $i ] ); } } ?> daten.php: <? php include ( . / f u n c t i o n s . i n c ); ?> <! DOCTYPE HTML PUBLIC //W3C//DTD HTML 3 . 2 / / EN> < html > < head > < title > Pers & ouml ; nliche Daten </ title > </ head > < body > <? php // l o k a l e V a r i a b l e n s e t z e n $ v a r s = array ( s e n d e r , g e s c h l , vorname , name , b e r u f , hobby ); foreach ( $ v a r s as $ v a r ) $ $ v a r = $ POST[ $ v a r ]; if ( $ s e n d e r ) printf ( D i e Daten wurden von % s a u s v e r s c h i c k t . , htmlentities ( $ s e n d e r )); if (! isset ( $ g e s c h l )) $ g e s c h l = m ;
Jens Hatlak
Seite 127
Dynamische Formulare
?> < form action = d a t e n . php method = p o s t > < input type = h i d d e n name = s e n d e r value = d a t e n . php > Vorname : < input name = vorname size = 25 maxlength = 60 value = <?php p r i n t f ( % s , h t m l e n t i t i e s ( $vorname ) ) ; ?> >< br > Name : < input name = name size = 25 maxlength = 60 value = <?php p r i n t f ( % s , h t m l e n t i t i e s ( $name ) ) ; ?> >< br > Geschlecht : < input type = r a d i o name = g e s c h l value = m<? php printf ( %s , ( $ g e s c h l == m ? c h e c k e d : )); ?>> m & auml ; nnlich & nbsp ; < input type = r a d i o name = g e s c h l value = w<? php printf ( %s , ( $ g e s c h l == w ? c h e c k e d : )); ?>> weiblich < br > Beruf : < select name = b e r u f > < option value = 0 <? php echo (! isset ( $ b e r u f ) ? s e l e c t e d : ); ?>>--- Bitte w & auml ; hlen ---</ option > <? php prin t_beruf _options ( $ b e r u f ); ?> </ select >< br > Hobbies : <? php prin t_hobby _options ( $hobby ); ?>< br > < input type = s u b m i t value = W e i t e r > < input type = r e s e t value = Zurü ; c k s e t z e n > </ form > </ body > </ html > Der Eekt dieser ganzen Abfragen und Zusatzangaben ist letztlich nicht nur, da die Daten verschickt werden, sondern da auch das Formular nach dem Verschicken wieder genau so aussieht, wie zuvor. In diesem Beispiel werden die verschickten Daten, bis auf das Ausfllen des Formulars, nicht weiterverarbeitet. Anbieten wrde sich fr persnliche u u u o Daten z. B. das Anlegen oder Aktualisieren von Datenstzen einer entsprechenden Daa tenbank. Da im obigen Beispiel die Hobbies uber Checkboxen realisiert wurden, die eine Auswahl ermglichen, die aus mehreren Werten besteht, mte dieses Feld datenbanko u technisch wohl relational deniert werden, also uber eine Zusatztabelle, die die Beziehung zwischen persnlichen Daten und einer Hobbytabelle herstellt: o
Jens Hatlak
Seite 128
pers daten ID
hobbies ID
Sind die Daten nicht korrekt, kann eine Fehlermeldung generiert und das Formular ggf. mit den korrigierten Daten erneut angezeigt werden, anstatt weiterzuleiten $_POST bzw. $_GET, je nach Ubermittlungsart
Jens Hatlak
Seite 129
assoziative Arrays an10 . <? php // g g f . F u n k t i o n sI n c l u d e if ( $ s u b m i t t e d ) { // Daten p r u f e n , g g f . F e h l e r m e l d u n g e n e r z e u g e n if (< Daten OK >) { // e i g e n t l i c h e V e r a r b e i t u n g : // neuen D a t e n s a t z a n l e g e n // b e s t e h e n d e n D a t e n s a t z a k t u a l i s i e r e n // b e s t e h e n d e n D a t e n s a t z l s c h e n o // und g g f . w e i t e r l e i t e n } else { // F e h l e r m e l d u n g e r z e u g e n // ( i n V a r i a b l e s c h r e i b e n ) } } elseif ( $ f i l l f o r m ) { // a l t e Daten l e s e n und i n V a r i a b l e n s p e i c h e r n } // F r u h e s t m g l i c h e A u s g a b e : g g f . HTMLHeaderI n c l u d e ; o // F e h l e r a u s g e b e n ?> Hier das Formular ( mit Werten ) anzeigen <? php // g g f . HTMLF o o t e rI n c l u d e ?> Mit den Includes sind mgliche Einbindungen von Funktions- und Klassendenitionso dateien gemeint bzw. HTML-Ausgaben, die weitgehend unabhngig von der Datenverara beitung sind (HTML-Header und -Footer). Falls Daten gendert werden sollen (im Beispiel angedeutet durch Abfragen der booleschen a Variable $fillform), mu natrlich das Formular ausgefllt werden, d. h. entsprechende u u DB-Abfragen mssen gestartet und ausgewertet werden, so da danach die Variablen, die u die Formularfelder vorbelegen, sinnvolle Werte haben. Ein schnes Beispiel fr die Anwendung des hier gelesenen ndet sich ubrigens in Kapitel o u 16.
Namensvergabe im Formular: einheitlicher Name, direkt gefolgt von dem eigentlichen Variablenname in eckigen Klammern, z. B. daten[vorname]
Jens Hatlak
Seite 130
wenn die fragliche Variable auch tatschlich den Wert Null (nicht null) hat. Falsch gea dacht! Wer das aktuelle DSP bis zu dieser Stelle aufmerksam gelesen hat, wei, warum: In Kapitel 8.2.9.1 wurde gesagt, da jeder String bei normalem Vergleich gleich dem Integerwert Null ist. if ($ POST[ s t u e c k m e n g e ]==0) { // F o r m u l a r f u r B e s t e l l u n g a u s g e b e n } else { // B e s t e l l u n g aufnehmen } Da hier nicht mittels === auf Typgleichheit uberprft wurde, wird das Formular immer u ausgegeben werden (d. h. die Abfrage ist in dieser Form nutzlos) POST- und GET-Werte sind immer Strings. . .
Christoph Reeg
Seite 131
10.1 Syntax
10.1.1 allgemein
Zum Beschreiben der Syntax von PHP-Befehlen hat sich ein gewisser Standard entwickelt. Dieser hat groe Ahnlichkeit mit dem anderer Programmiersprachen. Was mu in der Syntax erklrt werden? Als erstes interessiert natrlich der Funktionsa u name. Da Funktionen immer was zurckgeben, interessiert auch der Rckgabetyp und last u u but not least mu man auch wissen, welche Parameter die Funktion erwartet (und welchen Typ sie haben mssen). Diese Informationen sind in der Syntax versteckt. Nehmen wir als u Beispiel die Funktion mysql_connect(). Laut Anleitung ist die Syntax wie folgt (wegen Platzproblemen in zwei Zeilen geschrieben, eigentlich gehrt alles hintereinander): o resource mysql_connect ( [string server [, string username [, string password ]]]) Schauen wir uns die Syntax mal von vorne an. Das Wort resource gibt den Rckgabetyp u der Funkion mysql_connect an. In den Klammern stehen dann die Parameter, wobei das string den erwarteten Typ angibt, d. h. alle drei Parameter mssen in diesem Fall vom u Typ string sein. Eckige Klammern denieren jeweils einen optionalen Teil, der nur bei Bedarf angegeben werden mu. Wenn der jeweilige Parameter nicht angegeben wird, wird ein Standardwert genommen. Die genaue Beschreibung der Parameter und welche Standardwerte diese haben, wenn sie optional sind, steht in der anschlieenden Beschreibung. Die Typangabe und die eckigen Klammern werden spter selbstverstndlich nicht mit a a eingegeben. Mit allen Parametern wrde der Aufruf des Befehls z. B. folgendermaen ausu sehen: $link = mysql_connect(localhost,MeinName,MeinPasswort);
Christoph Reeg
Seite 132
Syntax
Mit mysql connect() wird eine Verbindung zum Server genet. o Wenn der zurckgegebene Wert nicht ,FALSE ist, verlief die Verbindung erfolgreich. u Bei einem zweiten Aufruf mit denselben Parametern wird keine neue Verbindung erstellt, aber es wird der link identier zurckgegeben. Der link identier wird bentigt, um bei u o mehreren Verbindungen eine eindeutig bestimmen zu knnen. o Als hostname ist in unserem Fall immer localhost zu nehmen. Fr username und u password sind die von uns bzw. von dir selbst zugewiesenen Werte zu nehmen. Die Verbindung wird automatisch bei Beenden des Scripts geschlossen; sie kann aber auch mit mysql close() explizit geschlossen werden.
Christoph Reeg
Seite 133
Syntax
Christoph Reeg
Seite 134
while ( $row = mysql_fetch_row ( $ r e s u l t )) { echo $row [0]; echo $row [1]; } ?>
Jens Hatlak
Seite 135
Ubung
10.3 Ubung
10.3.1 Ergebnis-Tabelle ausgeben I
Als Vertiefung und Ubung zu dem bisher gesagten eine kleine Aufgabe: Es soll eine Funktion geschrieben werden, die das Ergebnis einer SQL-Abfrage tabellarisch darstellt. Die Ausgabe soll im Prinzip die Tabelle, die man beim MySQL-Prompt bekommt, in HTML umsetzen (inkl. Spaltennamen). Wenn man zum Beispiel am MySQL-Prompt alle Mitarbeiter abfragen will, kommt folgende Ausgabe zustande: mysql> select * from Mitarbeiter; +-----+------+-------+----------------+------------+--------------+ | MNr | VNr | AbtNr | Name | GebDat | Telefon | +-----+------+-------+----------------+------------+--------------+ | 1 | NULL | 3 | Christoph Reeg | 1979-05-13 | NULL | | 2 | 1 | 1 | junetz.de | 1998-03-05 | 069/764758 | | 3 | 1 | 1 | Uli | NULL | NULL | | 4 | 3 | 1 | JCP | NULL | 069/764758 | | 5 | 1 | 2 | Maier | NULL | 06196/671797 | | 6 | 5 | 2 | Meier | NULL | 069/97640232 | +-----+------+-------+----------------+------------+--------------+ 6 rows in set (0.00 sec) mysql>
Christoph Reeg
Seite 136
Ubung
Um das in einer HTML-Tabelle darzustellen, ist folgender (vereinfachter) HTML-Code notwendig: < html > < body > < table > < tr > < th > MNr </ th > < th > VNr </ th > < th > AbtNr </ th > < th > Name </ th > < th > GebDat </ th > < th > Telefon </ th > </ tr > < tr > < td > 1 </ td > < td > </ td > < td > 3 </ td > < td > Christoph Reeg </ td > < td > 1979-05-13 </ td > < td > </ td > </ tr > < tr > < td > 2 </ td > < td > 1 </ td > < td > 1 </ td > < td > junetz . de </ td > < td > 1998-03-05 </ td > < td > 069/764758 </ td > </ tr > < tr > < td > 3 </ td > < td > 1 </ td > < td > 1 </ td > < td > Uli </ td > < td > </ td > < td > </ td > </ tr > < tr > < td > 4 </ td > < td > 3 </ td > < td > 1 </ td > < td > JCP </ td > < td > </ td > < td > 069/764758 </ td > </ tr >
Christoph Reeg
Seite 137
Ubung
< tr > < td > 5 </ td > < td > 1 </ td > < td > 2 </ td > < td > Maier </ td > < td > </ td > < td > 06196/671797 </ td > </ tr > < tr > < td > 6 </ td > < td > 5 </ td > < td > 2 </ td > < td > Meier </ td > < td > </ td > < td > 069/97640232 </ td > </ tr > </ t a b l e > </ body > </ html > Als kleine Hilfe hier das Rahmenprogramm fr die Funktion: u <? php $db host $db user $db pass $datab = = = = l o c a l h o s t ; c r ; 123 ; c r ;
function print_result_table ( $ r e s u l t ){ // h i e r d a s r i c h t i g e s c h r e i b e n } // Hauptprogramm / V e r b i n d u n g z u r D a t e n b a n k a u f b a u e n / $db = @mysql_connect ( $ d b h o s t , $ d b u s e r , $ d b p a s s ) or die ( mysql_error ()); @mysql_select_db ( $ d a t a b , $db ) or die ( mysql_error ()); / HTMLS t a r t c o d e a u s g e b e n / echo <html>\n<body>\n ; / SQLA b f r a g e / $ r e s u l t = @mysql_query ( SELECT FROM M i t a r b e i t e r ); print_result_table ( $ r e s u l t );
Christoph Reeg
Seite 138
Ubung
Tips zur Lsung o Die Aufgabe in mehreren Schritten lsen: o 1. Nur in der 1. Zeile die 1. Spalte ausgeben. 2. Mit einer Schleife fr alle Zeilen die 1. Spalte ausgeben. u 3. Mit einer 2. Schleife alle Spalten ausgeben (vorher in der Dokumentation nachsehen, mit welcher Funktion man die Spaltenanzahl abfragen kann). 4. Als letztes noch die Spaltennamen ausgeben. Eine Lsung bendet sich in Anhang B.2. o
Fehlerbehandlung wurde hier absichtlich auen vor gelassen, um die vorhergehenden Aufgaben nicht zu beeintrchtigen a
Christoph Reeg
Seite 139
Ubung
WHERE GebDat LIKE %2\ $02d %3\02d% , $was , $monat , $ j a h r ) ); ... In diesem Programmabschnitt sind sechs Fehler versteckt. Wer ndet sie? Ausung in o Anhang B.4.
Christoph Reeg
Seite 140
11.1.1 Weiterleiten
Wie bereits oben erwhnt, kann man, neben JavaScript und HTML2 , auch mit PHP den a Client auf eine andere Seite weiterleiten. Dies geschieht mit folgender Anweisung: header(Location: absolute_URL); exit; absolute_URL mu natrlich durch die gewnschte URL ersetzt werden. Es mu nach RFC u u die absolute URL angegeben werden, auch wenn fast alle Browser eine relative verstehen! Das exit ist nicht unbedingt notwendig, allerdings wrde es nichts bringen, nach dem u header noch etwas auszugeben, da es sowieso nicht angezeigt wird.
1 2
Request for Comments hier allerdings nicht konditional, also bedingungsabhngig, sondern nur nach einer festen Anzahl a Sekunden
Christoph Reeg
Seite 141
Header
11.1.3 Authentizierung
Mit PHP besteht die Mglichkeit, den Browser ein Fenster nen zu lassen, in dem Name o o und Pawort eingetragen werden mssen. Wenn PHP nicht als Modul, sondern als CGI u luft, funktioniert das allerdings nicht3 . a Es ist eigentlich ganz einfach, eine solche Datei mu vom Prinzip her so aussehen: <? php if ($PHP AUTH USER!= C h r i s t o p h OR $PHP AUTH PW!= Reeg ) { Header ( HTTP / 1 . 1 4 0 1 U n a u t h o r i z e d ); Header ( W W u t h e n t i c a t e : B a s i c r e a l m =Top S e c r e t ); W A echo Mit Abbrechen kommst Du h i e r n i c h t r e i n . ; ) \ n ; exit ; } ?> <! DOCTYPE HTML PUBLIC //W3C//DTD HTML 3 . 2 / / EN> < html > < head > < title > Authentification </ title > </ head > < body > < h1 > Hier ist der Top - Secret Bereich </ h1 > < h2 ><? php echo Username : .$PHP AUTH USER. Pawort : .$PHP AUTH PW; ?></ h2 > </ body > </ html > Das Funktionsprinzip ist ganz einfach: Beim ersten Aufruf sind im Array SERVER die beiden Stellen PHP AUTH USER und PHP AUTH PW nicht gesetzt. Dadurch wird der Bereich in der IF-Abfrage bearbeitet. Hier werden die beiden Header zurckgegeben, u die den Browser veranlassen, nach Usernamen und Pawort zu fragen. Diese beiden Zei3
Ubrigens: PHP gibt es auch als Modul fr Windows, siehe auch PHP-FAQ[8] Wo nde ich PHP als u Modul fr Windows? u
Christoph Reeg
Seite 142
Header
len mssen fast genau so ubernommen werden, damit es funktioniert!4 Das einzige, was u gendert werden darf, ist das Top Secret. Der Text danach wird nur dann ausgegeben, a wenn jemand bei der Pawortabfrage auf ,Abbrechen klickt (oder, im Falle des Internet Explorers, drei Versuche, sich zu authentizieren, milungen sind); dann springt der Webserver nach dem echo aus der Datei und der Rest wird nicht mehr ausgegeben. Wenn jedoch jemand das richtige Pawort mit dem richtigen Usernamen eingegeben hat, wird der Bereich in der IF-Abfrage nicht bearbeitet und der Rest der Datei wird abgearbei tet. In unserem Fall wird die Uberschrift Hier ist der Top-Secret Bereich und die Zeile Username: Christoph Pawort: Reeg im HTML-Format ausgegeben. Es gibt noch ein kleines Sicherheitsproblem bei der ganzen Sache - der Browser behlt a sich nmlich den Usernamen und das Pawort, so da die Autoren derjenigen Seiten, die a man nach der Paworteingabe abruft, theoretisch das Pawort abfragen knnten. Dies o kann man jedoch ganz einfach verhindern, indem man den Browser komplett beendet. Auf fast dieselbe Weise kann man sich natrlich auch direkt fr den Zugri auf eine u u Datenbank authentizieren. Der folgende Quelltext zeigt, wie man dies erreicht: <? php if ($ SERVER[ PHP AUTH USER] == OR ! @mysql_connect ( l o c a l h o s t , $ SERVER[ PHP AUTH USER], $ SERVER[ PHP AUTH PW])) { Header ( HTTP / 1 . 0 4 0 1 U n a u t h o r i z e d ); Header ( W W u t h e n t i c a t e : B a s i c r e a l m =Top S e c r e t ); W A echo Mit Abbrechen kommst Du h i e r n i c h t r e i n . ; ) \ n ; exit ; } ?> <! DOCTYPE HTML PUBLIC //W3C//DTD HTML 3 . 2 / / EN> < html > < head > < title > Authentification </ title > </ head > < body > < h1 > Hier ist der Top - Secret Bereich </ h1 > < h2 ><? php echo Username : .$ SERVER[ PHP AUTH USER]; echo Pawort : .$ SERVER[ PHP AUTH PW]; ?></ h2 > </ body > </ html > Das @-Zeichen vor dem mysql_connect hat nichts mit der if-Abfrage zu tun. Es sorgt dafr, da keine Fehlermeldung beim Aufruf von mysql_connect ausgegeben wird. Die u Fehlermeldung wrde nicht nur stren, sondern sie wrde auch die Pawortabfrage zuveru o u
4
Beachtet man z. B. die Gro-/Kleinschreibung nicht, kann das dazu fhren, da die Authentizierung u nicht mehr mit dem Internet Explorer, wohl aber weiterhin mit dem Netscape Navigator funktioniert!
Christoph Reeg
Seite 143
Header
lssig verhindern. Vor dem header-Aufruf darf nichts ausgegeben werden. a Der Bereich in der obigen IF-Abfrage wird genau dann nicht bearbeitet, wenn mittels Benutzername und Pawort eine Verbindung zur Datenbank aufgebaut werden konnte. In jedem anderen Fall wird, wie im ersten Beispiel, abgebrochen und (in diesem Fall) der Text Mit Abbrechen. . . ausgegeben. Um sich Probleme zu ersparen, sollte man obige Bedingung der IF-Anweisung einfach 1:1 ubernehmen, denn diese ist bestens erprobt! :-) Noch eine Anmerkung zum Schlu: Anstatt der Zeichenkette HTTP/1.0 401 Unauthorized kann auch Status: 401 Unauthorized benutzt werden. Im Falle des o. g. PHPCGI-Problems scheint es dann so, als ob die Authentizierung funktionieren wrde (es u tritt kein Fehler 500 mehr auf); dies ist jedoch ein Trugschlu, denn trotz allem werden die beiden bentigten Authentizierungs-Variablen nicht mit den Werten gefllt, die der o u Browser nach der Eingabe durch den Benutzer im entsprechenden Dialog zurckliefert. u
11.1.4 Download
In bestimmten Fllen wre es schn, wenn ein PHP-Script die von ihm erzeugten Daten a a o nicht einfach in Form einer HTML-Seite ausgeben, sondern diese an den Client senden knnte. Dieser sollte dann die Daten z. B. in Form einer Datei abspeichern oder auf sonstige o Weise verarbeiten (an spezielle Applikationen ubergeben). Solche Situationen gibt es z. B. bei Anhngen (Attachments) in einem Webmail-System. a Normalerweise wird die Ausgabe eines PHP-Scripts als HTML interpretiert, welches der Browser anzeigen soll. Damit der Browser die Datei aber direkt auf die Platte speichert (bzw. dem Benutzer uberlt, was er damit machen will), mu die Angabe uber den a Typ des Dateiinhalts fr die Ubertragung gendert werden. Das geschieht mit folgender u a Anweisung (siehe auch weiter unten): header("Content-Type: application/octetstream"); Wenn nichts anderes angegeben wird, benutzt der Browser den Dateinamen des Scripts aus der URL als Dateinamen zum Abspeichern. header("Content-Disposition: attachment; filename=datei_name.ext"); Mit diesem Header wird der Dateiname auf datei name.ext gesetzt. Man beachte das Fehlen von Quoting-Zeichen wie etwa Hochkommata5 . Grund hierfr ist, da bestimmte u Browser wie der IE sonst die Quoting-Zeichen als Teil des Dateinamens ansehen. Natrlich u kann anstelle des hier beispielhaft eingetragenen jeder mgliche Dateiname stehen. Eveno tuelle Pfadangaben sollen explizit ignoriert werden. D. h. es ist mglich den Dateinamen o festzulegen, aber nicht in welches Verzeichnis die Datei gespeichert werden sollte. Microsoft liest die RFCs scheinbar anders als alle anderen (oder gar nicht?), so da der IE 5.56 nur folgenden Header versteht: header("Content-Disposition: filename=datei_name.ext"); Uber die Variable _SERVER["HTTP_USER_AGENT"] knnen wir PHP auch entscheiden o lassen, welche Variante wahrscheinlich die richtige ist.
5 6
Nach RFC mu der Dateiname gequotet werden Wie es sich mit IE 6.x verhlt, wissen wir erst, wenn die endgltige Version erschienen ist. Altere a u Versionen dagegen scheinen den Fehler nicht aufzuweisen.
Christoph Reeg
Seite 144
Header
header ( C o n t e n tD i s p o s i t i o n : . ( strpos ($ SERVER[ HTTP USER AGENT], MSIE 5 . 5 )? : a t t a c h m e n t ; ). f i l e n a m e=d a t e i n a m e . e x t ); Die Variante, den Dateinamen uber Header festzulegen, hat einen kleinen Nachteil: Wenn der Nutzer spter im Browser nicht auf den Link klickt, um dann die Datei zu speichern, a sondern direkt uber Save Link as speichern will, konnte noch kein Header gesendet werden, so da der Browser den Dateinamen nicht kennt und wieder den Dateinamen des Scripts vorschlgt. Das kann nur umgangen werden, indem man dafr sorgt, da der a u gewnschte Dateiname in der URL steht. Dies ist wiederum nur uber Funktionen des u Webservers mglich. Beim Apache sind das die Funktionen Rewrite und Redirect. o Die Erfahrung hat gezeigt, da ein Content-Transfer-Encoding Header die ganze Sache sicherer macht, auch wenn er laut RFC 2616 nicht benutzt wird. header("Content-Transfer-Encoding: binary"); Die groen Browser zeigen beim Download hug einen Fortschrittsbalken an. Dies a funktioniert allerdings nur dann, wenn der Browser wei, wie gro die Datei ist. Die Gre o der Datei in Bytes wird uber den Content-Length Header angegeben. header("Content-Length: {Dateigre}"); o Zusammenfassend knnen wir nun folgenden Header benutzen, wenn die Ausgabe eines o Scripts heruntergeladen werden soll: // D a t e i t y p , d e r immer a b g e s p e i c h e r t w i r d header ( C o n t e n tType : a p p l i c a t i o n / o c t e t s t r e a m ); // Dateiname // m i t S o n d e r b e h a n d l u n g d e s IE 5 . 5 header ( C o n t e n tD i s p o s i t i o n : . (! strpos ($HTTP USER AGENT, MSIE 5 . 5 )? a t t a c h m e n t ; : ). f i l e n a m e=d a t e i name . e x t ); // e i g e n t l i c h u e b e r f l u e s s i g , h a t s i c h a b e r w o h l b e w a e h r t header ( C o n t e n tT r a n s f e rE n c o d i n g : b i n a r y ); // Z w i s c h e n s p e i c h e r n a u f P r o x i e s v e r h i n d e r n // ( s i e h e w e i t e r u n t e n ) header ( CacheC o n t r o l : p o s tc h e c k = 0 , p r ec h e c k =0 ); // D a t e i g r e f u r D o w n l o a d z e i tB e r e c h n u n g o header ( C o n t e n tL e n g t h : { D a t e i g r o e s s e } ); Diese Headerkombination sollte zuverlssig funktionieren. Bei der Vielzahl von Browsern, a die sich nicht immer an die RFCs halten, ist jedoch nicht ausgeschlossen, da das ganze angepat werden mu. Sollte jemand eine Kombination haben, die besser funktioniert, freue ich mich natrlich uber eine Rckmeldung. u u Ein letztes Wort noch zur Header-Kombination: Wie sich zeigte, funktioniert diese Download-Methode nicht mehr, wenn vor dem Senden o. g. Header schon bestimmte andere Header, wie die fr das Nicht-Cachen (11.1.6), gesandt wurden. Man sollte also immer u
Christoph Reeg
Seite 145
Header
auf die Reihenfolge der Header achten und sicherstellen, da vor den Headern fr den u Download keine oder nur denitiv nicht strende Header verschickt werden. o
11.1.5 Content-Type
Neben dem oben schon verwendeten Content-Type gibt es natrlich noch andere. So lange u nur normale Webseiten ausgegeben werden, interessiert uns der Typ nicht. Wenn aber z. B. mit den PHP image functions ein Bild dynamisch erzeugt wird, was auch als Bild im Browser angezeigt wird, mu der Typ explizit angegeben werden. Die Tabelle 11.1 zeigt die wichtigsten Typen. Bei statischen Dateien entscheidet der Webserver in der Regel anhand der Endung, welchen Typ er sendet. Normalerweise beachtet der Client den Typ, den der Server sendet. Es gibt jedoch IE Versionen, die der Meinung sind, anhand der Endung selbst besser entscheiden zu knnen, um was fr eine Datei es sich handelt. o u Bezeichnung application/octetstream image/gif image/jpeg image/png text/html text/plain Bedeutung wird als Datei (Binrdaten) angesehen (wird gespeichert) a GIF-Bild JPEG-Bild PNG-Bild wird als Webseite angesehen, HTML wird interpretiert wird als reiner Text angesehen, HTML wird nicht beachtet Tabelle 11.1: Content-Type Typen
Die Bedeutung des Type knnen wir uns an den folgenden Dateien ansehen. o <? php ?> < html > < body > < h1 > Hallo Welt !</ h1 > </ body > </ html > Die Datei ist im Endeekt eine ganz normale Webseite, enthlt zwar einen Bereich fr PHP a u Anweisungen, der ist jedoch leer. Sie wird auch dem entsprechend im Browser angezeigt. <? php header ( C o n t e n tType : t e x t / p l a i n ); ?> < html > < body > < h1 > Hallo Welt !</ h1 > </ body > </ html >
Christoph Reeg
Seite 146
Header
Nachdem das Script nun von sich behauptet, es wre ein normaler Text, werden die HTMLa Anweisung komplett miachtet und der Text so wie er ist ausgegeben. <? php header ( C o n t e n tType : t e x t / html ); ?> < html > < body > < h1 > Hallo Welt !</ h1 > </ body > </ html > Mit dem richtigen Typ ist die Welt aber wieder in Ordnung. <? php header ( C o n t e n tType : image / png ); ?> < html > < body > < h1 > Hallo Welt !</ h1 > </ body > </ html > Als Bild taugt der HTML-Code nun wirklich nicht und Netscape zeigt das Symbol fr ein u defektes Bild an. <? php header ( C o n t e n tType : a p p l i c a t i o n / o c t e t s t r e a m ); ?> < html > < body > < h1 > Hallo Welt !</ h1 > </ body > </ html > Und den Octetstream will Netscape ordnungsgem als Datei abspeichern. a
11.1.6 Cache
Uber die richtigen Headerangaben kann auch das Verhalten der Proxies beeinut werden. So verhindert folgende Kombination zuverlssig das Zwischenspeichern. a header ( E x p i r e s : 1 ); header ( CacheC o n t r o l : p o s tc h e c k = 0 , p r ec h e c k =0 ); header ( Pragma : noc a c h e ); header ( L a s tM o d i f i e d : . gmdate ( D , d M Y H : i : s ) . GMT); Zustzlich braucht der IE ab Version 5 oenbar noch folgenden HTML-Code am Ende a der Datei, d. h. zwischen </body> und </html>
Christoph Reeg
Seite 147
Header
< head > < meta http - equiv = pragma content = noc a c h e > </ head > Das Nicht-Cachen sollte aber nur benutzt werden, wenn es wirklich notwendig ist, d. h. die Seite sich bei jedem Aufruf ndert. Es ist sonst nmlich zum einen fr den Besucher a a u nervig, da die Seite bei jedem Aufruf neu geladen werden mu - normalerweise kommt sie ja nach dem ersten Aufruf fr eine gewisse Zeit aus dem Cache. Zum anderen belastet das u Neuladen den Server unntig. Davon abgesehen ignorieren viele Suchmaschinen Seiten, die o nicht gecached werden drfen, denn warum soll eine Seite aufgenommen werden, wenn die u Seite von sich behauptet, da sie sich stndig ndert? Von daher sollte die Seite, wenn sie a a in Suchmaschinen auftauchen soll, einen gescheiten Last-Modified und Expires Header senden. Was tun bei einer Seite, die sich jede Minute ndert? Ganz einfach: Die entsprechenden a Header richtig setzen. Die Tabelle 11.2 zeigt die entsprechenden Header. Es mu unterschieden werden zwischen den META-Tags, die im HTML stehen und den HTTP-Headern. Erstere werden nur vom Browser gelesen, letztere auch von Proxies. Bezeichnung Cache-Control Expires Last-Modied Bedeutung Anweisungen fr Proxies und Browser u Zeitpunkt, ab dem der Inhalt wahrscheinlich veraltet sein wird letzte Anderung des Inhalts Tabelle 11.2: Cache Header
Cache-Control Das RFC 2616 sagt zum Thema Syntax des Cache-Control (hier nur ein Ausschnitt): Cache-Control = "Cache-Control" ":" 1#cache-directive
cache-response-directive = | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1 | "no-store" ; Section 14.9.2 Bei einem Cache-Control: no-cache Header mu der Proxy bei jeder Anfrage uberpru fen, ob eine aktuellere Version vorliegt. Wenn dies nicht der Fall ist, kann er die gespeicherte Seite senden. Bei Cache-Control: no-store darf der Proxy die Seite nicht speichern, das heit, sie mu bei jedem Aufruf neu ubertragen werden.
Christoph Reeg
Seite 148
URLs parsen
Expires Beim Expire-Header mu die Uhrzeit richtig formatiert sein und in Greenwich Mean Time7 . Kleines Beispiel: Expires: Thu, 01 Dec 1994 16:00:00 GMT Diese Seite wre bis zum 1. Dezember 1994 0 Uhr aktuell gewesen. Danach mu sie wieder a auf Aktualitt uberprft werden. a u Last-Modied Bei Last-Modified sollte die Zeit der letzten Anderung angegeben werden. Bei statischen Seiten wird dafr hug das Anderungsdatum der Datei genommen. Bei dynamischen u a Seiten gestaltet sich die Sache etwas schwieriger. Alternative: GET-Parameter Eine Alternative zum Header ist, einen zuflligen GET-Parameter an die URL zu a hngen. Zum Beispiel wird dann aus der URL http://reeg.net/index.html die Adresse a http://reeg.net/index.html?IRGENDETWAS. Es mu natrlich gewhrleistet sein, da IRu a GENDETWAS immer etwas anderes ist.
Jens Hatlak
Seite 149
URLs parsen
ErrorDocument 404 /pfad/zur/alternativseite.php4 Auf diese Weise wird an Stelle der normalen Fehlerseite die Alternativseite aufgerufen, wobei der Benutzer davon nichts mitbekommt. In dieser Seite kann man dann in dem Fall, da mit Status-Code 404 umgeleitet wurde, auf den gesuchten letzten Teil der URL wie folgt zugreifen: <? php if ($ SERVER[ REDIRECT STATUS]==404) { $keyword = substr ($ SERVER[ REDIRECT URL], strrpos ($ SERVER[ REDIRECT URL], / )+1); } ?> Mit etwas Erfahrung sieht man dem Script direkt an, da es einfach alles abschneidet, was hinter dem letzten Slash / kommt, und es in die Variable $keyword schreibt. Letztere kann man nach Belieben im weiteren Scriptverlauf auswerten (und natrlich auch ganz u anders nennen!). Im Falle des PHP-Manuals wird aus dieser Information eine neue URL zusammengestellt, was recht leicht zu bewerkstelligen ist, da die meisten Dateinamen der Seiten des Manuals die Form function.FUNKTIONSNAME.html haben - lediglich Underscores (_) werden durch das Minuszeichen ersetzt. Mit der neuen URL wird dann eine einfache Header-Weiterleitung (siehe 11.1.1) durchgefhrt. u
Jens Hatlak
Seite 150
12 Regulre Ausdrucke a
Was sind denn uberhaupt regulre Ausdrcke1 ? Es gibt Leute, die nden regulre Ausa u a drcke fast so genial wie die Erndung des geschnittenen Brotes: Im Prinzip sind regulre u a Ausdrcke Suchmuster, die sich auf Strings (Zeichenketten) anwenden lassen. Auf den u ersten Blick sehen sie etwas kryptisch aus, was sie durchaus auch sein knnen. Was sie o aber so sinnvoll macht ist ihre Fhigkeit, komplexe Suchen durchzufhren - mehr dazu im a u folgenden. Anfngern empfehle ich, dieses Kapitel erstmal zu uberspringen. Alles, was man mit a regulren Ausdrcken realisieren kann, kann auch mit normalen Befehlen programmiert a u werden, ist dann aber natrlich etwas lnger. Mit regulren Ausdrcken ist es einfach u a a u eleganter. In Unix-Programmen, vor allem in Perl2 , werden die regulren Ausdrcke sehr gerne a u benutzt, wenn normale Suchmuster nicht mehr ausreichen. Auch wenn einfache Suchmuster noch relativ einfach zu erstellen sind, kann das beliebig kompliziert werden. Nicht umsonst gibt es dicke Bcher, die sich nur mit diesem Thema beschftigen. u a Wann sollen denn nun die regulren Ausdrcke eingesetzt werden? Die Antwort ist a u ganz einfach: Wenn man nach Mustern suchen will, bei denen die normalen Funktionen nicht mehr ausreichen. Wenn hingegen die normalen String-Funktionen, wie z. B. str_replace() oder strpos(), ausreichen, sollten diese auch benutzt werden, weil sie schneller abgearbeitet werden. PHP kennt zwei verschiedene Funktionsgruppen von regulren Ausdrcken, die ereg*a u und die preg*-Funktionen. Erstere sind von Anfang an dabei, halten sich an die POSIXDenition, sind aber langsamer und nicht so leistungsfhig wie die letzteren, die sich an Perl a orientieren. Von daher sollten, wenn mglich, die preg*-Funktionen verwendet werden. In o Konsequenz werde auch ich hier die Perl-kompatiblen Funktionen verwenden; diese werden auch hug als PCRE3 -Funktionen bezeichnet. a Mit regulren Ausdrcken ist es wie mit Programmen (und Dokus ;-)): Es ist eine Kunst, a u gut, verstndlich, fehlerfrei und ezient zu schreiben, und mit der Zeit perfektioniert man a sein Knnen. Von daher: Nicht verzweifeln, wenn es am Anfang nicht funktioniert oder o man Minuten braucht, bis dieser 20 Zeichen lange Ausdruck steht - uben, probieren und nochmals uben. Irgendwann geht es.
Engl.: regular expressions verbreitete Scriptsprache unter Unix Perl Compatible Regular Expressions
Christoph Reeg
Seite 151
einfache Suchmuster
im Suchmuster verwendet wird, bzw. immer mit einem Backslash \ escaped wird. Z. B. sucht das Muster /<title>/ nach dem HTML-Tag <title>. Wenn man jetzt aber Header-Tags (<h1>,<h2>,...,<h6>) nden will, funktioniert das so nicht mehr. Hier braucht man einen Platzhalter. Das, was auf der Kommandozeile der Stern * und in SQL bei LIKE das Prozentzeichen % ist, ist hier der Punkt .. Das Muster /<h.>/ wrde auf u alle HTML-Tags zutreen, bei denen auf das h ein beliebiges Zeichen folgt. Wir wollten aber nur die Zahlen 1-6. Mit \d steht ein Platzhalter fr eine beliebige Zahl zur Verfu u gung. /<h\d>/ trit nur noch die HTML-Tags <h0>,<h1>,<h2>,...,<h9>. Das ist zwar schon besser, aber auch noch nicht perfekt. Man bruchte genau die Menge der Zahlen a 1,2,3,4,5,6. Dies ist selbstverstndlich auch mglich: Der Ausdruck /<h[123456]>/ bewirkt a o das Gewnschte, er lt sich aber noch verschnern zu /<h[1-6]>/. u a o Wie wir gesehen haben, kann mit Hilfe der eckigen Klammern [] eine Menge von Zeichen deniert werden, indem man entweder alle aufzhlt oder den Bereich angibt. Wenn man a das Dach ^ als erstes Zeichen angibt, wird die Auswahl negiert. . [] z. B. [1-6] [^] z. B. [^1-6] \d \D \s \S \w \W ein beliebiges Zeichen (bis auf Newline \n) die angegebenen Zeichen bzw. Bereiche die Zahlen 1, 2, 3, 4, 5 und 6 als erstes Zeichen wird die Auswahl negiert alle Zeichen bis auf die Zahlen 1-6 Dezimalzier alle Zeichen bis auf die Dezimalziern Whitespace (Leerzeichen, Tabulator etc.) alle Zeichen auer Whitespace alle Wort-Zeichen (Buchstaben, Ziern, Unterstrich) alle Nicht-Wort-Zeichen Tabelle 12.1: Zeichenmengen \n \r \\ \t Zeilenumbruch (LF) Wagenrcklauf (CR) u Ein \ Tabulator Tabelle 12.2: Sonderzeichen bei den PCRE Das Dach ^ hat auch im Muster eine besondere Bedeutung: es markiert den Stringanfang. Der Ausdruck /^Hallo/ pat auf alle Strings, bei denen das Wort ,Hallo am Anfang steht, nicht jedoch in der Mitte. Neben dem Anfang ist natrlich auch das Ende interesu sant, das mit einem Dollar $ markiert wird. Das Suchmuster /Welt!$/ pat demnach auf alle Strings, die mit dem Wort Welt! enden. Das lt sich natrlich auch kombinieren: a u Mit /^Hallo Welt!$/ ndet man genau die Strings Hallo Welt! . Zur Vertiefung und Ubung habe ich versucht, ein paar Beispiele zu ernden. Diese benden sich bei den Ubungen am Ende des Kapitels.
Christoph Reeg
Seite 152
Quantizierer
12.2 Quantizierer
Mit dem bisher Gesagten kann man zwar schon schne Ausdrcke schreiben, aber irgendwie o u fehlt noch etwas. Wie die letzte Ubung gezeigt hat, ist es doch relativ umstndlich, mehrere a aufeinander folgende Zeichen anzugeben. Selbstverstndlich ist auch das mglich: hier hilft a o der Quantizierer (auch Quantor genannt). Man unterscheidet die folgenden Typen: Da wre als erstes der altbekannte Stern *, der fr keinmal oder beliebig oft steht. a u Das Suchmuster /^\d*$/ wrde z. B. alle Strings, die aus keiner oder beliebig vielen Ziern u bestehen, nden. Wie man an dem Beispiel sieht, mu der Quantizierer immer hinter dem Zeichen bzw. der Zeichenmenge stehen. Neben dem Stern *, Allquantor genannt, gibt es noch das Fragezeichen ?, das fr keinmal oder einmal steht und Existenzquantor genannt u wird, sowie das Pluszeichen + (mind. einmal). ? + * {n} {min,} {,max} {min,max} kein-/einmal mind. einmal keinmal oder beliebig oft genau n-mal mind. min-mal keinmal bis hchsten max -mal o mind. min, aber hchstens max -mal o Tabelle 12.3: Quantizierer Wenn man aber genau fnf Zahlen oder zwischen drei und sieben kleine Buchstaben u haben will, reichen die bisherigen Quantizierer dann doch noch nicht. Deshalb gibt es noch die geschweiften Klammern {}, in die man die gewnschten Zahlen eintragen kann. u Das Suchmuster fr genau fnf Zahlen sieht z. B. so aus: /\d{5}/; das fr die drei bis u u u sieben kleinen Buchstaben so: /[a-z]{3,7}/. Auch hier gibt es zur Vertiefung Ubungen am Ende des Kapitels.
12.3 Gruppierungen
Die runden Klammern ( ) haben auch eine besondere Bedeutung: Der Hauptzweck ist, den eingeklammerten Bereich zur spteren Verwendung zu markieren. Die Klammern kommen a auch beim wiederholten Vorkommen eines Teilaudrucks hintereinander zum Einsatz. Z. B. wrde ein Ausdruck, der auf drei zweistellige Zahlen, die mit einem Doppelpunkt anfangen, u pat, ohne Klammern so aussehen /:\d{2}:\d{2}:\d{2}/ . Mit Klammer wird das ganze ubersichtlicher: /(:\d{2}){3}/ . Die markierten Teilbereiche eines Ausdruckes werden auch Backreferences genannt. Sie werden von links nach rechts durchnumeriert. Innerhalb eines Ausdruckes kann man sich mit \1 auf den Text, der von der ersten Klammer eingeschlossen wird, beziehen, mit \2 auf den zweiten usw. Der Ausdruck /(\w)\s\1/ pat immer dann, wenn zweimal dasselbe Wort nur durch ein Leerzeichen getrennt vorkommt. Zur allgemeinen Verwirrung gibt es auch hier am Ende des Kapitels Ubungen.
Christoph Reeg
Seite 153
Optionen
12.4 Optionen
Die ganzen Ausdrcke knnen sich je nach angegebener Option auch noch anders verhalten. u o Die Optionen werden hinter dem letzten Delimiter angegeben. So bewirkt z. B. ein i, da nicht auf Gro-/Kleinschreibung geachtet wird. Der Ausdruck /hallo/i pat auf Hallo, hallo, HALLO oder jede andere Kombination von Gro-/Kleinbuchstaben. Eine Ubersicht zu den Optionen gibt die Tabelle 12.4. i m Es wird nicht auf Gro-/Kleinschreibung bei Buchstaben geachtet. Normalerweise betrachtet PHP bei den PCRE, wie auch Perl, den String als eine Zeile, das heit das Dach ^ pat nur auf den Anfang des Strings und das Dollar $ auf das Ende des Strings. Wenn diese Option gesetzt ist, pat das ^ auf jeden Zeilenanfang, wie auch auf den String-Anfang. Beim $ gilt das Ganze analog fr das Ende. u Wenn es keine Zeilenumbrche in dem Text gibt oder im Ausdruck kein ^ bzw. $ u verwendet wird, hat diese Option logischerweise keine Funktion. Ohne diese Option pat der Punkt . auf alle Zeichen, auer den Zeilenumbruch \n. Mit gesetzter Option pat der Punkt auf alle Zeichen, inklusive Zeilenumbruch. In negierten Mengen, wie z. B. [^a], ist der Zeilenumbruch immer enthalten, unabhngig von der Option. a Mit dieser Option wird der zu ersetzende Text bei preg_replace() als PHP-Code aufgefat und entsprechend ausgewertet. Bei Setzen dieser Option, wird der Ausdruck auf den Anfang des Strings angewandt. Dieses Verhalten kann auch durch entsprechende Konstrukte im Ausdruck erreicht werden. Normalerweise sind die Quantizierer greedy (gierig), das heit, sie versuchen immer den grtmglichen Text zu treen. Mit Hilfe dieser Option wird auf ungreedy o o umgeschaltet, das heit, es wird immer der krzestmgliche Text genommen. u o Tabelle 12.4: Optionen fr regulre Ausdrcke u a u
e A
12.5 Ubungen
12.5.1 einfache Suchmuster
Passen die folgenden Suchmuster auf die beiden Texte oder nicht? Text1= Hallo Welt!, Text2= PHP ist einfach genial und die Regex sind noch genialer. Die Lsung bendet o sich in Anhang B.6.1. 12.5.1.1 /hallo/ 12.5.1.2 /[^0-9A-Z]$/
Christoph Reeg
Seite 154
Ubungen
12.5.2 Quantizierer
Hier gilt dieselbe Aufgabenstellung wie in der vorigen Aufgabe 12.5.2.1 /^\w* \w*$/ 12.5.2.2 /^\w* \w*!$/ 12.5.2.3 /^\w* [\w!]*$/ 12.5.2.4 /\w{4} \w+/ 12.5.2.5 /\w{4} \w+$/ 12.5.2.6 Diesmal darfst du selbst nachdenken: Schreibe einen Ausdruck, der uberprft, ob u eine Log-Zeile dem Standard-Log Format des Apache-Servers entspricht. Eine Zeile kann z. B. so aussehen: 192.168.1.1 - - [01/Apr/2001:08:33:48 +0200] "GET /test.php4 HTTP/1.0" 200 3286 Die Bedeutung der Felder ist hier nicht wichtig, kann aber trotzdem interessant sein: Als erstes steht die IP-Adresse bzw. der Rechnername, von dem die Anfrage kam. Die nchsten beiden Felder sind der Username (vom identd bzw. a von auth), beide Felder mssen aber nicht existieren. In den eckigen Klammern steht das u genaue Datum mit Zeit und Zeitzone. In den Anfhrungszeichen steht die angeforderte u Datei mit der Methode (kann auch POST sein) und dem Protokoll (kann auch HTTP/1.1 sein). Als vorletztes wird der Status angegeben (200 ist OK) und das letzte Feld sagt, wie viel Daten bei diesem Aufruf ubertragen wurden.
Christoph Reeg
Seite 155
Ubungen
12.5.3 Gruppierungen
12.5.3.1 Schreibe einen Ausdruck, der uberprft, ob bei einer Preisangabe der DM-Betrag gleich u dem Pfennig-Betrag ist. Die Preisangabe sieht vom Format her so aus: 99,99DM.
Christoph Reeg
Seite 156
13 Fehlersuche
Fehlschlge sind die Wrze, die dem a u Erfolg sein Aroma geben.
Auch wenn man sehr gut programmieren kann und sowohl PHP als auch SQL im Schlaf beherrscht, kommt es vor, da es nicht so tut wie man will. In solchen Fllen ist a es praktisch, wenn man wei, wie man auf Fehlersuche gehen kann. Als erstes sollte man sich natrlich uberlegen, in welcher Reihenfolge das Programm u was wie tun sollte und dann uberprfen, bis zu welchem Punkt es funktioniert. Um das u festzustellen, gibt es verschiedene Methoden; am sinnvollsten ist es i. d. R., zu uberprfen, u ob die Variablen die Werte haben, die sie haben sollten. Dazu kann man sie z. B. einfach mit echo ausgeben. Bei Arrays kann die Funktion var_dump($var) sehr praktisch sein. var_dump gibt nmlich das gesamte Array mit Index und Werten aus. Da es aber in HTML a vorkommen kann, da Text lediglich im Quelltext der Seite sichtbar wird (innerhalb von Tabellenkonstrukten zum Beispiel), ist es sinnvoll, einfach noch einen festen String mit auszugeben; auf diese Weise wei man nachher auch noch, welche Variable es genau war. Als Beispiel haben wir die Variablen VAR1 und VAR2: echo $VAR1. AAA ; echo $VAR2. BBB ; Fr reine Testzwecke hat es sich auch als hilfreich erwiesen, um die Testausgabe der u Variablen herum einen HTML-PRE-Block zu setzen. Das bewirkt dann mit Sicherheit eine Darstellung der Ausgabe auch auf dem Bildschirm: echo <p r e>; echo $VAR1. .$VAR2; echo </ p r e>; Die dritte Mglichkeit schlielich ndet dann Anwendung, wenn man mehrere Variablen, o z. B. von POST/GET-Daten oder Servervariablen, im Zusammenhang sehen will und vielleicht die Namen einiger dieser Variablen gar nicht kennt. In diesem Fall kann man auf die PHP-interne Funktion phpinfo() zurckgreifen, die eine komplette HTML-Seite voll mit u datenberfllten Tabellen ausgibt. Hierber lassen sich dem System auch intime Details u u u uber die Serverkonguration, den User-Agent (Browser) uvm. entlocken. Bei IF-Abfragen z. B. kann es auch passieren, da Anweisungen gar nicht erst ausgefhrt u werden. Um das zu uberprfen, setzt man einfach ein echo in die Abfrage hinein. Zum u Beispiel: $ i =1; echo $ i . CCC ; if ( i ==1){
Christoph Reeg
Seite 157
13. Fehlersuche
echo DDD ; .... } Bei dieser Abfrage wrde man auf den ersten Blick davon ausgehen, da 1CCCDDD u ausgegeben wird, da $i, durch das erste echo bewiesen, den Wert 1 hat. Da man nun aber wei, da etwas mit der IF-Abfrage nicht stimmt, kann man auch einen zweiten Blick riskieren und sieht dann, da beim i das $ fehlt. Ein anderes Problem tritt hug auch im Zusammenhang mit Strings auf - warum wird a wohl bei folgendem String nicht das gewnschte ausgegeben: u $text = Alternativtext ; echo <img s r c =\ bild . gif \ w i d t h =\ 10\ h e i g h t =\ 10\ ; echo a l t =\ . $ t e x t .\ >; Oder warum bekommen wir hier eine Fehlermeldung: $text = Alternativtext ; echo <img s r c =\ bild . gif \ w i d t h =\ 10\ h e i g h t=10 ; echo a l t =\ . $ t e x t . \ >; Ganz einfach: Das Escapen stimmt nicht. Im oberen Beispiel sind hinter alt= zwar die doppelten Anfhrungsstriche escaped, aber da direkt im Anschlu daran mit Hilfe des u Stringoperators Strings verbunden werden, mten an dieser Stelle noch einmal doppelte u Anfhrungsstriche stehen, genauso wie bei der zweiten Stringverbindung. Im unteren Beiu spiel wurden zwar die Strings korrekt beendet und wieder angefangen, dafr wurden die u Anfhrungszeichen um die 10 nicht escaped. Komplett richtig mu es so aussehen: u $text = Alternativtext ; echo <img s r c =\ bild . gif \ w i d t h =\ 10\ h e i g h t =\ 10\ ; echo a l t =\ . $ t e x t . \ >; Solcherlei Vergelichkeiten sind nicht selten Grund genug fr eine langwierige Fehlersuche. u Man kann dies aber von vorne herein vermeiden, indem man gleich gut nachdenkt und dabei alle doppelten Anfhrungszeichen, die wirklich ausgegeben werden sollen, escaped. u :-) Hug funktioniert zwar der komplette PHP-Teil, die SQL-Anweisungen jedoch nicht. a In diesen Fllen gebe ich selbst einfach den Abfrage-String mit echo aus.1 Dann sieht a man nmlich schon hug den Fehler ein huger ist ubrigens, da ein Feld einen String a a a erwartet (z. B. die SQL-Typen TEXT und VARCHAR), in der Abfrage jedoch die fr Strings u charakteristischen Hochkommata vergessen wurden. Wenn man ihn jedoch nicht ndet, benutzt man einfach den String und gibt ihn direkt am SQL-Prompt2 ein. Wenn es dann immer noch nicht funktioniert, krzt man die Abfrage solange, bis sie funktioniert. Die u andere Richtung funktioniert natrlich auch, d. h. man konstruiert sich die Abfrage Stck u u fr Stck zusammen und probiert, wie lange sie noch funktioniert und hat dann hoentlich u u den Fehler.
1
Hat man die Tips fr guten Stil aus Kapitel 8.8.2 angewandt und sprintf() benutzt, kann man hier u einfach testweise das ,s entfernen. :-) Oder z. B. in PHPMyAdmin, siehe Kapitel 7.3
Christoph Reeg
Seite 158
13. Fehlersuche
Ubungen
Damit wren wir auch schon beim nchsten Punkt der Fehlersuche angelangt: Wenn a a etwas nicht laufen will, ersetzt man die Variablen durch Konstanten und probiert. Auf diese Weise ist nmlich eine Fehlerquelle (falsche Werte bei den Variablen) ausgeschlossen. a Bei groen PHP-Dateien ist es teilweise sinnvoll, den nicht funktionierenden Teil in eine Datei zu kopieren, denn nicht selten werden Anweisungen durch andere beeinut, die eigentlich gar nichts miteinander zu tun haben (sollten). Wenn es immer noch nicht funktioniert, hilft es vielleicht, wenn ein Freund, der auch etwas programmieren kann (das mu nicht einmal PHP sein) das Ganze mal begutachtet - denn vier Augen sehen mehr als zwei. Jeder hat natrlich seine eigene Art, eine Fehlersuche anzugehen. Ich selbst habe hier u nur kurz zeigen wollen, wie ich es mache (und damit bisher immer zum Ziel gekommen bin!). Wahrscheinlich habe ich wieder die Hlfte vergessen, aber du kannst mich natrlich a u auch auf bessere Methoden aufmerksam machen (gilt ubrigens fr die gesamte Anleitung)! u
13.1 Ubungen
Man lernt die Fehlersuche nirgendwo so gut wie beim Selbermachen. Ich habe hier mal versucht, ein paar typische Fehler zu sammeln. Fr alle Ubungen gelten folgende Fragen: u Was soll das Programm eigentlich tun? Was soll es ausgeben? Was macht das Programm? Gibt es eine Fehlermeldung, und wenn ja, welche? Wo liegt der Fehler? Kann man durch die Programmierweise diesen Fehler vermeiden? Die Lsungen benden sich im Anhang B.7. o
Christoph Reeg
Seite 159
13. Fehlersuche
Ubungen
Teil 2 $ i = 0; if ( $ i = 0){ echo D i e V a r i a b l e $ i h a t den Wert 0 ; } else { echo D i e V a r i a b l e h a t n i c h t den Wert 0 ; } ?>
Jens Hatlak
Seite 160
14 PHPDOC
Beim Programmieren, unabhngig von der verwendeten Sprache, ist nicht nur die Funktioa nalitt und Ezienz (Qualitt) des Codes wichtig, sondern meist auch die Verstndlichkeit a a a und Wiederverwendbarkeit. Das liegt darin begrndet, da teilweise mehrere Programmieu rer an einem Projekt arbeiten, zu dem der jeweilige Quelltext gehrt; auch wird oft der o Code stetig erweitert und verbessert oder es mssen Fehler gefunden und behoben werden. u In jedem Fall ist es also notwendig, da der Code ubersichtlich ist. Darber hinaus ist u es jedoch unerllich, den Quelltext ausfhrlich zu kommentieren. I. A. ist es hier dem a u jeweiligen Programmierer uberlassen, wie er dies anstellt. Es hat sich allerdings gezeigt, da eine standardisierte Vorgehensweise beim Kommentieren von Vorteil sein kann: Jeder Programmierer braucht die Syntax dieses Standards nur einmal erlernen und kann ihn dann sowohl selbst anwenden als auch schnell wiedererkennen, wenn es gilt, Quelltexte anderer zu verstehen. Mit der Programmiersprache Java wurde ein solcher Standard eingefhrt und JavaDoc u 1 liegt ein gleichnamiges Programm bei, das beliebige Klassen Java ist getauft. Dem JDK eine durch und durch objektorientierte Sprache durchparst und die enthaltenen standar disierten Kommentare so aufbereitet, da eine HTML-Ubersicht2 entsteht, die ebenfalls eine einheitliche Form aufweist: Die ozielle Java-Dokumentation wird ebenfalls mit JavaDoc erstellt. Einige PHP-Programmierer haben diese Idee aufgegrien und fr die Scriptsprache u angepat. Dabei herausgekommen ist PHPDOC eine standardisierte Methode, PHPFunktionen und Klassen3 zu kommentieren. Mit dem PHPDOC-Programm lassen sich dafr, ganz hnlich wie mit JavaDoc, HTML-Ubersichten erzeugen. Doch auch ohne das u a PHPDOC-Programm einzusetzen, wird der Code bei sachgemer Anwendung von PHPa DOC sehr viel ubersichtlicher. Es nden sich im Internet verschiedene PHPDOC-Pakete, wobei folgende Varianten zu unterscheiden sind: Der phpDocumentor von http:// www.phpdoc.org/ ist PHP-basiert und recht aktuell. Er scheint der einzige zu sein, der noch weitergepegt wird. Unter http:// www.phpdoc.de/ , http:// sourceforge.net/ projects/ phpdoc/ und http: // wwpakis.free.fr/ php3/ phpdoc/ index en.html gibt es noch eine weitere Varianten, diese werden aber oensichtlich schon lange nicht mehr weiterentwickelt. Es gibt von Christian Calloway eine Java-basierte Lsung, die den Vorteil hat, da sie o auf das Original-JavaDoc zurckgreift und somit dessen umfangreiche Ausgabemgu o lichkeiten bietet. Allerdings gibt es die Webseite nicht mehr und Google ndet keine neue.
1 2 3
Java Development Kit Es ist auch mglich, die Ubersicht in anderen Formaten wie PS oder PDF auszugeben . . . o PHP erlaubt ja, in beschrnktem Umfang, ebenfalls OOP [20]! a
Jens Hatlak
Seite 161
14. PHPDOC
phpDocumentor
In den neueren Versionen untersttzt auch Doxygen (http:// www.doxygen.org/ ) u PHP. Jede dieser Lsungen ist leider etwas anders und funktioniert auch unterschiedlich gut. o Auf die Unterschiede gehe ich weiter unten noch nher ein, wobei ich nur den phpDocua mentor im folgenden betrachten werde. Doch nun zur Vorgehensweise. Grundstzlich stehen alle PHPDOC-Kommentare in Ca Kommentarzeichen (/* C-Kommentar */); an das einleitende Zeichen wird jedoch in leichter Abwandlung ein zustzlicher Stern angehngt: /** PHPDOC-Kommentar */ a a Das folgende, etwas komplexere Beispiel zeigt einen typischen PHPDOC-Kommentar fr die ebenfalls exemplarische Funktion foo. u / foo : l i e f e r t $bar zuruck . @author Jens Hatlak @version 1.0 @param $bar Ein b e l i e b i g e r Wert @return Der n e g a t i v e E i n g a b e w e r t / function foo ( $ b a r ) { return - $ b a r ; } Da erst */ den mit /** begonnen Kommentar wieder beendet, kann dazwischen alles andere stehen, auch einzelne Sterne * wie am Anfang jeder Folgezeile; sie dienen der besse ren Abgrenzung des Kommentars vom Code. Ublicherweise beginnt der Kommentar dann auch in der zweiten Zeile mit der Beschreibung der Funktion/Klasse/Methode. Der erste Satz wird auch in der Ubersicht genutzt und der Rest erscheint dann bei der eigentlichen Beschreibung. Diese kann sich uber mehrere Zeilen hinziehen und sollte die Funktionalitt a des zu kommentierenden Folgecodes im Kern dokumentieren. Nach einer zustzlichen Leerzeile4 folgen dann die einzelnen Angaben zur Codebea schreibung. Hierzu werden Schlsselworte benutzt, die grundstzlich mit einem @ eingeleiu a tet werden und ansonsten aus der Tabelle 14.1 zu ersehen sind. In JavaDoc gibt es ubrigens noch die Schlsselworte @deprecated, @exception und u @throws sowie @package und @subpackage, die allerdings in PHPDOC in Ermangelung entsprechender Konstrukte in PHP nicht bentigt werden. Da PHP (zumindest in der o aktuellen Version 4.x) keine Zugrisrechte kennt, macht auch die Verwendung des @accessSchlsselwortes entsprechend wenig Sinn. u
14.1 phpDocumentor
Ein Aufruf des phpDocumentors, der derzeit nur unter Linux/Unix lauhig ist, erfolgt a z. B. mit phpdoc -f script.php -d html das wrde die Datei script.php im aktuellen u
4
Es sollte natrlich, wie oben beschrieben, doch ein Stern an passender Stelle stehen . . . u
Christoph Reeg
Seite 162
14. PHPDOC
phpDocumentor
@return
Tabelle 14.1: PHPDOC-Schlsselworte u Bedeutung Autor des Codeabschnitts Version des Codeabschnitts eine Version oder ein Datum Zugrisrechte: private oder public z. B. Name und Datum zu verlinkendes, ebenfalls dokumentierbares Element eine weiterfhrende URL u Parameter (Wert und Typ, ggf. auch Angabe von op tional) der Funktion bzw. Methode in der Reihenfolge der An- bzw. Ubergabe Typ des Rckgabewerts der Funktion bzw. Methode u
Verzeichnis parsen und die HTML-Ubersicht im (ggf. neu zu erstellenden) Unterverzeichnis html erzeugen.
Christoph Reeg
Seite 163
Teil IV Beispiele
Christoph Reeg
Seite 164
15 Einfaches Gstebuch a
Dieses Beispiel-Gstebuch soll wirklich ganz einfach gehalten sein, es soll nur zeigen, wie ein a einfaches Script aufgebaut ist. Es werden keinen Funktionen oder gar Objektorientierung benutzt. Das wre hier Overkill. Entsprechende Beispiele kommen spter noch. a a Was mu so ein Gstebuch eigentlich knnen? Im Prinzip nur vorhandene Eintrge a o a anzeigen und neue Eintrge entgegen nehmen. Das Speichern der Eintrge machen wir a a hier der Einfachheit halber einmal in einer Datenbank. Es stellt sich also zuerst die Frage, wie die Tabelle aussehen mu. Was fr Informationen u mssen denn gespeichert werden? Bei einem einfachen Gstebuch sollen mal der Name des u a Eintragenden und der eigentliche Text, sowie das Eintragedatum reichen. Daraus lt sich a eigentlich schon unser create table bestimmen. CREATE TABLE meldung ( id int not null primary key auto_increment , datum datetime , name varchar (200), eintrag text ); Ich habe hier noch eine eindeutige ID eingefgt, damit man einen Eintrag auch spter u a noch eindeutig identizieren kann. Nun stellt sich die Frage: Was programmiert man zuerst? Erst die Eingabe oder die Ausgabe? Ohne Daten kann man die Ausgabe nicht testen, aber ohne Ausgabe auch nicht vollstndig die Eingabe. Wenn man die Eingabe (Formular und Eintragen in die Datena bank) programmiert, mu man irgendwie uberprfen, ob das Script die Daten auch richtig u in die Datenbank schreibt. Das kann man z. B. per geeignetem select am MySQL-Prompt erledigen. Um die Ausgabe zu testen, braucht man natrlich irgendwelche Daten. Auch u diese knnte man zum Testen von Hand per passendem insert in die Datenbank schreio ben. Dies ist in der Regel jedoch aufwendiger, so da es sich empehlt, erst die Eingabe und dann die Ausgabe zu programmieren. Wer gut ist, kann natrlich auch beides gleichzeitig programmieren und damit beide u Scripte auf einmal testen. Problematisch wird es allerdings im Fehlerfall. Bei unserem Gstebuch zum Beispiel: Nehmen wir an, die Ein-/Ausgabe ist soweit programmiert, aber a bei der Ausgabe bleibt das Feld mit dem Kommentar leer. Wo liegt dann der Fehler? Einerseits kann er bei der Eingabe liegen (dann steht nichts in der Datenbank) oder bei der Ausgabe (dann wird der korrekt in der DB stehende Text nicht ausgegeben). Nun aber zu unserem Script. Als erstes nun die Eingabe. Im Prinzip sollte es durch die Kommentare selbst erklrend sein. Eine nette Spielerei ist die Weiterleitung nach erfolgtem a Eintrag. Wie wir jedoch seit Kapitel 11.1.1 wissen, mu die angegebene Adresse absolut sein. Hier werden die von PHP zur Verfgung gestellten Umgebungsvariablen genutzt, um u automatisch die aktuelle Adresse zu ermitteln und dorthin weiterzuleiten. Die Alternative
Christoph Reeg
Seite 165
wre gewesen, die Adresse hart reinzuschreiben und dann htte man Probleme, wenn a a sich die Adresse des Scripts ndert. a <? php $DBHost $DBName $DBUser $DBPasswd = = = = l o c a l h o s t ; r e e g ; r e e g ; s u p e r g e h e i m ;
// V e r b i n d u g z u DBS e r v e r h e r s t e l l e n mysql_connect ( $DBHost , $DBUser , $DBPasswd) OR die ( Konnte DBS e r v e r n i c h t e r r e i c h e n ); mysql_select_db ($DBName); if ( isset ($ GET[ s u b m i t ])){ // Der S u b m i t B u t t o n wurde g e d r u c k t // > d i e Werte m ssen u b e r p r u f t u // und b e i G u l t i g k e i t i n d i e DB e i n g e f u g t werden // w i r g e h e n von d e r G u l t i g k e i t d e r Daten a u s $DatenOK = 1; // e s g a b noch k e i n e F e h l e r m e l d u n g $ e r r o r = ; if (! isset ($ GET[ name ])){ // e s wurde k e i n Name e i n g e g e b e n $DatenOK = 0; $ e r r o r .= Es mu e i n Name e i n g e g e b e n werden<b r>\n ; $ d a t e n [ name ] = ; } else { $ d a t e n [ name ] = $ GET[ name ]; } if (! isset ($ GET[ e i n t r a g ])){ // e s wurde k e i n Komementar e i n g e g e b e n $DatenOK = 0; $ e r r o r .= Ein E i n t r a g ohne Komemntar mach n i c h t v i e l ; $ e r r o r .= S i n n , o d e r ?< b r>\n ; $ d a t e n [ e i n t r a g ] = ; } else { $ d a t e n [ e i n t r a g ] = $ GET[ e i n t r a g ]; }
Christoph Reeg
Seite 166
if ( $DatenOK ){ // Daten waren OK > a l s o i n DB e i n t r a g e n mysql_query ( sprintf ( i n s e r t i n t o meldung ( datum , name , e i n t r a g ) VALUES ( now ( ) , % s , % s ) , addslashes ( $ d a t e n [ name ]), addslashes ( $ d a t e n [ e i n t r a g ]))); echo mysql_error (); // A l l e s e i n g e t r a g e n > z u r u c k z u r U b e r s i c h t header ( L o c a t i o n : h t t p : / / .$ SERVER[ HTTP HOST]. substr ($ SERVER[ PHP SELF ],0, strrpos ($ SERVER[ PHP SELF ], / )) . / ); // und f e r t i g . . . die (); } } else { $ d a t e n [ name ] = ; $ d a t e n [ e i n t r a g ] = ; } ?> < html > < head > < title > Neuer Eintrag in unser GB </ title > </ head > < body > <? php if ( $ s u b m i t && ! $DatenOK ){ // Das F o r m u l a r wurde s c h o n a b g e s c h i c k t a b e r d i e Daten // waren n i c h t OK // > F e h l e r m e l d u n g a u s g e b e n echo <h2>F e h l e r :</ h2>\n ; echo $ e r r o r ; } // F o r m u l a r a n z e i g e n ?> < form action = <?php e c h o $ SERVER [ PHP_SELF ] ; ? > method = GET> Name : < input type = t e x t name = name size = 30 maxlength = 200 value = <?php e c h o $ d a t e n [ name ] ; ? > >
Christoph Reeg
Seite 167
< br > Text :< br > < textarea rows = 10 cols = 50 wrap = v i r t u a l name = e i n t r a g > <? php echo $ d a t e n [ e i n t r a g ]; ?> </ textarea > < br > < input type = s u b m i t name = s u b m i t value = Absenden > </ body > </ html > Die Ausgabe ist eigentlich noch einfacher als die Eingabe. Fr den ,realen Einsatz sollte u man allerdings z. B. das Datum schner formatieren und die Tabelle ist auch nicht so toll. o <? php $DBHost $DBName $DBUser $DBPasswd = = = = l o c a l h o s t ; r e e g ; r e e g r o ; s t r e n g g e h e i m ;
// V e r b i n d u g z u DBS e r v e r h e r s t e l l e n mysql_connect ( $DBHost , $DBUser , $DBPasswd) OR die ( Konnte DBS e r v e r n i c h t e r r e i c h e n ); mysql_select_db ($DBName); ?> < html > < head > < title > Die Eintr & auml ; ge in unserem GB </ title > </ head > < body > <? php $ r e s = mysql_query ( s e l e c t datum , name , e i n t r a g from meldung o r d e r by datum d e s c ); echo mysql_error (); while ( $row = mysql_fetch_array ( $ r e s )){ echo <t a b l e b o r d e r =\ 1\ w i d t h =\ 600\ >\n ; printf ( <t r><t d>Name:</ t d><t d>%s </t d></ t r >\n , htmlentities ( $row [ name ])); printf ( <t r><t d>Datum :</ t d><t d>%s </t d></ t r >\n , $row [ datum ]);
Christoph Reeg
Seite 168
printf ( <t r><t d>E i n t r a g :</ t d></ t r >\n ); printf ( <t r><t d c o l s p a n =\ 2\ >%s </t d></ t r >\n , nl2br ( htmlentities ( $row [ e i n t r a g ]))); echo </ t a b l e >\n ; } ?> < hr > < a href = e i n t r a g . php4 > neuen Eintrag hinzuf gen </ a > u </ body > </ html > Das Script in Aktion gibt es auf meiner Homepage http:// reeg.net/ . Die einzigen Anderungen wurden am Layout vorgenommen.
Christoph Reeg
Seite 169
Christoph Reeg
Seite 170
alle Sprche ausgeben u Spruch lschen o Spruch ndern a Kurze Begrndung: u Das Einfgen in die Datenbank kann uber ein einfaches SELECT von Hand uberprft weru u den. Der zufllige Spruch ist ja das eigentliche Ziel des Ganzen; hier knnte man jetzt a o auch schon fast aufhren; zumindest kann man jetzt das Ganze in Betrieb nehmen. Um o einen Spruch zu lschen, mu man ihn irgendwie auswhlen; deshalb die Gesamtausgabe o a vor dem Lschen. Uber Lschen und anschlieendes Einfgen kann man im Prinzip auch o o u a ndern; um das aber etwas komfortabler zu machen, gibt es als Letztes auch noch ein Andern.
Christoph Reeg
Seite 171
// Daten OK $ d a t e n [ S p r u c h ] = $ GET[ S p r u c h ]; } // a n z e i g e n kann nur t r u e o d e r f a l s e s e i n // b e i t r u e i s t e s g e s e t z t if ( isset ($ GET[ a n z e i g e n ])){ $ d a t e n [ a n z e i g e n ] = 1; } else { $ d a t e n [ a n z e i g e n ] = 0; } if ( $DatenOK ){ // Daten i n DB e i n t r a g e n mysql_connect ($DB HOST, $DB USER, $DB PASS) OR die ( Konnte DB n i c h t e r r e i c h e n ! ); mysql_select_db ($DB NAME) OR die ( Konnte DB n i c h t e r r e i c h e n ); mysql_query ( sprintf ( INSERT INTO s p r u c h ( Spruch , a n z e i g e n ) VALUES ( % s , % d ) , addslashes ( $ d a t e n [ S p r u c h ]), $ d a t e n [ a n z e i g e n ])); switch ( mysql_errno ()){ case 0: // A l l e s OK header ( L o c a t i o n : h t t p : / / .$ SERVER[ HTTP HOST]. substr ($ SERVER[ PHP SELF ],0, strrpos ($ SERVER[ PHP SELF ], / )) . / s h o w a l l . php4 ); exit ; continue ; case 1062: // S p r u c h d o p p e l t e i n g e t r a g e n $DatenOK = false ; $ F e h l e r .= Den S p r u c h g i b t e s s c h o n<b r>\n ; continue ; default : // S o n s t i g e r F e h l e r // > F e h l e r m e l d u n g a u s g e b e n $DatenOK = false ; $ F e h l e r .= MySQL : . mysql_errno (). : . mysql_error (). <b r>\n ; }
Christoph Reeg
Seite 172
} } else { // Werte v o r b e l e g e n $ d a t e n [ S p r u c h ] = ; $ d a t e n [ a n z e i g e n ] = 1; } ?> < html > < head > < title > SDA eintragen </ title > </ head > < body > <? php if (! $DatenOK ){ echo <h1>$ F e h l e r </h1>; } ?> < form action = <?php e c h o $ SERVER [ PHP_SELF ] ; ?> method = GET> < div align = c e n t e r > <p> < textarea name = S p r u c h rows = 5 cols = 80 ><? php echo htmlentities ( $ d a t e n [ S p r u c h ]); ?></ textarea > </ p > Spruch anzeigen < input type = c h e c k b o x name = a n z e i g e n value = c h e c k e d <? php echo ( $ d a t e n [ a n z e i g e n ] ? c h e c k e d : ); ?>>< p > < input type = s u b m i t name = s u b m i t value = A l l e s OK > </ div > </ form > </ body >
Christoph Reeg
Seite 173
sda.php4 genannt habe, wird eine Funktion get_spruch() realisiert. Diese kann dann spter aus beliebigen anderen Dateien per include() eingefgt werden. a u Es gibt auch noch eine zweite Funktion get_sprueche, die ein Array mit mehreren Sprchen zurck gibt. Diese Funktion ist praktisch, wenn du verhindern willst, das deine u u Besucher den Server mit stndigem Neuladen der Webseite uberlasten, um an alle Sprche a u zu kommen. ;-) <? php / Ho lt einen z u f a e l l i g e n Spruch aus der Datenbank und g i b t i h n a l s S t r i n g z u r u e c k / function get_spruch (){ // V e r b i n d u n g s d a t e n MySQL DB $DB HOST = l o c a l h o s t ; $DB USER = c r ; $DB PASS = 123 ; $DB NAME = c r ; mysql_connect ($DB HOST, $DB USER, $DB PASS) OR die ( Konnte DB n i c h t e r r e i c h e n ! ); mysql_select_db ($DB NAME) OR die ( Konnte DB n i c h t e r r e i c h e n ); $ r e s = mysql_query ( SELECT S p r u c h , SID0+ r a n d ( ) AS s o r t FROM s p r u c h WHERE a n z e i g e n = 1 ORDER BY s o r t LIMIT 1 ); if (! $row = mysql_fetch_array ( $ r e s )){ echo F e h l e r im S c r i p t ! ; } return $row [ S p r u c h ]; }
Christoph Reeg
Seite 174
function get_sprueche ( $ a n z a h l =3){ // V e r b i n d u n g s d a t e n MySQL DB $DB HOST = l o c a l h o s t ; $DB USER = c r ; $DB PASS = 123 ; $DB NAME = c r ; mysql_connect ($DB HOST, $DB USER, $DB PASS) OR die ( Konnte DB n i c h t e r r e i c h e n ! ); mysql_select_db ($DB NAME) OR die ( Konnte DB n i c h t e r r e i c h e n ); $ r e s = mysql_query ( sprintf ( SELECT S p r u c h , SID0+ r a n d ( ) AS s o r t FROM s p r u c h WHERE a n z e i g e n = 1 ORDER BY s o r t LIMIT %d , $ a n z a h l )); while ( $row = mysql_fetch_array ( $ r e s )){ $ s p r u e c h e [] = $row [ S p r u c h ]; } return $ s p r u e c h e ; } ?> Und um zu sehen, da die Funktion tatschlich funktioniert, hier noch ein sehr komplia ziertes Script. <? php // Wir w o l l e n s a u b e r p r o g r a m m i e r e n error_reporting ( E_ALL ); include ( . / s d a . php4 ); ?> < html > < head > </ head > < body > < pre > <? php echo get_spruch (); ?> </ pre > </ body > Und hier das zweite Script, fr mehrere Sprche. Es liest aus einen GET-Parameter die u u
Christoph Reeg
Seite 175
gewnschte Anzahl der Sprche. u u <? php // Wir w o l l e n s a u b e r p r o g r a m m i e r e n error_reporting ( E_ALL ); include ( . / s d a . php4 ); if ( isset ($ GET[ a n z a h l ]) && is_numeric ($ GET[ a n z a h l ]) && $ GET[ a n z a h l ] > 0) { $ a n z a h l = $ GET[ a n z a h l ]; } else { $ a n z a h l = 3; } ?> < html > < head > </ head > < body > <? php $ s p r u e c h e = get_sprueche ( $ a n z a h l ); foreach ( $ s p r u e c h e AS $ s p r u c h ){ printf ( <p r e >\n%s \ n</ p r e >\n , $ s p r u c h ); } ?> </ body >
Christoph Reeg
Seite 176
Spruch lschen o
$DB USER = c r ; $DB PASS = 123 ; $DB NAME = c r ; mysql_connect ($DB HOST, $DB USER, $DB PASS) OR die ( Konnte DB n i c h t e r r e i c h e n ! ); mysql_select_db ($DB NAME) OR die ( Konnte DB n i c h t e r r e i c h e n ); ?> < html > < head > < title > SDA ansehen </ title > </ head > < body > < center > < a href = i n s e r t c h a n g e . php4 > Neu einf & uuml ; gen </ a > </ center > < ul > <? php $ r e s = mysql_query ( SELECT SID , S p r u c h FROM s p r u c h ORDER BY SID ); while ( $row = mysql_fetch_array ( $ r e s )){ printf ( < l i ><p r e>%s </ p r e> <a h r e f =i n s e r t c h a n g e . php4 ? SID=%d>Ä ; n d e r n </a>   ; <a h r e f =d e l e t e . php4 ? SID=%d>Lö ; s c h e n </a> . \ n , $row [ S p r u c h ], $row [ SID ], $row [ SID ]); } ?> </ ul > </ body >
Manche Leute behaupten, bei meinen Scripten wrde nie die Lschenfunktion programmiert u o
Christoph Reeg
Seite 177
Spruch ndern a
fr das Script haben wir ja eben schon geschrieben. u Nach dem Lschen kann man entweder eine Erfolgsmeldung ausgeben, oder einfach o wieder auf z. B. die Ubersichtsseite weiterleiten, was hier gemacht wird. <? php if (! isset ($ GET[ SID ]) || ! is_numeric ($ GET[ SID ])){ die ( F e h l e r ); } // V e r b i n d u n g s d a t e n MySQL DB $DB HOST = l o c a l h o s t ; $DB USER = c r ; $DB PASS = 123 ; $DB NAME = c r ; mysql_connect ($DB HOST, $DB USER, $DB PASS) OR die ( Konnte DB n i c h t e r r e i c h e n ! ); mysql_select_db ($DB NAME) OR die ( Konnte DB n i c h t e r r e i c h e n ); mysql_query ( sprintf ( DELETE FROM s p r u c h WHERE SID=%d , $ GET[ SID ])); header ( L o c a t i o n : h t t p : / / .$ SERVER[ HTTP HOST]. substr ($ SERVER[ PHP SELF ],0, strrpos ($ SERVER[ PHP SELF ], / )) . / s h o w a l l . php4 ); ?>
Christoph Reeg
Seite 178
Spruch ndern a
und wir haben unser Anderungsscript fertig. <? php // V e r b i n d u n g s d a t e n MySQL DB $DB HOST = l o c a l h o s t ; $DB USER = c r ; $DB PASS = 123 ; $DB NAME = c r ; // e r s t m a l i s t a l l e s OK $DatenOK = true ; $ F e h l e r = ; mysql_connect ($DB HOST, $DB USER, $DB PASS) OR die ( Konnte DB n i c h t e r r e i c h e n ! ); mysql_select_db ($DB NAME) OR die ( Konnte DB n i c h t e r r e i c h e n );
if ( isset ($ GET[ s u b m i t ])){ // F o r m u l a r wurde a b g e s c h i c k t > Daten p r u e f e n if ($ GET[ S p r u c h ] == ){ $DatenOK = false ; $ F e h l e r .= B i t t e noch e i n e n Text e i n g e b e n !< b r>\n ; $ d a t e n [ S p r u c h ] = ; } else { // Daten OK $ d a t e n [ S p r u c h ] = $ GET[ S p r u c h ]; } // a n z e i g e n kann nur t r u e o d e r f a l s e s e i n // b e i t r u e i s t e s g e s e t z t if ( isset ($ GET[ a n z e i g e n ])){ $ d a t e n [ a n z e i g e n ] = 1; } else { $ d a t e n [ a n z e i g e n ] = 0; } if ( isset ($ GET[ SID ])){ // E i n e S p r u c hID wurde u b e r g e b e n // > s i e mu g e p r u f t werden // // U b e r p r u f u n g , ob s i e t a t s a c h l i c h
existiert ,
Christoph Reeg
Seite 179
Spruch ndern a
// k n n t e a u ch noch e r f o l g e n o if (! is_numeric ($ GET[ SID ])){ $DatenOK = 0; $ F e h l e r .= U n g u l t i g e S p r u c hID & uuml ; b e r g e b e n !< b r>\n ; } else { // Daten OK $ d a t e n [ SID ] = $ GET[ SID ]; } } if ( $DatenOK ){ // Daten i n DB e i n t r a g e n if ( isset ( $ d a t e n [ SID ])){ // S p r u c hID wurde u b e r g e b e n // > S p r u c h w i r d g e n d e r t a mysql_query ( sprintf ( UPDATE s p r u c h SET S p r u c h=%s , a n z e i g e n=%d WHERE SID=%d , addslashes ( $ d a t e n [ S p r u c h ]), $ d a t e n [ a n z e i g e n ], $ d a t e n [ SID ])); } else { // n e u e r S p r u c h > e i n f u g e n mysql_query ( sprintf ( INSERT INTO s p r u c h ( Spruch , a n z e i g e n ) VALUES ( % s , % d ) , addslashes ( $ d a t e n [ S p r u c h ]), $ d a t e n [ a n z e i g e n ])); } // MySQLR u c k g a b e w e r t a u s w e r t e n // Wenn OK > W e i t e r l e i t e n // s o n s t > F e h l e r m e l d u n g switch ( mysql_errno ()){ case 0: // A l l e s OK header ( L o c a t i o n : h t t p : / / .$ SERVER[ HTTP HOST]. substr ($ SERVER[ PHP SELF ],0, strrpos ($ SERVER[ PHP SELF ], / )) . / s h o w a l l . php4 ); exit ; continue ; case 1062: // S p r u c h d o p p e l t e i n g e t r a g e n
Christoph Reeg
Seite 180
Spruch ndern a
$DatenOK = false ; $ F e h l e r .= Den S p r u c h g i b t e s s c h o n<b r>\n ; continue ; default : // S o n s t i g e r F e h l e r // > F e h l e r m e l d u n g a u s g e b e n $DatenOK = false ; $ F e h l e r .= MySQL : . mysql_errno (). : . mysql_error (). <b r>\n ; } } } elseif ( isset ($ GET[ SID ])){ // e s s o l l d e r S p r u c h SID g e n d e r t werden a // > b i s h e r i g e Werte l a d e n // // SID muss an d e r S t e l l e n i c h t a u f n u m e r i s c h // g e t e s t e t w erd en , da e s nur m i t %d im s p r i n t f ( ) // g e n u t z t w i r d $ r e s = mysql_query ( sprintf ( SELECT SID , S p r u c h , a n z e i g e n FROM s p r u c h WHERE SID=%d , $ GET[ SID ])); if (! $row = mysql_fetch_array ( $ r e s )){ $DatenOK = false ; $ F e h l e r .= Konnte S p r u c h n i c h t l a d e n ; } $ d a t e n [ SID ] = $row [ SID ]; $ d a t e n [ S p r u c h ] = $row [ S p r u c h ]; $ d a t e n [ a n z e i g e n ] = $row [ a n z e i g e n ]; } else { // Werte v o r b e l e g e n $ d a t e n [ S p r u c h ] = ; $ d a t e n [ a n z e i g e n ] = 1; } ?> < html > < head > < title > SDA eintragen </ title > </ head > < body > <? php if (! $DatenOK ){ echo <h1>$ F e h l e r </h1>;
Christoph Reeg
Seite 181
Schlubemerkung
} ?> < form action = <?php e c h o $ SERVER [ PHP_SELF ] ; ?> method = GET> < div align = c e n t e r > <p> < textarea name = S p r u c h rows = 5 cols = 80 ><? php echo htmlentities ( $ d a t e n [ S p r u c h ]); ?></ textarea > </ p > Spruch anzeigen < input type = c h e c k b o x name = a n z e i g e n value = c h e c k e d <? php echo ( $ d a t e n [ a n z e i g e n ] ? c h e c k e d : ); ?>>< p > <? php if ( isset ( $ d a t e n [ SID ])){ // zum Andern a uch d i e SID u b e r g e b e n printf ( <i n p u t t y p e =h i d d e n name=SID v a l u e =%d> , $ d a t e n [ SID ]); } ?> < input type = s u b m i t name = s u b m i t value = A l l e s OK > </ div > </ form > </ body >
16.6 Schlubemerkung
Dieses Beispiel erhebt keine Anspruch darauf, ein elegant programmiertes Script zu sein. So ist es z. B. ungeschickt, die Datenbankverbindungsdaten in jede Datei zu schreiben. Eine Anderung und man mu jede Datei ndern. Es bietet sich also an, diese Daten in a eine zentrale Datei zu speichern. Bei der Gelegenheit knnte man dann auch gleich noch o den ganzen Datenbankverbindungsaufbau mit auslagern. Noch geschickter wre natrlich a u die Verwendung von OOP, wie sie in Kapitel 18 beschrieben wird. Ahnliches gilt fr das HTML-Layout der Adminseiten2 . Hier steht es fest in jeder Dau tei; exibler wren dann z. B. HTML-Header und HTML-Footer Dateien, die jeweils per a include() eingebunden wrden. u
2
Christoph Reeg
Seite 182
Ubung
Ein Programmieren mit Copy and Paste geht zwar am Anfang schneller, aber sobald man etwas ndern mu, wird es aufwendig, weil jede Stelle gendert werden mu. Beim a a Andern htte man auch das Einfgescript kopieren und dann anpassen knnen, dann a u o htte man sich die Abfragen gespart. Allerdings wre der Aufwand beim Einfgen eines a a u weiteren Feldes grer gewesen. Bei dieser Lsung mu an 4 Stellen erweitert werden: o o bei der Datenprfung, den beiden SQL-Anweisungen und dem HTML-Formular. Bei der u Kopierlsung mte man zwei mal 3 Stellen ndern. o u a
16.7 Ubung
Wieso habe ich bei der Auswahl von mehrere Sprchen eine extra Funktion get_sprueche u implementiert? Ich htte doch auch mit Hilfe einer for-Schleife mehrmals die Funktion a get_spruch aufrufen knnen. o
Christoph Reeg
Seite 183
17 Kleines Bannerscript
In diesem Kapitel zeige ich, wie ein Script zum Bannereinblenden und -zhlen aussehen a kann. Es erhebt keinen Anspruch darauf, perfekt zu sein oder alles zu knnen. Es soll nur o ein paar Mglichkeiten von PHP demonstrieren. o Was soll das Script knnen? o zuflliges Banner einblenden a zhlen, wie hug welches Banner angezeigt wurde a a die zum Banner gehrige URL als Link anbieten o zhlen, wie hug auf welches Banner geklickt wurde a a Das Ganze habe ich in zwei Teile geteilt; das Script aus dem ersten Teil erfllt die u Anforderungen der ersten beiden Punkte, das zweite kann alles geforderte. Zu Demonstrationszwecken wurde das erste Script sowohl ohne als auch mit Datenbank realisiert, das zweite hingegen der Einfachheit halber nur mit Datenbank.
Christoph Reeg
Seite 184
Erste Realisierung
Fr die Datenbankversion wird auch eine entsprechende Tabelle bentigt, fr die hier u o u die entsprechende create table-Anweisung steht. CREATE TABLE banner ( BID int not null primary key auto_increment , Name varchar (50) not null , shown int , unique ( Name ) ); Das Script sollte durch die Kommentare eigentlich soweit selbsterklrend sein. a <? php function file_get_name (){ function get_file_array (){ // g i b t e i n Array m i t a l l e n Dateinamen z u r u c k $ h a n d l e = opendir ( b a n n e r ); while ( $ f i l e = readdir ( $ h a n d l e )){ if ( $ f i l e != . && $ f i l e != . . ){ $ r e t []= $ f i l e ; } } return $ r e t ; } // a l l e Dateinamen i n Array s c h r e i b e n $ f i l e s = get_file_array (); // Z u f a l l i n i t i a l i s i e r e n srand (( double ) microtime ()*1000000); $ n r = rand (0, count ( $ f i l e s )-1); // z u f a l l i g e D a t e i a u s w a h l e n $ b a n n e r = $ f i l e s [ $ n r ]; // Z h l e r f u r D a t e i e r h h e n a o // Dateiname f u r Z h l e r i n $ f i l e n a m e s c h r e i b e n a $ f i l e n a m e = rw / . $ b a n n e r ; if ( file_exists ( $ f i l e n a m e )){ $ f p = fopen ( $ f i l e n a m e , r+); flock ( $ f p ,2); $ c o u n t = fgets ( $ f p ,1024); if ( empty ( $ c o u n t )){ $ c o u n t = 0;
Christoph Reeg
Seite 185
Erste Realisierung
function db_get_name (){ $DB HOST = l o c a l h o s t ; $DB USER = c r ; $DB PASSWD = 123 ; $DB NAME = c r ; mysql_connect ($DB HOST, $DB USER, $DB PASSWD); mysql_select_db ($DB NAME); $ r e s = mysql_query ( s e l e c t BID , Name , BID0+ r a n d ( ) AS s o r t from b a n n e r o r d e r by s o r t LIMIT 1 ; ); if (! $ r e s ){ // F e h l e r echo F e h l e r i n d e r Datenbank ; return false ; } // E r g e b n i s h o l e n $row = mysql_fetch_array ( $ r e s ); // shownZ h l e r um e i n s e r h h e n a o mysql_query ( sprintf ( u p d a t e b a n n e r s e t shown = shown+1 where BID=%d , $row [ BID ])); // B i l d n a m e z u r u c k g e b e n return $row [ Name ]; }
Christoph Reeg
Seite 186
Zweite Realisierung
?> < html > < head > < title > Banner </ title > </ head > < body > < img src = b a n n e r /<?php e c h o f i l e g e t n a m e ( ) ; ? > > < img src = b a n n e r /<?php e c h o d b g e t n a m e ( ) ; ? > > </ body > </ html >
Christoph Reeg
Seite 187
Zweite Realisierung
Die grte Anderung bendet sich im Hauptprogramm. Hier wird nun nicht mehr nur o die Funktion aufgerufen, sondern es gibt eine Fallunterscheidung. Wird das Ganze ohne ,bid aufgerufen, so wird das Banner zurckgegeben, ansonsten wird der Klick gezhlt und u a zur URL weitergeleitet. Die Kombination in einem Script mag fr diesen Fall einfacher sein, weil man alles u zusammen hat; hug ist es aber wahrscheinlich besser, diese beiden Funktionen in zwei a verschiedene Scripte zu packen. <? php $DB HOST $DB USER $DB PASSWD $DB NAME = = = = l o c a l h o s t ; c r ; 123 ; c r ;
mysql_connect ($DB HOST, $DB USER, $DB PASSWD); mysql_select_db ($DB NAME); function show_banner (){ // z u f l l i g e s Banner a u s DB h o l e n a $ r e s = mysql_query ( s e l e c t BID , Name , BID0+ r a n d ( ) AS s o r t from b a n n e r o r d e r by s o r t LIMIT 1 ; ); if (! $ r e s ){ // F e h l e r echo F e h l e r i n d e r Datenbank ; return false ; } // E r g e b n i s h o l e n $row = mysql_fetch_array ( $ r e s ); // shownZ h l e r um e i n s e r h h e n a o mysql_query ( sprintf ( u p d a t e b a n n e r s e t shown = shown+1 where BID=%d , $row [ BID ])); // Banner m i t L i n k a u s g e b e n printf ( <a h r e f =%s ? b i d=%d> . <img s r c =b a n n e r/%s b o r d e r =0></ a> , $ SERVER[ PHP SELF ], $row [ BID ], $row [ Name ]);
Christoph Reeg
Seite 188
Zweite Realisierung
/ Hauptprogramm / if ( isset ($ GET[ b i d ])){ // b i d g e s e t z t > e s wurde a u f e i n e Banner g e k l i c k // > User w e i t e r l e i t e n if (! is_numeric ($ GET[ b i d ])){ // b i d mu immer n u m e r i s c h s e i n die ( K e i n e g u l t i g e ID u b e r g e b e n ); } // URL a u s DB h o l e n $ r e s = mysql_query ( sprintf ( s e l e c t URL from b a n n e r where b i d=%d , $ GET[ b i d ])); if (! $row = mysql_fetch_array ( $ r e s )){ // F e h l e r die ( F e h l e r i n d e r Datenbank ); } // c l i c k Z a e h l e r um e i n s e r h o h e n mysql_query ( sprintf ( u p d a t e b a n n e r s e t c l i c k e d = c l i c k e d +1 where BID=%d , $ GET[ b i d ])); // User a u f d i e r i c h t i g e S e i t e w e i t e r l e i t e n header ( L o c a t i o n : . $row [ URL]); exit ; } else { // Banner a n z e i g e n ?> < html > < head > < title > Banner </ title > </ head > < body > <? php show_banner (); ?> </ body > </ html >
Christoph Reeg
Seite 189
Zweite Realisierung
Christoph Reeg
Seite 190
Jens Hatlak
Seite 191
In diesem Teil soll die Objektorientierte Programmierung (kurz OO bzw. OOP) allgemein erklrt werden. Im nchsten Teil kommt dann die Realisierung in PHP. a a Die ersten Beispielprogramme, die im Verlauf dieses Manuals besprochen wurden, hatten keine groe Struktur: In ihnen wird einfach am Anfang des Programmes angefangen und am Ende aufgehrt. Teilweise wird mit if & Co. ein wenig hin und her gesprungen. Das o Ganze wird allerdings schnell unbersichtlich und Teile lassen sich nur durch CutnPaste u wieder verwenden. Bei der Programmierung mit Funktionen wird das ganze schon etwas ubersichtlicher. Es gibt Funktionen, die bei einem Aufruf mit den ubergebenen Werten etwas machen. Diese Funktionen lassen sich in diverse Dateien ausgliedern und knnen dadurch in mehreren o Programmen wieder verwendet werden. Allerdings knnen die Funktionen untereinander o (ber den eigentlichen Funktionsaufruf hinaus) keine Werte austauschen, das heit die u Werte mssen alle uber das aufrufende Programm gehen. u Bei der Objektorientierung ist man einen Schritt weiter gegangen und hat die Daten und die Funktionen zusammengefat, so da man sie nun als eine Einheit betrachtet. Im Prinzip hat man damit versucht, die reale Welt abzubilden. Konkret heit das, da in einer solchen Einheit, Klasse genannt, die Funktionen (hier Methoden genannt) auf gemeinsame Daten (Attribute, Klassen- oder Instanzvariablen genannt) zugreifen knnen. o
Normalerweise interessiert uns das auch gar nicht Wenn auch einer, der in PHP bisher nicht konsequent umgesetzt wurde mehr dazu spter a
Jens Hatlak
Seite 192
knnten die Werte aber nicht speichern (auf globale Variablen wollen wir unter anderem o des guten Programmierstils wegen verzichten). Bei einem Objekt gibt es das Problem nicht, denn dieses besitzt das Attribut Uhrzeit und kann diese mit Hilfe der Methode setTime() nach einer Plausibiltsprfung setzen. Das Beispiel Funkwecker hinkt natrlich etwas, da a u u Objekte im Hintergrund nicht einfach weiterlaufen und damit z. B. die Weckfunktion nicht funktionieren wrde. u
Jens Hatlak
Seite 193
Nun stellen die DSP-Werke im Wesentlichen natrlich DSP-Modelle her. Sicher gibt u es aber auch den einen oder anderen Extra-Dienst, der nicht direkt abhngig von einem a Wagen ist, jedoch sinnvollerweise von den DSP-Werken angeboten wird. Auf diesen ExtraDienst soll jeder Auenstehende zugreifen knnen egal, ob er einen DSP CR hat oder o nicht. Vielleicht will der DSP-Konzern aber auch einfach nur eine Sonderaktion starten und fr begrenzte Zeit ein Logo auf jeden gefertigten DSP CR sprhen lassen. Die beiden Flle u u a scheinen auf den ersten Blick vllig unterschiedliche Problematiken zu beschreiben, aber o sie haben eine Gemeinsamkeit: Sie sollen beide auch indirekt von jedem Besitzer eines DSP CR aufruf- bzw. vernderbar sein. Logischerweise drfen Extra-Dienste der DSP-Werke a u auf keine Eigenschaften von DSP-Modellen zugreifen, denn wenn ein solcher Dienst von jemandem erbeten wird, der keinen DSP CR besitzt, dann kann auch an keinem DSP CR eine Vernderung durchgefhrt werden nicht einmal die Abfrage von Daten eines a u DSP-Modells ist erlaubt4 !
18.4 Vererbung
Wer hat nicht schon einmal von Vererbung gehrt? Beim Programmieren und insbesondere o bei Objekten geht es natrlich nicht um den biologischen Begri. Vererbung ist der zweite u fundamentale Begri der OOP und bedeutet, da jedes Objekt einer Klasse B dieselben Methoden beherrscht wie ein Objekt der Klasse A, von der Klasse B abgeleitet wurde, d. h. geerbt hat. Zustzlich kann es noch eigene Methoden denieren oder vorhandene a Methoden neu denieren (berschreiben). u
4
Auer, ein solches Datum ist wiederum vom gleichen Charakter wie die Sonderaktion
Jens Hatlak
Seite 194
Konstruktor
Bezogen auf das obige Auto-Beispiel knnten z. B. die DSP-Werke ein neues Modell DSP o CR-JH herausbringen wollen. Der Einfachheit halber nehmen sie dazu die Plne des DSP a CR, ergnzen hier und ndern da etwas das neue Modell basiert ja auf dem erfolgreichen a a DSP CR. Ebenso ist der DSP CR im Grunde auch nur ein Auto, also warum fr jedes u Modell das Rad neu ernden? ;-) Denkbar ist doch, da irgendwo in den Tiefen des DSPArchivs schon ein Plan fr ein Ur-Auto liegt, der wieder herausgekramt werden kann, wenn u alle Ideen des Modells CR verworfen werden mten niemals aber die Grundidee des u Autos!
18.4.1 Image-Beispiel
Ein Beispiel aus der Programmier-Praxis zeigt es noch deutlicher: Wir haben eine Klasse Image (Bild), die mit Hilfe der Image-Funktionen der Program miersprache ein leeres Bild mit denierbarer Hhe und Breite erzeugt. Diese Klasse kennt o die Methode show(), die das Bild anzeigt. Ein leeres Bild ist aber langweilig, also beno tigen wir eine neue Klasse Point, durch die es mglich sein soll, einen Punkt in diesem o Bild zu malen. Fr diesen Zweck gibt es die Methode set(x, y). Da die Methode show() u mehr oder weniger unverndert ubernommen werden kann, nehmen wir Point (Punkt) a als Erweiterung von Image, das heit Point erbt alle Methoden und Attribute von Image. Auf dieselbe Weise kann man von Point wiederum Circle (Kreis) und Rec tangle (Rechteck) ableiten, wobei letzteres durch die Spezialform Square (Quadrat) erweitert werden kann. Das ganze wird spter als Ubung noch etwas vertieft werden. a
18.5 Konstruktor
Wie schon in der Erklrung des Auto-Beispiels gesehen, braucht man zum Erzeugen eia nes Objektes einen sogenannten Konstruktor. Bei unserem Image-Beispiel haben wir die Methode init(), die bei jedem Objekt als erstes einmal aufgerufen werden mu, damit verschiedene Attribute initialisiert werden. Wird dies nicht gemacht, kann es passieren, da die Klasse nicht richtig funktioniert5 . Damit aber eine solche Initialisierungsfunktion immer einmal aufgerufen wird, wurde der Konstruktor eingefhrt. In vielen Programmieru sprachen so auch in PHP heit der Konstruktor so wie die Klasse. Der Konstruktor ist im Grunde nichts anderes als eine spezielle Methode, die keine Return-Anweisung enthalten darf, weil der Konstruktor grundstzlich und implizit ein Objekt der Klasse zurckliefert. a u Es gibt die Unterscheidung zwischen dem Standardkonstruktor und dem Allgemeinen Konstruktor. Whrend ersterer parameterlos und dadurch eindeutig bestimmt ist, erwara tet letzterer mindestens einen Parameter und ist dadurch allgemein brauchbar (daher die Bezeichnung). Allgemeiner Konstruktor ist natrlich nur ein Oberbegri fr alle Konu u struktoren auer dem Standardkonstruktor.
18.6 Destruktor
Manchmal braucht man neben einem Konstruktor auch eine Funktion, die am Ende die Aufrumarbeiten erledigt. Auch dafr wurde eine Mglichkeit geschaen, Destruktor gea u o
5
Jens Hatlak
Seite 195
nannt. Destruktoren knnen keine Parameter haben. o PHP kennt allerdings bisher leider keine Destruktoren. Man kann sich uber die Funktion register_shutdown_function() einen Behelf zusammenbauen oder sich PHP 5 anschauen. ;-)
Jens Hatlak
Seite 196
19 Bezeichnungen
Im Rahmen der Objektorientierung gibt es eigene Vokabeln wie Objekte, Klassen, Methoden, Vererbung etc. Ich versuche, sie hier zu erklren. a
19.1 Instanz
Eine Instanz ist ein konkretes Exemplar, das zu genau einer Klasse gehrt, also von ihr o erzeugt wurde.
19.2 Objekt
Ein Objekt ist eine Instanz einer Klasse, der man einen Namen gegeben hat, also eine ganz bestimmte Instanz. Im Endeekt wird nur mit den Objekten gearbeitet. Vom Prinzip her kann man Objekte mit Variablen und Klassen mit Datentypen vergleichen.
19.3 Klasse
Eine Klasse ist die Denition, welche Attribute und Fhigkeiten ein Objekt spter haben a a soll. Also quasi ein Bauplan fr Objekte. u
19.3.1 Basisklasse
Die Klasse, von der eine Klasse erbt, also quasi ihr direkter Vorfahre.
19.4 Methode
In der OOP werden die Funktionen einer Klasse als Methoden bezeichnet.
19.5 Attribut
Die Variablen einer Klasse heien Attribute. Attribute werden oft auch Eigenschaften eines Objekts genannt, zusammen mit den Methoden (wobei die Attribute Eigenschaften wie Farbe oder Gre sein knnen Attribute eben und Methoden z. B. Onen oder Lesen o o und Schreiben, also eine gewisse Fhigkeit). a
Jens Hatlak
Seite 197
19. Bezeichnungen
Konstruktor
19.6 Konstruktor
Methode, die beim Instanzieren1 eines Objektes einmal automatisch aufgerufen wird. Diese wird in der Regel genutzt, um Initialisierungen vorzunehmen.
19.7 Destruktor
Das Gegenteil vom Konstruktor: Die Methode wird beim Lschen des Objektes automao tisch aufgerufen. PHP 4 kennt keine Destruktoren, erst die kommende Version 5 (siehe 20.5).
Jens Hatlak
Seite 198
Jens Hatlak
Seite 199
20 Objektorientierung in PHP
Objektorientiert: Den Code habe ich von meinem Vorgnger geerbt. a Kristian Khntopp o
PHP, ursprnglich eine prozedurale Skriptsprache, hat erst mit Version 4 brauchbare u objektorientierte Zge angenommen. In diesem Kapitel will ich versuchen zu zeigen, wie u man objektorientiert in PHP programmiert und auch ein paar Beispiele geben, um zu sehen, wie man von der Problemstellung zur OO-Umsetzung kommt und auch, da sich das bichen Mehraufwand wirklich lohnt (Stichwort Ubersichtlichkeit/Wiederverwendbarkeit). Das folgende Kapitel gilt der Einfachheit halber nur fr PHP 4.06 aufwrts, exklusive PHP u a 5.
20.1.1 Konstruktoren
Konstruktoren in PHP heien grundstzlich genau so wie die Klasse, zu der sie gehren. a o Anders als etwa in Java kann es in PHP immer nur genau einen Konstruktor pro Klasse geben. Durch die Eigenschaft von PHP-Funktionen, Parameter im Funktionskopf auf
1
Vorsicht: In PHP3 gilt eine Methode mit dem Namen einer Klasse auch dann als Konstruktor dieser Klasse, wenn sich die betreende Methode gar nicht innerhalb dieser Klasse bendet!
Jens Hatlak
Seite 200
Klassen in PHP
Defaultwerte setzen zu knnen, ergibt sich jedoch die Mglichkeit, durch if-Abfragen oder o o switch-Blcke die meisten Konstellationen von Parameterbergaben abzudecken. o u Eine Besonderheit beim Umgang mit Konstruktoren ist schlielich noch der implizite Aufruf des Konstruktors der Basisklasse in dem Fall, da eine Klasse keinen Konstruktor besitzt. Die Unfhigkeit von PHP3, dies zu meistern, ist auch der Grund, warum nach a Mglichkeit PHP4 eingesetzt werden sollte. o
Noch ist es mglich, es wird aber wahrscheinlich irgendwann nicht mehr erlaubt sein o
Jens Hatlak
Seite 201
Klassen in PHP
Jens Hatlak
Seite 202
3 4
Vorsicht: Die bisherigen Beispiele waren noch unvollstndig! a Wenn das auch an dieser Stelle etwas verwirrend erscheint, so sollte es doch im Folgenden klar werden.
Jens Hatlak
Seite 203
20.2.1 Referenzierung
Um eine Methode eines Objekts bzw. einer Klasse aufzurufen, mu man quasi den Umweg uber das Objekt bzw. die Klasse gehen, denn PHP mu schlielich wissen, wessen Methode aufgerufen werden soll (unterschiedliche Klassen knnen natrlich Methoden mit genau o u demselben Namen denieren!). Will man also Attribute oder Methoden referenzieren, beginnt man mit dem fr PHP tyu pischen Dollarzeichen, gefolgt vom Namen des zu referenzierenden Objektes, einem Strichpfeil (->) und dem Namen des Attributs bzw. der Methode5 . Will man innerhalb einer Klasse auf die Attribute oder Methoden derselben Klasse zugreifen (die wie gesagt in dem Moment, wo man ein Objekt betrachtet, das Objekt selbst darstellt), mu man das Schlsselwort this anstelle des Objektnamens verwenden.6 u Eine kleine Anmerkung noch: Wie vielleicht bekannt sein drfte, werden in in doppelten u Anfhrungsstrichen geschriebenen Strings Variablen ersetzt. Das gilt aber nur fr solche u u Variablen, deren Namen nur aus Buchstaben bestehen. Insbesondere werden Referenzen nicht erkannt, da sie notwendigerweise den Strichpfeil enthalten. Hier ist also ein Unterbrechen des Strings unter Zuhilfenahme des Punkt-Stringoperators geboten. 20.2.1.1 Kopier- vs. Referenzsemantik Beim Aufruf einer Methode kann man dieser vorausgesetzt, diese erlaubt das Parameter ubergeben. Ist einer dieser Parameter ein Objekt, genauer: eine Referenz auf ein Objekt, so ndet in PHP bei der Ubergabe dasselbe Vorgehen statt wie bei gewhnlichen o Primitivtypen (Integer, Boolean, String): Es wird eine Kopie des ursprnglichen Datums, u bei Objekten also eine Kopie der Referenz, erzeugt und ubergeben. Da eine Referenz auf ein Objekt ja immer nur das Objekt bezeichnet, es aber nicht selbst darstellt, kann folglich keine Methode das ursprngliche Objekt (z. B. mit NULL) uberschreiben. Auch in u Java, wo bei Objekten statt der Kopiersemantik die sog. Referenzsemantik zum Einsatz kommt7 , funktioniert dies nicht. Der Unterschied zwischen Java und PHP in der Behandlung von Objekten kommt erst dann zum Tragen, wenn versucht wird, den Wert eines Objekt-Attributes zu ndern. Whrend Java innerhalb einer aufgerufenen Methode nma a a lich tatschlich das Attribut sowohl der ubergebenen als auch der ursprnglichen Referenz a u auf das Objekt ndert, erfolgt dies bei PHP standardmig nur auf der Referenzkopie, die a a lokal in der Methode sichtbar ist. Grundstzlich ist es natrlich immer mglich, mittels einer return-Anweisung die lokale a u o Referenz zurckzugeben und der ursprnglichen Objektvariable zuzuweisen. u u 20.2.1.2 Ausweg aus dem Referenzdilemma Wie also kann man sicherstellen, da eine Referenz auch als solche behandelt wird? In PHP gibt es dafr spezielle Syntax, bei der das Kaufmanns-Und den Referenz-Charakter u einer Zuweisung oder einer Variablen-Betrachtung anzeigt:
5
6 7
Im gesamten Ausdruck also nur ganz vorne ein Dollarzeichen. Steht hinter dem Pfeil auch ein Dollar, wird dieser Teil als variabel aufgefat und zuerst ersetzt! Eine implizite Referenzierung ohne Angabe eines Objekts wie in Java gibt es in PHP nicht. man spricht daher auch von Call by value bzw. Call by reference
Jens Hatlak
Seite 204
$a = & $b ; $ c =& $a ; $ c = 42; Die beiden obigen Zuweisung bewirken gleichermaen, da jeweils beide Variablen nicht nur denselben Wert haben, sondern auch eine Wertnderung einer der Variablen automaa tisch die exakt selbe Wertnderung der anderen Variablen bewirkt. Die letzte Anweisung a bewirkt folglich, da alle drei Variablen den Wert 42 annehmen. Auch bei Funktions- und Methodenaufrufen wird normalerweise Kopiersemantik angewandt. Um explizit Referenzsemantik zu fordern, stellt man hierbei bei einer Funktions/Methodendenition einer zu referenzierenden Parameter-Variablen ein Kaufmanns-Und voran: function Plus ( $ v a r ) { $ v a r ++; } function Minus (& $ v a r ) { $ v a r --; } $ v a r = 42; Plus ( $ v a r ); Minus ( $ v a r ); echo $ v a r ; Die Ausgabe obigen Codes ist natrlich 41, da nur die Funktion Minus den Wert der u Variablen $var ndert. In Java wre das nicht so oensichtlich, da hier u. U. eine Klassena a variable namens var existieren knnte, die implizit gemeint sein knnte. In PHP mte o o u man dann aber $this->var schreiben. Unter 20.7.2 ndet sich noch eine Ubung zu komplexeren Anwendungen der Referenzsemantik. 20.2.1.3 foreach mit Objektreferenzen Wie bereits im foreach-Kapitel 8.2.19 beschrieben, benutzt diese Schleifenvariante8 stets Kopien der Arrayinhalte. Hat man nun ein Array von Objektreferenzen, kann dies leicht zu unerwnschten Eekten fhren. Deshalb macht es hier Sinn, nur den jeweiligen Arrayu u Schlssel in Erfahrung zu bringen und diesen zu nutzen, um auf das Original-Objekt u zuzugreifen: $ c a r = new Auto (); $ d s p = new DSP_CR (); $ m y c a r s = array ( $ c a r , $ d s p ); // m i t K o p i e r s e m a n t i k foreach ( $ m y c a r s as $ o b j e c t c o p y ) echo $ o b j e c t c o p y -> farbe . \ n ; // R e f e r e n z s e m a n t i k I foreach ( $ m y c a r s as $ k e y => $ o b j e c t )
8
Dieser Begri beschreibt zwar auch das, was sich von einem Schleifendurchlauf zum nchsten andert; a das ist hier aber nicht gemeint
Jens Hatlak
Seite 205
Methoden-Aufrufe
echo $ m y c a r s [ $ k e y ]-> farbe . \ n ; // R e f e r e n z s e m a n t i k I I foreach ( array_keys ( $ m y c a r s ) as $ k e y ) echo $ m y c a r s [ $ k e y ]-> farbe . \ n ; In diesem Beispiel habe ich direkt auf die Klassenvariable $farbe zugegrien, weil die Klasse Auto auer dem Konstruktor keine bekannten Methoden hat. Das ist in PHP4 auch gar kein Problem; in PHP5 wird das je nach Sichtbarkeitsdenition aber nicht mehr mglich sein. Auerdem sollte man sich grundstzlich angewhnen, auf Attribute nur per o a o Getter- und Settermethoden zuzugreifen, da in diesen dann (ggf. auch erst nachtrglich) a geeignete Plausibilittstests gemacht und diese bei Bedarf in erbenden Klassen uberschriea ben werden knnen. o
20.3 Methoden-Aufrufe
20.3.1 static
Manchmal ist es ntig, eine Methode einer Klasse aufzurufen, ohne ein Objekt derselben o erzeugt zu haben. In diesem Fall kann man einen sogenannten static-Aufruf machen. Zu beachten ist dabei, da eine so aufgerufene Klassenmethode nirgends auf die Attribute der Klasse, in der sie sich bendet, zugreifen darf, da diese ja nur im Kontext einer Instanz (Objekt) vorhanden sind. Lokale und globale Variablen drfen dagegen natrlich benutzt u u werden. Ein primitives Beispiel: class Auto { ... function Erfinder () { return E t i e n n e L e n o i r , C a r l Benz ; } ... } echo Auto :: Erfinder ();
20.3.2 parent
Im Falle von Zugrien auf geerbte, aber uberschriebene Methoden sollte man statt des Klassennamens das Schlsselwort parent benutzen. Dadurch taucht der Name der beerbu ten Klasse nur hinter extends auf, was das nachtrgliche Vornehmen von Anderungen a z. T. stark erleichtert. Auerdem wird dadurch auch der Unterschied zwischen static (kein Objekt vorhanden) und normalem Instanz-Methodenaufruf deutlich. Beispiel: Angenommen, wir htten in der Klasse DSP_CR die Methode lenke(drehung) a der Klasse Auto uberschrieben, von der DSP_CR erbt. Will man nun innerhalb von DSP_CR auf die gleichnamige Methode der Basisklasse Auto zugreifen, kommt folgende Syntax zum Einsatz:
Jens Hatlak
Seite 206
Jens Hatlak
Seite 207
function setzeFarbe ( $ f a r b e ) { $ t h i s -> farbe = $ f a r b e ; } / Welches Baujahr ? @ r e t u r n Das B a u j a h r @returns s t r i n g / function Baujahr () { return $ t h i s -> baujahr ; } } / DSP CR K l a s s e Konstruktor der B a s i s k l a s s e wird i m p l i z i t aufgerufen @ a u t h o r DSP @version 1 . 0 / class DSP_CR extends Auto { / Konstruktor e i g e n t l i c h w re g a r k e i n e r n o t w e n d i g , a aber wir w o l l e n j a d i e Farbe i n i t i a l i s i e r e n . . . / function DSP_CR ( $ f a r b e ) { if ( empty ( $ f a r b e )) $ f a r b e = m e t a l l i c ; parent :: Auto ( $ f a r b e ); } } Alternativ zum letzten Codeteil, in dem der Konstruktor der Basisklasse aufgerufen wird, kann man auch folgende Syntax verwenden, die unabhngig vom Namen der Baa sisklasse ist - in Java wrde man das ubrigens mit einem einfachen super(parameter) u machen. class A extends B { function A ( $param ) { $ p a r e n t = get_parent_class ( $ t h i s ); $ t h i s -> $ p a r e n t ( $param ); }
Jens Hatlak
Seite 208
Ausblick: PHP5
Also nicht nur bei Zuweisungen, sondern auch bei Parameterbergaben an Funktionen und Methoden! u Implementieren, engl. to implement, heit in diesem Zusammenhang genau das Beschriebene Richtig, auch hier lt wieder Java gren! :-) a u
Jens Hatlak
Seite 209
Ausblick: PHP5
} ... Diese Methode wrde also alle Objekte akzeptieren, die von Auto oder einer davon u erbenden Klasse (z. B. DSP CR) instanziert worden sind. Falls ein Objekt von anderem Typ oder gar ein Primitivtyp ubergeben wird, wird eine Fehlermeldung ausgegeben. Nett ist auch, da nun von Methoden zurckgelieferte Objekte direkt angesprochen, u sprich dereferenziert, werden knnen. In PHP4 mu der Rckgabewert eines Funktionso u aufrufs immer erst in einer (Objekt-) Variablen abgespeichert werden, auf die man dann weitere Aufrufe anwenden kann. Beispiel fr die neue Freiheit: u ... class MyTest { var $ t e s t ; function __construct () { $ t h i s -> test = 4 2 i s t h e a n s w e r . ; } function print () { echo $ t h i s -> test ; } } MyTest ()-> print (); ... Wer schon einmal herausnden wollte, von welcher Klasse ein Objekt ist (zur Laufzeit, sonst ist das ja trivial), wird sicherlich is_a und Konsorten bemht haben. Mit PHP5 u gibt es nun ganz analog zu Java die Inx-Notation instanceof, die dasselbe ermglicht o (im Gegensatz zu is_a wird der Klassenname aber nicht als PHP-String aufgefat, sprich gequotet). Die neue __autoload() Funktion erleichtert das Einbinden von Klassen, indem sie immer dann automatisch aufgerufen wird, wenn eine Klasse nicht gefunden wird. Mit ihrer Hilfe kann man z. B. vom Namen der Klasse abhngig verschiedene includes denieren. a Ganz besonders schick ist die extra geschaene Mglichkeit, Methodenaufrufe sowie das o Setzen und Abfragen von Variablen zu uberschreiben (mittels Methoden __call, __get und __set). Die Setter-Funktionen werden dabei konsequenterweise nicht nur bei Zuweisungen, sondern auch auch beim Inkrementieren und Dekrementieren aufgerufen. Mit diesen Eigenschaften eignen sich diese Funktionen natrlich besonders gut zum sauberen u Debuggen. Bleibt noch anzumerken, da all dies rckwrtskompatibel implementiert wurde, d. h. u a PHP4-Skripts sollten mit evtl. leichten Anpassungen auch unter PHP5 laufen. Das ist wohl der Punkt, wo sich die Geister scheiden und Lager bilden OOP-pur-Verfechter auf der einen und PHP3-Hacker auf der anderen Seite . . . Zu den weiteren Neuerungen in PHP5 zhlen die rundum erneuerte XML-Untersttzung a u (libxml2-basiert), SQLite-Integration (dafr ist MySQL-Support nicht mehr direkt eingeu baut) sowie verbesserte Handhabung von Streams12 .
12
Wobei diese sicher trotzdem keine so zentrale Rolle spielen werden wie etwa in C++
Jens Hatlak
Seite 210
Konzeptionelle Beispiele
Wer sich selbst ein Bild machen will, sollte sich mal http:// www.php.net/ zend-engine-2. php ansehen.
20.7 Ubung
20.7.1 OOP
Das Beispiel aus Abschnitt 18.4.1 bietet sich an, um OOP verstehen zu lernen. Folgende Aufgabe ist zu lsen: o Schreibe eine Klasse Image, die mit Hilfe der PHP-Image-Funktionen14 ein leeres Bild erzeugt und die Methode show() implementiert. Leite von dieser Klasse eine neue namens Pointab mit der Methode set(x, y), die einen Punkt malt. Programmiere schlielich die Klassen Circle (Kreis), Rectangle (Rechteck) als Erben von Point sowie die Klasse Square (Quadrat), die Rectangle erweitert. Erstelle von jeder malfhigen Klasse ein a Objekt und benutze es jeweils, um das jeweilige Symbol auszugeben.
13 14
Siehe auch die unten folgenden Beispiele Sptestens hier solltest du einen Blick in das ozielle PHP-Manual werfen; dort gibt es ein Liste aller a Image-Funktionen samt Syntax.
Jens Hatlak
Seite 211
Ubung
Gemeinsame Daten IMAP-Session/Mailbox ID der aktuellen Nachricht String mit Meldungen Sortierungsdaten Anzahl Nachrichten in der Mailbox
Zentrale Funktionalitt a Mailbox-Ubersicht ausgeben einzelne Daten aus den Mail-Headern lesen Sortierung ndern a einzelne Mail lesen einzelne Daten aus den Mail-Headern lesen Umbruch/Formatierung Mail verschicken Mail(s) lschen o Attachmentmanagement Meldungen/Fehler ausgeben Tabelle 20.1: IMAP-Webmail-System
Zentrale Funktionalitt a zustzlich zu Webmail (Vererbung!) a Newsgroup-Liste ausgeben Newsgroup wechseln Mail verschicken Mail(s) lschen o Meldungen/Fehler ausgeben Tabelle 20.2: IMAP-Webnews-System
Jens Hatlak
Seite 212
Ubung
Zentrale Funktionalitt a Adressen hinzufgen u Adressen aktualisieren Adressen lschen o Adressauswahlliste Suchfunktion Adreliste Ausfhrliche Anzeige u Schnittstelle zu Webmail Exportmglichkeit o Tabelle 20.3: Mehrbenutzer-Adrebuch
Zentrale Funktionalitt a Bookmarks hinzufgen u Bookmarks aktualisieren Bookmarks lschen o Bookmarkauswahlliste Gruppennamen ndern a Gruppen lschen o Gruppenauswahlliste Anzeige/Ausgabe
Jens Hatlak
Seite 213
Ubung
20.7.2 Referenzsemantik
Wenn man von anderen objektorientierten Sprachen wie Java her gewohnt ist, bestimmte Dinge mit Referenzen machen zu knnen, wird man von PHP 4 zumindest anfangs o enttuscht sein.15 Wie sich zeigt, kann man aber auch in PHP 4 so programmieren, da a Referenzen auch als solche behandelt werden. In dieser Ubung soll deshalb eine bestehende Implementierung derart abgendert werden, da sie alle Referenzen auch als solche a behandelt, d. h. z. B. bei Zuweisungen statt Kopier- Referenzsemantik anzuwenden. Vorgegeben ist folgende naive Implementierung, die die besonderen Erfordernisse der Verwendung von Referenzen schlicht ignoriert und damit sicher nicht das Gewnschte u leistet. Auerdem steht unten noch das erwartete Ergebnis, also das, welches eine korrekte Implementierung erzeugen wrde und deine Lsung ist das doch hoentlich, oder? :-) u o Hinweis: Eine korrekte Implementierung kann schon erreicht werden, wenn pro Zeile maximal ein Zeichen (welches wohl?) hinzugefgt wird. u class MyArray { var $ a r r a y ; function MyArray () {} function add ( $ v a l ) { $ t h i s -> array [] = $ v a l ; } } class Test { var $ v a r ; function Test ( $ v a r , $ a r r a y ) { $ t h i s -> var = $ v a r ; $ a r r a y -> add ( $ t h i s ); } function setVar ( $ v a r ) { $ t h i s -> var = $ v a r ; } function getVar () { return $ t h i s -> var ; } } $ a r r a y = new MyArray (); $ t e s t = new Test (42, $ a r r a y ); $test2 = $test ; $ t e s t -> setVar (0); echo $ t e s t -> getVar (). . $ t e s t 2 -> getVar (). \ n ;
15
Mit PHP 5 wird standardmige Referenzsemantik fr Objekte eingefhrt, so da sich dann das a u u folgende zumindest teilweise erbrigt u
Jens Hatlak
Seite 214
Ubung
var_dump ( $ a r r a y ); Das Beispiel soll zuerst 0 0 ausgeben. Bei falscher Implementierung (wie hier) wird statt dessen 0 42 ausgegeben. Der Variablen-Dump schlielich soll besttigen, da tata schlich eine Referenz auf das Objekt $test im Array $array abgelegt wurde. Ist dies nicht a der Fall, ist der Wert der Variablen var 42 statt 0 und es steht dann kein Kaufmanns-Und vor object. object(myarray)(1) { ["array"]=> array(1) { [0]=> &object(test)(1) { ["var"]=> int(0) } } } Eine mgliche Lsung ndet sich in Kapitel B.10. o o
Jens Hatlak
Seite 215
David Peter
Seite 216
21 Sessions
21.1 Was sind Sessions?
Mit Session (engl.: Sitzung) bezeichnet man alle Daten, die dem Server einer Site (z. B. einem personalisierten System) whrend dem Verweilen eines Benutzers auf derselben a bekannt sind. Verschiedene Benutzer werden somit auch durch verschiedene Sessions identiziert.1 Technisch kann man sich die uber einen Nutzer bekannten Daten als einen serverseitigen 2 vorstellen. Die Daten knnen uber einzelne Seiten hinweg benutzt und manipuo Cookie liert werden, ohne dabei den Bezug zum jeweiligen Benutzer bzw. Abrufer zu verlieren. Sie werden aber im Gegensatz zum normalen Cookie nicht beim Client, sondern auf dem Server gespeichert. Das klingt natrlich erstmal fantastisch, da man sich so keine Gedanken u uber Browser-spezische Probleme3 machen mte. Doch gleich die schlechte Nachricht: u So einfach funktioniert das nicht, denn der Server kann bei einer normalen Anfrage nicht herausnden, welche gespeicherte Session er dem Client zuweisen soll. Deswegen mu diese Aufgabe wiederum der Client ubernehmen: Er mu eine sog. Session-ID zur Identikation selbst speichern, was er normalerweise wieder mit Cookies macht ein Teufelskreis. Keine Arme, keine Kekse . . . Es gibt auch die Mglichkeit, die Session-ID per URL o weiterzugeben. Es mu dabei egal ob POST- oder GET-Anfrage die 32-stellige ID4 immer ubergeben werden. Das passiert bei einer POST-Anfrage (Formular) uber versteckte Felder5 : <form> <input type="hidden" name="PHPSESSID" value="edb0e8zz5et4e9042fe0176a89cbde16" /> </form> und bei GET uber die URL: index.php?PHPSESSID=edb0e8zz5et4e9042fe0176a89cbde16 Einen Teil dieser Arbeit kann PHP selbst ubernehmen, wenn man die entsprechenden Stellen in der Kongurationsdatei php.ini richtig eingestellt hat: url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
1
2 3 4 5
Fr komplexere Systeme gibt es zudem die Mglichkeit, Sessions zu gruppieren, indem verschiedene u o Sessionnamen vergeben werden. Mehr dazu weiter unten. Cookies sind Variablen mit Werten, die zwischen Server und Client (Browser) ausgetauscht werden. Manche Browser (oder deren User) nehmen keine Cookies an Die Session-ID ist ein md5-verschlsselter Zufallswert. Die Chance, ihn zu erraten, ist 1 : 2128 u Im Beispiel heit die Session PHPSESSID, das mu aber nicht immer so sein
David Peter
Seite 217
21. Sessions
Praxis
Dies veranlat PHP dazu, die entsprechenden Erweiterungen bei den HTML-Tags <a>, <area>, <frame>, <input> und <form> selbst einzutragen. Auerdem mu der Pfad, unter dem die Sessions auf dem Server gespeichert werden sollen, bei Nicht-UNIX-Systemen6 noch angepat werden: session.save_path = c:\temp\
21.2 Praxis
21.2.1 Datei 1 - index.php
<? php session_start (); $ s t r i n g = T e s t ; $ i n t e g e r = 1; $ a r r a y = array ( Wert1 , Wert2 ); session_register ( s t r i n g , i n t e g e r , a r r a y ); ?> < html > < head > < title > Beginn </ title > </ head > < body > < a href = i n d e x 2 . php > Weiter zu Index2 </ a > </ body > </ html >
Standard: /tmp/
David Peter
Seite 218
21. Sessions
Praxis
print Array :< b r /> ; print_r ( $ SESSION [ a r r a y ]); // I n d e r S e s s i o n g e s p e i c h e r t e n Wert v e r n d e r n a $ SESSION [ i n t e g e r ]++; ?> </ pre > < a href = i n d e x 2 . php > Erneut aufrufen </ a > </ body > </ html > Rufen wir die Datei index.php auf, so wird zuerst eine Session durch session_start gestartet. Danach fllen wir 3 Variablen mit verschiedenen Werten, die dann durch u session_register 7 fr die Session registriert (also gespeichert) werden. Wenn man dau nach uber den Link die 2. Seite aufruft, bekommt man die eben gespeicherten Werte angezeigt. Wir haben in diesem Fall keinen Versuch unternommen, die Session-ID manuell weiterzugeben, sondern uberlassen diese Aufgabe PHP, was nicht unbedingt vorteilhaft ist, da PHP den Browser dazu veranlat, die Session-ID als Cookie zu speichern und gleichzeitig teilweise die ID der URL hinzufgt. Wenn wir eine Cookie-unabhngige Seiu a te programmieren wollen, mssen wir diese Aufgabe selbst ubernehmen und an jeden u Link in der HTML-Ausgabe den String ?SESSION-NAME =SESSION-ID anhngen. a Der Standard-Sessionname ist PHPSESSID. Wenn wir sichergehen wollen, benutzen wir die Funktion session_name(), die den Namen als String zurckgibt8 . Das gleiche gilt fr die u u Session-ID, hier verwenden wir die Funktion session_id() oder alternativ die Konstante SID. Beispiel: <? php // U n t e r b i n d e n von C o o k i e s : ini_set ( s e s s i o n . u s e c o o k i e s , 0 ); // Da w i r d i e S e s s i o nID a u t o m a t i s c h a n f u g e n , // mu PHP d i e s e A u f g a b e n i c h t mehr u bernehmen : ini_set ( u r l r e w r i t e r . t a g s , ); ... printf ( <a h r e f =i n d e x 2 . php?% s=%s > W e i t e r . . . < / a> , session_name (), session_id () ); ?>
7 8
Nimmt beliebig viele Parameter entgegen Setzen kann man den Sessionnamen, indem man derselben Funktion einen String als Parameter uber gibt. Ein solcher Aufruf mu logischerweise immer vor dem ersten Benutzen/Manipulieren der Session gemacht werden.
David Peter
Seite 219
21. Sessions
Beispiel Warenkorb
21.3.1 Waren
Die Datei waren.php enthlt ein Array9 mit verfgbaren Produkten. Da diese Seite von a u allen anderen eingebunden wird, startet sie auch die Session und registriert die Variable warenkorb als Session-Variable. <? php ini_set ( s e s s i o n . u s e c o o k i e s , 0 ); ini_set ( u r l r e w r i t e r . t a g s , ); session_start (); session_register ( w a r e n k o r b ); $waren = array ( 1 => array (
9
Bei einem richtigen Onlineshop wrden die Produkte mit Sicherheit aus einer Datenbank kommen u
David Peter
Seite 220
21. Sessions
Beispiel Warenkorb
t i t e l => DSP T aschenbuch , p r e i s => 9.90, t e x t => Das S t a n d a r d w e r k zum Thema Datenbank , SQL und PHP a l s h a n d l i c h e s Taschenbuch . ), 2 ), 3 => array ( t i t e l => DSP a u f CD , p r e i s => 19.90, t e x t => Wenn I h n e n d i e D o w n l o a d z e i t zu w e r t v o l l k nnen s i e DSP auch a l s CD b e s t e l l e n . o => array ( t i t e l => DSP H o r s p i e l , p r e i s => 29.90, t e x t => Das S t a n d a r d w e r k zum Thema Datenbank , SQL und PHP f u r A n a l p h a b e t e n .
ist ,
) ); ?> < html > < head > < title > Warenkorb </ title > </ head > < body >
21.3.2 Ubersicht
Die Seite index.php soll einen Uberblick uber die angebotenen Produkte liefern. Wei ter unten erhlt man einen Uberblick darber, was man bereits bestellt hat. Uber einen a u Entfernen-Link kann man seine bisherige Bestellung auerdem noch korrigieren. <? php include_once ( waren . php ); foreach ( $waren as $ i d => $ p r o d u k t ) { printf ( <p> <b>%s </b><b r /> P r e i s : < b>Euro % 0 1 . 2 f </b><b r /> B e s c h r e i b u n g : < b>%s </b><b r /> <a h r e f =b e s t e l l e n . php ? i d=%d&%s > B e s t e l l e n </a> </p> , $ p r o d u k t [ t i t e l ], $ p r o d u k t [ p r e i s ], htmlentities ( $ p r o d u k t [ t e x t ]), $id , SID
David Peter
Seite 221
21. Sessions
Beispiel Warenkorb
); } if ( isset ( $ SESSION [ w a r e n k o r b ]) && ! empty ( $ SESSION [ w a r e n k o r b ])) { print S i e haben f o l g e n d e Waren b e r e i t s ausgewä ; h l t : ; print <u l>; foreach ( $ SESSION [ w a r e n k o r b ] as $ i d ) { printf ( < l i > %s (<a h r e f =e n t f e r n e n . php ? i d=%d&%s > Entfernen </a>) </ l i > , htmlentities ( $waren [ $ i d ][ t i t e l ]), $id , SID ); } print </ u l>; } ?> </ body > </ html >
21.3.3 Bestellen
Die Datei bestellen.php legt Produkte in den Warenkorb <? php include_once ( waren . php ); if (! isset ($ GET[ i d ])) { die ( Kein P r o d u k t ausgewä ; h l t . ); } // Wenn d a s P r o d u k t noch n i c h t g e k a u f t wurde . . . if (! isset ( $ SESSION [ w a r e n k o r b ]) || ! in_array ($ GET[ i d ], $ SESSION [ w a r e n k o r b ])) { // I n den Warenkorb l e g e n : $ SESSION [ w a r e n k o r b ][] = $ GET[ i d ]; } ?> <p>
David Peter
Seite 222
21. Sessions
Beispiel Warenkorb
Das Produkt wurde Ihrem Warenkorb hinzugef & uuml ; gt .< br /> < a href = i n d e x . php?<?php p r i n t SID ; ? > > Zur & uuml ; ck zur Startseite </ a > </ p > </ body > </ html >
21.3.4 Entfernen
Mit der Seite entfernen.php kann man schlielich noch einzelne Produkte aus dem Warenkorb lschen. o <? php include_once ( waren . php ); // Wenn k e i n P r o d u k t a u s g e w h l t wurde , o d e r a // d a s P r o d u k t n i c h t im Warenkorb i s t . . . if (! isset ($ GET[ i d ]) || ! in_array ($ GET[ i d ], $ SESSION [ w a r e n k o r b ])) { print ( S i e haben d i e s e s P ro du kt noch n i c h t b e s t e l l t , o d e r k e i n P r o d ukt ausgewä ; h l t . ); } else { foreach ( $ SESSION [ w a r e n k o r b ] as $ i d => $ p r o d u k t ) { if ( $ p r o d u k t == $ GET[ i d ]) { unset ( $ SESSION [ w a r e n k o r b ][ $ i d ]); } } print Das P r o d u k t wurde a u s Ihrem Warenkorb g e lö ; s c h t . ; } ?> < br /> < a href = i n d e x . php?<?php p r i n t SID ; ? > > Zur & uuml ; ck zur Startseite </ a > </ body > </ html >
David Peter
Seite 223
22 XML-Dokumente parsen
In diesem Abschnitt geht es um das Parsen von XML-Dokumenten1
http:// www.w3.org/ XML/ In dieser neue Sprach- Version mu man sich ubrigens strikt an die XML-Regeln halten, also alle Tags schlieen, ihre Namen klein schreiben und Attributwerte in doppelte Anfhrungszeichen einschlieen! u Simple API for XML
David Peter
Seite 224
Beispiel 1
<!-- eXpat-abschnitt-Ende --> DTD-Tags <![CDATA[ (\$a <= \$b \&\& \$a >= \$c) ]]> Findet der Parser ein startendes Element, wie <abschnitt titel="XML-Dokumente"> , so ruft er die passende, vordenierte Funktion auf und ubergibt dieser den Namen des Elements(abschnitt) sowie die Attribute (titel = XML-Dokumente).
22.3 Beispiel 1
22.3.1 Die XML-Datei (tutorial.xml)
<? xml version = 1 . 0 ?> < tutorial titel = e x p a t > < abschnitt titel = E i n f u e h r u n g > < index keyword = e x p a t /> Text ... </ abschnitt > < abschnitt titel = B e i s p i e l e > < index keyword = b e i s p i e l e /> Wie im < ref id = e x p a t >1. Abschnitt </ ref > gezeigt , ... </ abschnitt > </ tutorial >
David Peter
Seite 225
Beispiel 1
// Die XMLNamen w e rden i n G r o b u c h s t a b e n u b e r g e b e n . // D e s h a l b w a n d e l n w i r s i e m i t s t r t o l o w e r ( ) i n K l e i n // b u c h s t a b e n um . switch ( strtolower ( $name )) { case t u t o r i a l : // Wir f u g e n d e r g l o b a l e n V a r i a b l e e i n e U b e r s c h r i f t h i n z u : $html .= <h1>. $ a t t s [ TITEL ]. </h1>; break ; case a b s c h n i t t ; $html .= <h2>. $ a t t s [ TITEL ]. </h2>; break ; case i n d e x : // Einen HTMLAnker e r z e u g e n : $html .= <a name=\ . $ a t t s [ KEYWORD ] . \ ></a>; break ; case r e f : // V e r w e i s a u f e i n e n HTMLAnker : $html .= <a h r e f =\ # . $ a t t s [ ID ] . \ >; break ; default : // Ein u n g u l t i g e s E l e m e n t i s t vorgekommen . $ e r r o r = U n d e f i n i e r t e s Element < . $name . >; die ( $ e r r o r . i n Z e i l e . x m l _ g e t _ c u r r e n t _l i ne _ nu mb e r ( $ p a r s e r )); break ; } } Wenn im XML-Dokument ein nendes Element gefunden wird, passiert folgendes: o startElement() wird mit den entsprechenden Parametern aufgerufen. in der switch()-Schleife wird uberprft, welches Element vorliegt u tutorial: Eine Uberschrift wird an das Ausgabe-Dokument($html) angehngt. a abschnitt: Eine kleinere Uberschrift wird erzeugt. index: Eine HTML-Referenz wird erstellt. ref: Ein Verweis auf eine gesetzte Referenz wird erstellt. Falls ein anderes Element ubergeben wird, gibt das Script einen Fehler aus. Dabei werden die in $atts gespeicherten Elemente verwendet. / Diese Funktion b e h a n d e l t e i n a b s c h l i e e n d e s Element A l l e P a r a m e t e r w erden a u t o m a t i s c h vom P a r s e r u b e r g e b e n
David Peter
Seite 226
Beispiel 1
@param p a r s e r Object Parserobjekt @param name string Name d e s s c h l i e e n d e n E l e m e n t s / function endElement ( $ p a r s e r , $name ) { global $html ; switch ( strtolower ( $name )) { case r e f : // Den HTMLL i n k s c h l i e e n : $html .= </a>; break ; } } Diese Funktion schliet einen eventuell oenen HTML-Link. / Diese Funktion b e h a n d e l t normalen Text A l l e P a r a m e t e r w erden a u t o m a t i s c h vom P a r s e r u b e r g e b e n @param parser Object Parserobjekt @param text string Der T e x t / function cdata ( $ p a r s e r , $ t e x t ) { global $html ; // Der n o r m a l e T e x t w i r d e i n f a c h an $html a n g e h n g t : a $html .= $ t e x t ; } Die Funktion cdata() wird aufgerufen, wenn normaler Text im XML-Dokument gefunden wird. In diesem Fall wird dieser einfach an die Ausgabe angehngt. a // Die XMLD a t e i w i r d i n d i e V a r i a b l e $ x m l F i l e e i n g e l e s e n $ x m l F i l e = implode ( , file ( t u t o r i a l . xml )); // Der P a r s e r w i r d e r s t e l l t $ p a r s e r = xml_parser_create (); // S e t z e n d e r H a n d l e r x m l _ s e t _ e l e m e n t _ h a n d l er ( $ p a r s e r , s t a r t E l e m e n t , e nd E l e m e n t ); // S e t z e n d e s CDATAH a n d l e r s x m l _ s e t _ c h a r a c t e r _ d a t a _ h a n d l e r ( $ p a r s e r , c d a t a ); // P a r s e n xml_parse ( $ p a r s e r , $ x m l F i l e ); // G i b t a l l e v e r b r a u c h t e n R e s s o u r c e n w i e d e r f r e i . xml_parser_free ( $ p a r s e r );
David Peter
Seite 227
Nachteile
// A u s g a b e d e r g l o b a l e n V a r i a b l e $html . print $html ; Wenn man das XML-Dokument etwas abndert und ein fehlerhaftes Element an einer a beliebigen Stelle einfgt, gibt das Script einen Fehler aus und zeigt an, in welcher Zeile u das falsche Element gefunden wurde.
22.4 Nachteile
Das erste Beispiel hat den Nachteil, da im Dokument keine Umlaute oder XMLspezischen Sonderzeichen verwendet werden knnen. Man lst dieses Problem meistens o o durch Entities: <? xml version = 1 . 0 ?> <! DOCTYPE tutorial [ <! ENTITY auml & ; auml ; > <! ENTITY ouml & ; ouml ; > <! ENTITY uuml & ; uuml ; > <! ENTITY Auml & ; Auml ; > <! ENTITY Ouml & ; Ouml ; > <! ENTITY Uuml & ; Uuml ; > <! ENTITY szlig & ; s z l i g ; > ]> < tutorial titel = e x p a t > < abschnitt titel = B e i s p i e l e > < index keyword = b e i s p i e l e /> Im normalen Text k & ouml ; nnen wir jetzt Umlaute verwenden : & Auml ; & auml ; & Uuml ; & uuml ; & Ouml ; & ouml ; Und auch ein scharfes S : & szlig ; </ abschnitt > </ tutorial > Dieses Dokument ist etwas kompliziert. ä (ein ) wird zu &auml; wenn man a & wiederum aust, erhlt man &auml; wird ä. Also den Ausgangszuo a stand. Das ist aber gewnscht! Die Ausgabe soll nmlich in HTML gewandelt werden. u a Und dort verwenden wir wieder ein Entity... Allerdings hat auch diese Methode ihre Nachteile. Zum Beispiel mssen &-Zeichen u immer mit & geschrieben werden. Das ist ziemlich lstig, zum Beispiel bei Codea Beispielen:
David Peter
Seite 228
Beispiel 2
... < tutorial ...> ... < code language = php > if ( $a & amp ;& amp ; $b ) { print \ $a und \ $b s i n d n i c h t 0 ; } </ code > ... </ tutorial > In diesem Fall kann man aber auch <![CDATA[ ... ]]> verwenden: ... < tutorial ...> ... < code language = php > <![ CDATA [ if ( $a && $b ) { print \ $a und \ $b s i n d n i c h t 0 ; } ]]> </ code > ... </ tutorial > Das Ausen von Entities und CDATA-Abschnitten ubernimmt expat. o
22.5 Beispiel 2
Erweitern wir unser Parser-Script. Es soll jetzt zustzlich auch noch: a PHP-Code ausfhren knnen und u o Codeabschnitte formatiert darstellen. Auerdem sollen keine globalen Funktionen/Variablen mehr benutzt werden. Zunchst bentigen wir ein neues XML-Dokument: a o <? xml version = 1 . 0 ?> <! DOCTYPE tutorial [ <! ENTITY auml & ; auml ; <! ENTITY ouml & ; ouml ; <! ENTITY uuml & ; uuml ; <! ENTITY Auml & ; Auml ; <! ENTITY Ouml & ; Ouml ; <! ENTITY Uuml & ; Uuml ;
David Peter
Seite 229
Beispiel 2
<! ENTITY szlig & ; s z l i g ; > ]> < tutorial titel = B o o l e s c h e Werte > < abschnitt titel = B e i s p i e l e > Nachfolgend ein paar Beispiele zu booleschen Abfragen . < code language = php > <![ CDATA [ $a = 3; $b = 5; if ( $a && $b ) { print $a und $b s i n d n i c h t 0 < b r /> ; } if ( $a < $b ) { print $a i s t k l e i n e r a l s $b<b r /> ; } ]]> </ code > Beim Ausf & uuml ; ren erh & auml ; lt man folgende Ausgabe . <? php $a = 3; $b = 5; if ( $a && $b ) { print $a und $b s i n d n i c h t 0 < b r /> ; } if ( $a < $b ) { print $a i s t } ?> </ abschnitt > </ tutorial >
k l e i n e r a l s $b<b r /> ;
Um globale Funktionen/Variablen zu vermeiden, bleibt uns als einziger Ausweg die Verwendung einer Klasse. class TutorialParser { var $html ; // E r s t e l l t e r HTMLCode function TutorialParser ( $ f i l e ) { // U b e r p r u f e n , ob d i e D a t e i v o r h a n d e n i s t : if (! file_exists ( $ f i l e )) { $ t h i s -> error ( D a t e i . $ f i l e . kann n i c h t g e f u n d e n werden ! );
David Peter
Seite 230
Beispiel 2
} else { $ b u f f e r = implode ( , file ( $ f i l e )); $p = xml_parser_create (); // Durch d a s S e t z e n d i e s e r O p t i o n werden n i c h t a l l e // E l e m e n t und A t t r i b u t n a m e n i n G r o b u c h s t a b e n // u m g e w a n d e l t . x ml _ pa rs e r_ se t _option ( $p , XML_OPTION_CASE_FOLDING , 0); // W i c h t i g , da d e r P a r s e r n i c h t g l o b a l e F u n k t i o n e n // a u f r u f t , s o n d e r n Methoden d i e s e r K l a s s e . xml_set_object ( $p , $ t h i s ); x m l _ s e t _ e l e m e n t _handler ( $p , s t a r t E l e m e n t , c l o s e E l e m e n t ); x m l _ s e t _ c h a r a c t e r _ d a t a _ h a n d l e r ( $p , c d a t a H a n d l e r ); // S e t z t den H a n d l e r f u r P r o c e s s i n g I n s t r u c t i o n s . x m l _ s e t _ p r o c e s s i n g _ i n s t r u c t i o n _ h a n d l e r ( $p , p i H a n d l e r ); xml_parse ( $p , $ b u f f e r ); xml_parser_free ( $p ); } } function startElement ( $ p a r s e r , $name , $a ) { // Wie im e r s t e n B e i s p i e l , a l l e r d i n g s w i r d d i e K l a s s e n // V a r i a b l e $html a n s t e l l e d e r g l o b a l e n b e n u t z t . switch ( $name ) { case t u t o r i a l : $ t h i s -> html .= <h1> . $a [ t i t e l ]. </h1> ; break ; case a b s c h n i t t ; $ t h i s -> html .= <h2> . $a [ t i t e l ]. </h2> ; break ; case i n d e x : $ t h i s -> html .= <a name= . $a [ keyword ]. ></a> ; break ; case r e f : $ t h i s -> html .= <a h r e f =# . $a [ i d ]. > ; break ; case c o d e : $ t h i s -> html .= <p r e> ; break ; default : $ e r r o r = U n d e f i n i e r t e s Element & l t ; . $name . &g t ; ; $ l i n e = x m l _ g e t _ c ur r en t_ l in e _n u mb e r ( $ p a r s e r ); $ t h i s -> error ( $ e r r o r . i n Z e i l e . $ l i n e );
David Peter
Seite 231
Beispiel 2
} } function closeElement ( $ p a r s e r , $name ) { switch ( $name ) { case r e f : $ t h i s -> html .= </a> ; break ; case c o d e : $ t h i s -> html .= </ p r e> ; break ; } } function cdataHandler ( $ p a r s e r , $ c d a t a ) { $ t h i s -> html .= $ c d a t a ; } function piHandler ( $ p a r s e r , $ t a r g e t , $ d a t a ) { switch ( $ t a r g e t ) { case php : // Es wurde e i n e C o d e s t e l l e g e f u n d e n , d i e // a u s g e f u h r t w erden s o l l . Z u e r s t s t a r t e n // w i r den A u s g a b e p u f f e r , d a m i t d i e g e s a m t e // A u s g a b e e i n g e f a n g e n werden kann . ob_start (); // A u s f u h r e n d e s PHPCodes eval ( $ d a t a ); // Einsammeln d e r A u s g a b e $ o u t p u t = ob_get_contents (); // A u s g a b e v e r w e r f e n ob_end_clean (); // Anh ngen d e r A u s g a b e an $html : a $ t h i s -> html .= <b>Ausgabe :</ b><b r /> ; $ t h i s -> html .= $ o u t p u t ; break ; } } function error ( $ s t r ) { // A u s g e b e n e i n e r F e h l e r m e l d u n g : die ( <b>F e h l e r :</ b> . $ s t r ); } function get () { // G i b t den e r z e u g t e n HTMLCode z u r u c k .
David Peter
Seite 232
Beispiel 2
return $ t h i s -> html ; } function show () { // G i b t den e r z e u g t e n HTMLCode a u s . print $ t h i s -> get (); } } Zuletzt brauchen wir noch ein kleines Script, das die Klassenfunktionen benutzt. include_once t p . php ; // E i n b i n d e n d e r K l a s s e $ t p = new TutorialParser ( t u t o r i a l . xml ); $ t p -> show ();
David Peter
Seite 233
23 Templates
Templates sind Layout/Design-Vorlagen, die meistens aus HTML-Code bestehen. Der Hauptvorteil von Templates besteht in der strikten Trennung von PHP-Code und HTML. Somit wird eine Website nicht nur ubersichtlicher (fr den Webmaster). Auch die Arbeit u an der Homepage kann besser zwischen Scripter und Designer aufgeteilt werden. Der Designer kann in Ruhe ein HTML-Dokument erstellen. An den Stellen, wo spter a der dynamische Inhalt eingesetzt werden soll, fgt er Variablen/Platzhalter ein. Der Prou grammierer setzt fr diese Variablen Werte ein, und mu sich somit nicht um das Design u kmmern. Wenn sich in der HTML-Datei etwas ndert, mu das Script nicht gendert u a a werden und umgekehrt.
23.1 Beispiel
< html > < head > < title >{ titel }</ title > </ head > < body > < h1 >{ titel }</ h1 > <p> { inhalt } </ p > </ body > </ html > So knnte ein einfaches Template zum Beispiel aussehen. Als Gegenstck dazu mu vom o u Programmierer ein Script geschrieben werden, das die Variablen titel und inhalt mit entsprechenden Werten fllt. u Da wir uns nicht mit regulren Ausdrcken herumschlagen wollen, verwenden wir ein a u Templatemodul. Ich will in den weiteren Abschnitten zwei solcher Module vorstellen.
23.2 PEAR::IT[X]
Integrated Template (Extension) ist ein Templatemodul, das mit PHP (ab Version 4) mitgeliefert wird. Es ist Bestandteil des Code-Repositorys PEAR1 und ist somit bei korrekter Installation von PEAR per include "HTML/ITX.php" verfgbar. u <? php include_once HTML/ITX . php ;
1
David Peter
Seite 234
23. Templates
PEAR::IT[X]
// ITX e r w a r t e t den P f a d z u den T e m p l a t e s a l s P a r a m e t e r $ t p l = new I n t e g r a t e d T em pl a te E xt e ns i on ( . / ); $ t p l -> loadTemplateFile ( t e m p l a t e . html ); // S e t z e n d e r V a r i a b l e n $ t p l -> setVariable ( t i t e l , T e s tTemplate ); $ t p l -> setVariable ( i n h a l t , J u s t a t e s t . . . ); // A u s g e b e n $ t p l -> show (); ?>
23.2.1 Block-API
IT[X] bietet auch noch weitere interessante Features an. Mit Hilfe der Block-API ist der Programmierer in der Lage, Teile des Templates mehrmals auszugeben (mit unterschiedlichen Werten). Als Beispiel knnte man zum Beispiel ein HTML-Selectfeld nehmen. Es o hat mehrere Optionen zum auswhlen. Wenn diese Optionen dynamisch generiert werden, a kommt man mit den herkmmlichen Platzhaltern nicht mehr weit. o < html > < body > < select > <!-- BEGIN option_loop --> < option >{ wert }</ option > <!-- END option_loop --> </ select > </ body > </ html > Das PHP-Script soll in diesem Fall fr jede einzelne Option den gesamten Block einfgen, u u und fr wert etwas einfgen. u u <? php include_once HTML/ITX . php ; // ITX e r w a r t e t a l s P a r a m e t e r den P f a d z u den T e m p l a t e $ t p l = new I n t e g r a t e d T em pl a te E xt e ns i on ( . / ); $ t p l -> loadTemplateFile ( t e m p l a t e . html );
David Peter
Seite 235
23. Templates
Smarty
Wieder e i n e , Und d i e l e t z t e ); // A u s w h l e n d e s B l o c k s a $ t p l -> setCurrentBlock ( o p t i o n l o o p ); foreach ( $ o p t i o n e n as $ w e r t ) { $ t p l -> setVariable ( w e r t , $ w e r t ); // Z u l e t z t mu d e r B l o c k nach dem S e t z e n // d e r V a r i a b l e ( n ) g e p a r s e d werden $ t p l -> parseCurrentBlock (); } // A u s g e b e n $ t p l -> show (); ?>
23.3 Smarty
Smarty wird nicht mit PHP mitgeliefert, sondern mu von http:// smarty.php.net/ heruntergeladen werden. Die Smarty-Templates sind um einiges exibler, aber auch komplizierter. Smarty bietet viele Features, die hier nicht alle aufgelistet werden knnen. Trotzdem o ein kleines Beispiel: < html > < head > < title >{ $ t i t e l }</ title > </ head > < body > < select > { html_options output = $ o p t i o n e n } </ select > </ body > </ html > Ich habe hier eine kombinierte Version der beiden oberen Beispiele benutzt. Wie man sieht, hat Smarty eine leicht vernderte Syntax fr Variablen. Auerdem gibt es schon a u vorgefertigte Funktionen, wie zum Beispiel html options, welche eine Select-Liste erstellt. <? php include_once Smarty . c l a s s . php ; $ s m a r t y = new Smarty ; $ s m a r t y -> assign ( t i t e l , SmartyT e s t ); $ s m a r t y -> assign ( o p t i o n e n , array ( O p t i o n 1 ,
David Peter
Seite 236
23. Templates
Smarty
O p t i o n 2 , Noch e i n e O p t i o n , Wieder e i n e , Und d i e l e t z t e )); $ s m a r t y -> display ( t e s t . t p l ); // T e m p l a t e s w erden b e i // Smarty a l s . t p l g e s p e i c h e r t ?> Der Titel wird hnlich wie bei IT[X] in das Template eingefgt. Aus dem Array mit a u dem Optionen erstellt Smarty direkt eine Select-Liste, was etwas Tipp-Arbeit erspart. Smarty bietet noch viele weitere Funktionen, wie zum Beispiel eine Art Debugger, der auer einer Angabe, wieviel Zeit zum Parsen bentigt wurde, auch ausgibt, welche Vao riablen mit welchen Werten belegt wurden. Auch If-Else-Abfragen, Foreach-Schleifen und Include-Direktiven sind kein Problem. Allerdings stellt man sich bei der Flle an Funktiou nen manchmal die Frage, ob man nicht gleich den PHP-Code in die HTML-Datei einbetten sollte, da die Templates oft sehr unbersichtlich werden. u
David Peter
Seite 237
Christoph Reeg
Seite 238
Christoph Reeg
Seite 239
Daten einfgen u
Abteilung AbtNr 1 2 3
Fahrtenbuch MNr 1 1
PKWNr 1 2
AbtNr 3 1 in SQL:
Typ RR VW-Golf
INSERT INTO Abteilung (AbtNr,Name) VALUES (1,EDV); INSERT INTO Abteilung (AbtNr,Name) VALUES (2,Verwaltung); INSERT INTO Abteilung (AbtNr,Name) VALUES (3,Chefetage); INSERT INTO Mitarbeiter (MNr,AbtNr,Name,GebDat) VALUES (1,3,Christoph Reeg,1979-5-13); INSERT INTO Mitarbeiter (MNr,VNr,AbtNr,Name,GebDat,Telefon) VALUES (2,1,1,junetz.de,1998-3-5,069/764758); INSERT INTO Mitarbeiter (MNr,VNr,AbtNr,Name) VALUES (3,1,1,Uli); INSERT INTO Mitarbeiter (MNr,VNr,AbtNr,Name,Telefon) VALUES (4,3,1,JCP,069/764758); INSERT INTO Mitarbeiter (MNr,VNr,AbtNr,Name,Telefon) VALUES (5,1,2,Maier,06196/671797); INSERT INTO Mitarbeiter (MNr,VNr,AbtNr,Name,Telefon) VALUES (6,5,2,Meier,069/97640232);
Christoph Reeg
Seite 240
Daten einfgen u
INSERT INTO PKW (PKWNr,AbtNr,Kennzeichen,Typ) VALUES (1,3,MTK-CR 1,RR); INSERT INTO PKW (PKWNr,AbtNr,Kennzeichen,Typ) VALUES (2,1,F-JN 1,VW-Golf); INSERT INTO Fahrbuch (MNr,PKWNr,Datum) VALUES (1,1,2000-5-13); INSERT INTO Fahrbuch (MNr,PKWNr,Datum) VALUES (2,2,1998-5-3);
Christoph Reeg
Seite 241
B Losungen
B.1 Lsung zu Baumdarstellungen o
B.1.1 Vater-Zeiger
Die CREATE TABLE mit anschlieender INSERT Anweisung sollte nicht das Problem sein: CREATE TABLE vaterzeiger ( ID int not null primary key , Name varchar (100), VID int ); INSERT INSERT INSERT INSERT INSERT INSERT INSERT INSERT INSERT INSERT INSERT INTO INTO INTO INTO INTO INTO INTO INTO INTO INTO INTO vaterzeiger vaterzeiger vaterzeiger vaterzeiger vaterzeiger vaterzeiger vaterzeiger vaterzeiger vaterzeiger vaterzeiger vaterzeiger VALUES VALUES VALUES VALUES VALUES VALUES VALUES VALUES VALUES VALUES VALUES (1, Root ,0); (2, A ,1); (3, B ,1); (4, C ,1); (5, A1 ,2); (6, B1 ,3); (7, B2 ,3); (8, C1 ,4); (9, A1I ,5); (10, C1I ,8); (11, C 1 I I ,8);
Nachdem nun die Tabelle existiert, die einzelnen Abfragen: mysql> SELECT * FROM vaterzeiger -> WHERE VID = 0; +----+------+------+ | ID | Name | VID | +----+------+------+ | 1 | Root | 0 | +----+------+------+ 1 row in set (0.00 sec) Ich bin hier einfach mal davon ausgegangen, da man die ID der Wurzel kennt. mysql> SELECT count(*) FROM vaterzeiger -> WHERE VID = 1; +----------+ | count(*) | +----------+
Christoph Reeg
Seite 242
B. Lsungen o
Lsung zu Baumdarstellungen o
| 3 | +----------+ 1 row in set (0.00 sec) Wenn man die ID der Wurzel nicht kennt, wird die Abfrage etwas lnger: a mysql> SELECT count(*) -> FROM vaterzeiger V, vaterzeiger S -> WHERE V.ID = S.VID -> AND V.VID = 0; +----------+ | count(*) | +----------+ | 3 | +----------+ 1 row in set (0.01 sec) Hier wird ein Self-Join gemacht, da MySQL keine Unterabfragen untersttzt. u Dies ist im Prinzip dieselbe Abfrage wie gerade eben, nur das count() wird weggelassen. Eine Mglichkeit wre o a mysql> SELECT V.Name, ist Vater von , S.Name -> FROM vaterzeiger V, vaterzeiger S -> WHERE V.ID = S.VID -> AND V.VID = 0; +------+-----------------+------+ | Name | ist Vater von | Name | +------+-----------------+------+ | Root | ist Vater von | A | | Root | ist Vater von | B | | Root | ist Vater von | C | +------+-----------------+------+ 3 rows in set (0.00 sec) Viel schner nde ich aber folgende Ausgabe: o mysql> SELECT S.VID != 0 AS Tiefe, S.Name -> FROM vaterzeiger V, vaterzeiger S -> WHERE (V.ID = S.VID OR S.VID = 0) -> AND V.VID = 0; +-------+------+ | Tiefe | Name | +-------+------+ | 0 | Root |
Christoph Reeg
Seite 243
B. Lsungen o
Lsung zu Baumdarstellungen o
| 1 | A | | 1 | B | | 1 | C | +-------+------+ 4 rows in set (0.00 sec) Uber entsprechend viele Self-Joins (hier 4) ist dies mglich, allerdings wird dadurch o die maximale Tiefe des Baums bestimmt und die Abfrage ist nicht mehr wirklich schn und performant. o
Christoph Reeg
Seite 244
B. Lsungen o
Lsung zu Baumdarstellungen o
-> AND s_id.Name = "C" -> AND s.l BETWEEN s_id.l AND s_id.r -> GROUP BY s.l; +------+-------+ | Name | Level | +------+-------+ | C | 2 | | C1 | 3 | | C1I | 4 | | C1II | 4 | +------+-------+ 4 rows in set (0.00 sec) Die Abfrage lautet: mysql> SELECT s.Name, -> (s.r-s.l-1)/2 AS Nachfolger, -> count(*)+(s.l>1) AS Tiefe, -> ((min(v.r)-s.r-(s.l>1))/2) > 0 AS Bruder -> FROM NestedSet v, NestedSet s -> WHERE s.l BETWEEN v.l AND v.r -> AND (v.Name != s.Name OR s.l = 1) -> GROUP BY s.Name -> ORDER BY s.l; +------+------------+-------+--------+ | Name | Nachfolger | Tiefe | Bruder | +------+------------+-------+--------+ | Root | 10.00 | 1 | 0 | | A | 2.00 | 2 | 1 | | A1 | 1.00 | 3 | 0 | | A1I | 0.00 | 4 | 0 | | B | 2.00 | 2 | 1 | | B1 | 0.00 | 3 | 1 | | B2 | 0.00 | 3 | 0 | | C | 3.00 | 2 | 0 | | C1 | 2.00 | 3 | 0 | | C1I | 0.00 | 4 | 1 | | C1II | 0.00 | 4 | 0 | +------+------------+-------+--------+ 11 rows in set (0.00 sec) Eigentlich gar nicht so schwierig, oder? ;-) Ich gebe zu, ich habe auch einen Moment fr diese Abfrage gebraucht und mute u etwas basteln, bis es endlich funktioniert hat.
Christoph Reeg
Seite 245
B. Lsungen o
2. Teilschritt
function print_result_table ( $ r e s u l t ){ // T a b e l l e n a n f a n g echo <t a b l e >\n ; // A l l e E r g e b n i s z e i l e n d u r c h g e h e n while ( $row = mysql_fetch_row ( $ r e s u l t )){ // T a b e l l e n z e i l e n Anfang echo <t r >\n ; // e r s t e s F e l d d e r Z e i l e a u s g e b e n echo <t d>$row [ 0 ] < / t d>\n ; // T a b e l l e n z e i l e n Ende echo </ t r >\n ; } // T a b e l l e n e n d e echo </ t a b l e >\n ;
Christoph Reeg
Seite 246
B. Lsungen o
3. Teilschritt
Mit int mysql_num_fields(int result) kann man abfragen, wie viele Spalten bei der Abfrage zurckgegeben wurden. Mit einer for-Schleife werden einfach alle Felder ausgeu geben. function print_result_table ( $ r e s u l t ){ // T a b e l l e n a n f a n g echo <t a b l e >\n ; // A l l e E r g e b n i s z e i l e n d u r c h g e h e n while ( $row = mysql_fetch_row ( $ r e s u l t )){ // T a b e l l e n z e i l e n Anfang echo <t r >\n ; // A l l e S p a l t e n d u r c h g e h e n for ( $ i = 0; $ i < mysql_num_fields ( $ r e s u l t ); $ i ++){ echo <t d>$row [ $ i ]</ t d>\n ; } // T a b e l l e n z e i l e n Ende echo </ t r >\n ; } // T a b e l l e n e n d e echo </ t a b l e >\n ; }
4. Teilschritt
Als letztes mssen wir noch abfragen, wie die Spalten heien. Das macht die Funktion u string mysql_field_name(int result, int field_index). function print_result_table ( $ r e s u l t ){ // T a b e l l e n a n f a n g echo <t a b l e >\n ; // 1 . T a b e l l e n z e i l e Anfang echo <t r >\n ; for ( $ i = 0; $ i < mysql_num_fields ( $ r e s u l t ); $ i ++){ echo <t h>. mysql_field_name ( $ r e s u l t , $ i ). </t h>\n ; } // 1 . T a b e l l e n z e i l e Ende echo </ t r >\n ;
Christoph Reeg
Seite 247
B. Lsungen o
// A l l e E r g e b n i s z e i l e n d u r c h g e h e n while ( $row = mysql_fetch_row ( $ r e s u l t )){ // T a b e l l e n z e i l e n Anfang echo <t r >\n ; // A l l e S p a l t e n d u r c h g e h e n for ( $ i = 0; $ i < mysql_num_fields ( $ r e s u l t ); $ i ++){ echo <t d>$row [ $ i ]</ t d>\n ; } // T a b e l l e n z e i l e n Ende echo </ t r >\n ; } // T a b e l l e n e n d e echo </ t a b l e >\n ; }
function print_result_table ( $ r e s u l t ){ // s . o . } // Hauptprogramm / V e r b i n d u n g z u r D a t e n b a n k a u f b a u e n / $db = @mysql_connect ( $ d b h o s t , $ d b u s e r , $ d b p a s s ) OR die ( mysql_error ()); @mysql_select_db ( $ d a t a b , $db ) OR die ( mysql_error ());
Christoph Reeg
Seite 248
B. Lsungen o
/ HTMLS t a r t c o d e a u s g e b e n / echo <html>\n<body>\n ; / SQLA b f r a g e / $ r e s u l t = @mysql_query ( SELECT FROM M i t a r b e i t e r ); / Wenn d i e Fehlernummer ! = 0 i s t , dann g a b e s e i n e n F e h l e r => F e h l e r m e l d u n g a u s g e b e n / if ( mysql_errno () != 0){ echo mysql_error (); } // e s g a b k e i n e F e h l e r => E r g e b n i s a u s g e b e n else { // Wie v i e l e D a t e n s a t z e wurden g e f u n d e n ? // B e i 0 Meldung a u s g e b e n if ( mysql_num_rows ( $ r e s u l t ) == 0){ echo K e i n e D a t e n sä ; t z e g e f u n d e n ! ; } // s o n s t d i e F u n k t i o n a u f r u f e n else { print_result_table ( $ r e s u l t ); } } / HTMLEndcode a u s g e b e n / echo </body>\n</html>\n ; ?>
Christoph Reeg
Seite 249
B. Lsungen o
VNr fr die zweite Einfgeoperation zu haben. Ersteres erreicht man durch Weglassen der u u MNr bei der Liste der Felder (oder bei Angabe der MNr Setzen des Wertes auf NULL), letzteres durch mysql_insert_id(). Folgender Code implementiert dies: $db host $db user $db pass $datab = = = = l o c a l h o s t ; c r ; 123 ; c r ;
/ V e r b i n d u n g z u r D a t e n b a n k a u f b a u e n / $db = @mysql_connect ( $ d b h o s t , $ d b u s e r , $ d b p a s s ) OR die ( mysql_error ()); @mysql_select_db ( $ d a t a b , $db ) OR die ( mysql_error ()); // J e n s e i n f u g e n mysql_query ( INSERT INTO M i t a r b e i t e r ( VNr , AbtNr , Name , GebDat ) VALUES ( 1 , 2 , J e n s , 1 9 8 1 0 5 2 6 ) ); echo mysql_error (); $ v n r = mysql_insert_id (); // K i l e e i n f u g e n und J e n s u n t e r s t e l l e n mysql_query ( INSERT INTO M i t a r b e i t e r ( VNr , AbtNr , Name ) VALUES ( $ v n r , 1 , K i l e ) ); echo mysql_error ();
Christoph Reeg
Seite 250
B. Lsungen o
B.6.1.4 Und nochmal passen beide. In beiden Strings kommt ein 5 Zeichen langes Wort vor. B.6.1.5 Auf den ersten Text pat er nicht, dafr auf den zweiten. u
Christoph Reeg
Seite 251
B. Lsungen o
nicht optimal, weil 1.1, keine.gueltiger.name oder rechnername keine gltigen Namen u oder IP-Adressen sind, trotzdem aber auf den Ausdruck passen. Die nchsten beiden Felder sind einfacher, weil hier entweder normale Wrter oder ein a o Minus fr nicht vorhanden stehen. Die eckigen Klammern mssen mit einem Backslash u u escaped werden, weil ich hier keine Menge von Zeichen denieren will, sondern die Zeichen eckige Klammer auf und eckige Klammer zu haben will. Ahnliches gilt fr den Slash: u Wenn ich ihn nicht mit einem Backslash escaped htte, wre der Ausdruck an der Stelle a a zu Ende. Der Ausdruck in den eckigen Klammern mte eigentlich klar sein. Da die Anzahl der u einzelnen Zeichen feststehen, habe ich sie hier explizit angegeben. In den Anfhrungszeichen steht das nchste nicht-Optimum. Das erste Wort in den u a Anfhrungszeichen ist entweder GET oder POST; trotzdem mu ich beliebige Wrter u o erlauben, weil ich noch nicht geschrieben habe, wie man Oder realisiert. Der Punkt bei HTTP mu auch mit einem Backslash escaped werden, damit nur der Punkt pat und nicht ein beliebiges Zeichen. Da der Statuscode immer eine dreistellige Zahl zwischen 100 und 505 ist, mu auch hier explizit die Anzahl der Ziern angegeben. Ist auch nicht ganz optimal, weil nicht weit genug geprft wird. Die Grenangabe (das letzte Feld) kann auch nichts (d. h. ein Minus u o -) enthalten, wenn es keinen Sinn machen wrde. Daher dieser Ausdruck. u
B.6.3 Gruppierungen
B.6.3.1 /(\d*),\1DM/
Christoph Reeg
Seite 252
B. Lsungen o
Solche Fehler knnte man vermeiden, wrde man statt if ($i == 1) ein if (1 == $i) o u schreiben. Dann erhielte man nmlich eine Fehlermeldung, wenn man nur 1 = $i schriebe, a weil man dem Wert 1 nicht den Wert der Variablen zuweisen kann. Teil 2 Im Prinzip gilt das oben Gesagte. Hier wird allerdings der else-Block ausgefhrt. Bei der u if-Anweisung wird der Wert 0 genommen und der gilt als false.
Christoph Reeg
Seite 253
B. Lsungen o
<?php for ($i=1; $i<=5; $i++) { echo "$i<br>\n"; echo "Das waren die Zahlen 1-5"; ?>
farbliche Hervorhebung in Abhngigkeit der Bedeutung. Z. B. Text in Anfhrungszeichen wird grn a u u gefrbt, reservierte Wrter in dunkelrot. a o
Christoph Reeg
Seite 254
B. Lsungen o
Die GD-Lib, die von den Image-Funktionen benutzt wird, untersttzt das GIF-Format aus lizenzu rechtlichen Grnden nicht mehr u
Christoph Reeg
Seite 255
B. Lsungen o
$ t h i s -> dcolor = ImageColorAllocate ( $ t h i s -> im , 0, 0, 0); } / B i l d a n z e i g e n (PNG) / function show () { Header ( C o n t e n tt y p e : image / png ); ImagePNG ( $ t h i s -> im ); } / Breite des Bildes z u r u c k l i e f e r n @return i n t B r e i t e / function getWidth () { return $ t h i s -> width ; } / H he d e s B i l d e s z u r u c k l i e f e r n o @ r e t u r n i n t H he o / function getHeight () { return $ t h i s -> height ; } } Nun die Punkt-Klasse: / Point Klasse Kann e i n e n Punkt malen und a u s g e b e n / class Point extends Image { / XK o o r d i n a t e / var $x = 0; / YK o o r d i n a t e / var $y = 0; / K o o r d i n a t e n und s o n s t i g e s s e t z e n @param i n t XK o o r d i n a t e
Christoph Reeg
Seite 256
B. Lsungen o
@param i n t YK o o r d i n a t e / function set ( $x =0, $y =0) { $ t h i s -> x = $x ; $ t h i s -> y = $y ; ImageSetPixel ( $ t h i s -> im , $ t h i s -> x , $ t h i s -> y , $ t h i s -> dcolor ); } } Und schlielich die Ausgabe: $ m ypo int = new Point (); $ m ypo int -> set (50, 50); $ m ypo int -> show (); Als nchstes der Kreis: a / Circle Klasse Kann e i n e n K r e i s malen und a u s g e b e n , e r f o r d e r t a b e r d i e GDL i b 2 . 0 / class Circle extends Point { / R a d i u s / var $ r = 0; / K o o r d i n a t e n und R a d i u s s e t z e n @param i n t XK o o r d i n a t e @param i n t YK o o r d i n a t e @param i n t R a d i u s / function set ( $x =0, $y =0, $ r =10) { parent :: set ( $x , $y ); $ t h i s -> r = $ r ; ImageEllipse ( $ t h i s -> im , $ t h i s -> x , $ t h i s -> y , $ t h i s -> r , $ t h i s -> r , $ t h i s -> dcolor ); } } Kreis-Ausgabe (funktioniert nur mit GD-Lib 2.0!): $ m y c i r c l e = new Circle (); $ m y c i r c l e -> set (50, 50, 30); $ m y c i r c l e -> show (); Da ich die GD-Lib 2.0 nicht habe, fehlt hier das entsprechende Bild . . . Ein Rechteck ist auch nicht schwer zu erstellen:
Christoph Reeg
Seite 257
B. Lsungen o
/ Rectangle Klasse Kann e i n R e c h t e c k malen und a u s g e b e n / class Rectangle extends Point { / z w e i t e XK o o r d i n a t e / var $x2 ; / z w e i t e YK o o r d i n a t e / var $y2 ; / K o o r d i n a t e n und Ausmae s e t z e n ; ggf . zentrieren @param i n t XK o o r d i n a t e @param i n t YK o o r d i n a t e @param i n t B r e i t e @param i n t H he o @param b o o l e a n Um X/YK o o r d i n a t e n z e n t r i e r e n / function set ( $x =0, $y =0, $ w i d t h =10, $ h e i g h t =10, $ c e n t e r = false ){ if ( $ c e n t e r ) { parent :: set ( $x - $ w i d t h /2, $y - $ h e i g h t /2); $ t h i s -> x2 = $x + $ w i d t h /2; $ t h i s -> y2 = $y + $ h e i g h t /2; } else { parent :: set ( $x , $y ); $ t h i s -> x2 = $x + $ w i d t h ; $ t h i s -> y2 = $y + $ h e i g h t ; } // ( x , y ) i s t l i n k s o b e n , ( x2 , y2 ) r e c h t s u n t e n ImageRectangle ( $ t h i s -> im , $ t h i s -> x , $ t h i s -> y , $ t h i s -> x2 , $ t h i s -> y2 , $ t h i s -> dcolor ); } } Beispiel-Rechteck: $ m y r e c t = new Rectangle (); $ m y r e c t -> set (50, 50, 30, 40, true ); $ m y r e c t -> show (); Zum Schlu noch das Quadrat: /
Christoph Reeg
Seite 258
B. Lsungen o
Square Klasse Kann e i n Q u a d r a t malen und a u s g e b e n / class Square extends Rectangle { / K o o r d i n a t e n und Ausmae s e t z e n ; ggf . zentrieren @param i n t XK o o r d i n a t e @param i n t YK o o r d i n a t e @param i n t S e i t e n l n g e a @param b o o l e a n Um X/YK o o r d i n a t e n z e n t r i e r e n / function set ( $x =0, $y =0, $ l e n g t h =10, $ c e n t e r = false ) { parent :: set ( $x , $y , $ l e n g t h , $ l e n g t h , $ c e n t e r ); } } Und die obligatorische Ausgabe: $m ysqua re = new Square (); $m ysqua re -> set (50, 50, 30); $m ys qua re -> show ();
Christoph Reeg
Seite 259
B. Lsungen o
class Test { var $ v a r ; function Test ( $ v a r , & $ a r r a y ) { $ t h i s -> var = $ v a r ; $ a r r a y -> add ( $ t h i s ); } function setVar ( $ v a r ) { $ t h i s -> var = $ v a r ; } function getVar () { return $ t h i s -> var ; } } $ a r r a y = new MyArray (); $test =& new Test (42, $ a r r a y ); $ t e s t 2 =& $ t e s t ; $ t e s t -> setVar (0); echo $ t e s t -> getVar (). . $ t e s t 2 -> getVar (). \ n ; var_dump ( $ a r r a y );
Christoph Reeg
Seite 260
COPYRIGHT
The copyright to each Open Publication is owned by its author(s) or designee.
SCOPE OF LICENSE
The following license terms apply to all Open Publication works, unless otherwise explicitly stated in the document. Mere aggregation of Open Publication works or a portion of an Open Publication work with other works or programs on the same media shall not cause this license to apply to those other works. The aggregate work shall contain a notice specifying the inclusion of the Open Publication material and appropriate copyright notice. SEVERABILITY. If any part of this license is found to be unenforceable in any jurisdiction, the remaining portions of the license remain in force. NO WARRANTY. Open Publication works are licensed and provided as is without warranty of any kind, express or implied, including, but not limited to, the implied warranties of merchantability and tness for a particular purpose or a warranty of noninfringement.
Christoph Reeg
Seite 261
Englische Version
GOOD-PRACTICE RECOMMENDATIONS
In addition to the requirements of this license, it is requested from and strongly recommended of redistributors that: If you are distributing Open Publication works on hardcopy or CD-ROM, you provide email notication to the authors of your intent to redistribute at least thirty days before your manuscript or media freeze, to give the authors time to provide updated documents. This notication should describe modications, if any, made to the document. All substantive modications (including deletions) be either clearly marked up in the document or else described in an attachment to the document. Finally, while it is not mandatory under this license, it is considered good form to oer a free copy of any hardcopy and CD-ROM expression of an Open Publicationlicensed work to its author(s).
LICENSE OPTIONS
The author(s) and/or publisher of an Open Publication-licensed document may elect certain options by appending language to the reference to or copy of the license. These options are considered part of the license instance and must be included with the license (or its incorporation by reference) in derived works. To prohibit distribution of substantively modied versions without the explicit permission of the author(s). SSubstantive modication dened as a change to the s semantic content of the document, and excludes mere changes in format or typographical corrections. To accomplish this, add the phrase Distribution of substantively modied versions of this document is prohibited without the explicit permission of the copyright holder. to the license reference or copy.
Christoph Reeg
Seite 262
Deutsche Version
To prohibit any publication of this work or derivative works in whole or in part in standard (paper) book form for commercial purposes is prohibited unless prior permission is obtained from the copyright holder. To accomplish this, add the phrase Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder. to the license reference or copy.
COPYRIGHT
Das Copyright jeder Open Publication gehrt dem Autor / den Autoren oder Zeichnungso berechtigten.
Christoph Reeg
Seite 263
Deutsche Version
mengefate Arbeit soll einen Hinweis enthalten, die die Aufnahme von Open-PublicationMaterial und eine geeignete Copyright-Notiz angibt. ABTRENNBARKEIT. Wenn irgendein Teil dieser Lizenz durch irgendeine Rechtsprechung auer Kraft gesetzt werden, bleiben die verbleibenden Teile der Lizenz in Kraft. KEINE GEWAHRLEISTUNG. Open-Publication-Arbeiten werden lizensiert und verbreitet wie sie sindhne Gewhrleistung jeglicher Art, explizit oder implizit, einschlielich, o a aber nicht begrenzt auf, der impliziten Gewhrleistung des Vertriebs und der Geignetheit a fr einen besonderen Zweck oder eine Gewhleistung einer non-infringement. u a
Christoph Reeg
Seite 264
Deutsche Version
LIZENZ-OPTIONEN
Der/die Autor/en und/oder der Herausgeber eines unter Open Publication lizensierten Dokuments darf bestimmte Optionen durch Anhngen von Regelungen an den Lizenza Verweis oder die Lizenz-Kopie whlen. Diese Optionen sind empfohlener Teil der Lizenza bestimmungen und mssen in abgeleiteten Arbeiten in die Lizenz eingefgt werden. u u Verhindern der Verteilung von substantiell modizierten Versionen ohne explizite Erlaubnis des Autors / der Autoren. Substantielle Modizierung ist deniert als eine Anderung des semantischen Inhalts des Dokuments und schliet bloe Format Anderungen oder typographische Korrekturen aus. Zur Anwendung fgen Sie den Satz Distribution of substantively modied versions of u this document is prohibited without the explicit permission of the copyright holder (Verbreitung von substantiell modizierten Versionen dieses Dokuments ist ohne die explizite Erlaubnis des Copyright-Inhabers untersagt) dem Lizenz-Verweis oder der Lizenz-Kopie hinzu. Verhindern jeglicher Verentlichung dieser Arbeit oder abgeleiteter Arbeiten im o Ganzen oder in Teilen in Standard- (Papier-) Buchform fr kommerzielle Zwecke u ohne vorherige Erlaubnis durch den Copyright-Inhaber. Zur Anwendung fgen Sie den Satz Distribution of the work or derivative of the work u in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder(Verbreitung dieser Arbeit oder abgeleiteter Arbeiten in Teilen in Standard- (Papier-) Buchform fr kommerzielle Zwecke ohne vorherige u Erlaubnis durch den Copyright-Inhaber ist untersagt) dem Lizenz-Verweis oder der Lizenz-Kopie hinzu.
Christoph Reeg
Seite 265
Literaturverzeichnis
C.3 zitierte Literatur
[1] Brockhaus Enzyklopdie, Band 6 a [2] Duden Fremdwrterbuch o [3] IBM-Lehrgangsunterlagen DB2 Familie - Datenbankdesign
Christoph Reeg
Seite 266
Abbildungsverzeichnis
3.1 3.2 3.3 4.1 4.2 4.3 7.1 7.2 7.3 8.1
Struktur eines Datenbanksystems . . . . . . . . . . . . . . . . . . . . . . . . 10 Die vier Ebenen eines DBS . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Tabellenstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Streifendiagramm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 ER-Beispiel 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 ER-Beispiel 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Unser Baum-Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Baumdarstellung als Summe von Mengen . . . . . . . . . . . . . . . . . . . 70 Baumdarstellung im Nested Set Modell . . . . . . . . . . . . . . . . . . . . 71 Umschichten der Trme von Hanoi . . . . . . . . . . . . . . . . . . . . . . . 116 u
Christoph Reeg
Seite 267
Tabellenverzeichnis
1.1 3.1 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 7.1 8.1 8.2 8.3 8.4 8.5 8.6 8.7 9.1 9.2
Typogr. Konventionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Beispiel fr Tabellenstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . 13 u Verfgbare Datentypen in SQL . . . . . . . u Bedeutung der YMHSDs . . . . . . . . . . . Verfgbare Vergleichsoperatoren in SQL . . u Mathematische Funktionen in SQL . . . . . Logische Funktionen in SQL . . . . . . . . . Bit-Funktionen in SQL . . . . . . . . . . . . String-Funktionen in SQL . . . . . . . . . . Datum-Funktionen in SQL . . . . . . . . . mgl. Formatierungen fr DATE FORMAT o u Gruppenfunktionen in SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 32 43 50 51 51 51 52 53 53
Baumdarstellung mit Vater-Zeiger . . . . . . . . . . . . . . . . . . . . . . . 69 Arithmetische Operatoren in PHP Bit-Operatoren in PHP . . . . . . Logische Operatoren in PHP . . . Typen in PHP . . . . . . . . . . . escaped characters . . . . . . . . . Vergleichsoperatoren in PHP . . . printf: Platzhalter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 86 86 87 88 93 107
11.1 Content-Type Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 11.2 Cache Header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 12.1 12.2 12.3 12.4 Zeichenmengen . . . . . . . . . . Sonderzeichen bei den PCRE . . Quantizierer . . . . . . . . . . . Optionen fr regulre Ausdrcke u a u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 152 153 154
14.1 PHPDOC-Schlsselworte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 u 18.1 Von Autos zu Objekten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 20.1 IMAP-Webmail-System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Christoph Reeg
Seite 268
Tabellenverzeichnis
Tabellenverzeichnis
Christoph Reeg
Seite 269
D Danksagungen
Bei so einem Werk drfen die Danksagungen natrlich nicht fehlen. u u Als erstes und wichtigstes mu ich natrlich allen Programmierern von Linux, Apache, u A MySQL, PHP, L TEX, Ghostview, XFree86, xEmacs, CVS und allen Programmierern, deren Programme ich auch noch benutzt habe, danken. Denn erst durch sie ist die Grundlage fr dieses Dokument mglich geworden. u o Dann darf ich natrlich die ganzen Kritiker, allen voran Stefan Nagelschmitt, Jens Hatu lak und Nikolai Stiehl mit Mutter(!), nicht vergessen, die dieses Dokument erst zu dem gemacht haben, was es jetzt hoentlich ist. Ich hoe, es kommen noch ein paar dazu. Ein besonderes Dankeschn geht an die kreativen Leser, die einen Vorschlag fr das o u neue Logo gemacht haben. Es waren viele, sehr interessante Logos dabei! Und wie es immer so schn heit: Ich bedanke mich noch bei allen, die ich vergessen o habe.
Christoph Reeg
Seite 270
E Versionen
Aufschieben ist die groe Kunst, Dinge, zu denen man heute keine Lust hat, nchste Woche erst recht nicht zu a tun.
E.1 19.06.2005
neues Kapitel: Bentigte Software o Rekursion uberarbeitet MySQL: Nested Set etwas erweitert MySQL: IF Ausdrcke in MySQL u PHP: foreach-Schleife; break x, continue PHP: Variable Variablen PHP: Typensichere Vergleiche PHP: Warn-/Fehlermeldungen anzeigen PHP/MySQL: Tips und Tricks (Abfragen mit IN) PHP/MySQL: Einfgen mit automatischer ID u PHP/OO: Referenzsemantik mit Ubung PHP/OO: Ausblick PHP5 neues Kapitel: Sessions viel Kleinkram und Fehlerkorrekturen ein paar neue Ubungen zustzliche PS/PDF Versionen zum ausdrucken a
Christoph Reeg
Seite 271
E. Versionen
13.05.2002
E.2 13.05.2002
PHP, SQL und HTML Code hat Syntaxhervorhebung neues Kapitel uber Baumstrukturen in SQL neues Kapitel uber PHPDOC neues Kapitel uber das Parsen von URLs neues Kapitel uber Objektorientierung neues Kapitel uber XML neues Kapitel uber Templates PHP: Funktionen (s)printf und number format Beispielscripte Banner, SDA und Gstebuch hinzugefgt a u Kapitel uber HTTP-Header ergnzt a Kapitel Tips & Tricks erstellt HTML- und PDF-Versionen etwas verbessert Mailingliste fr neue Versionen u Autorenvorstellung viel Kleinkram
E.3 22.06.2001
Verbesserungsvorschlge und Korrekturen von Markus Maier und Jens Teich umgea setzt
E.4 27.04.2001
Kapitel zu regulren Ausdrcken hinzugefhrt a u u Vorwort erweitert Literaturverzeichnis komplettiert require(), include() besser erklrt a verschiedene Kapitel etwas erweitert
Christoph Reeg
Seite 272
E. Versionen
05.12.2000
E.5 05.12.2000
Ubungen zum Kapitel Fehlersuche hinzugefgt u ausfhrliche Erklrung der einzelnen Datentypen hinzugefgt u a u kleine Detailverbesserungen
E.6 27.11.2000
Funktion DATE FORMAT hinzugefgt u kleine Fehler korrigiert
E.7 19.10.2000
Kapitel PHP & HTTP erweitert print erklrt a Beispiel zur Erklrung der Tabellenstruktur hinzugefgt a u Erklrung zu Funktionen etwas uberarbeitet a
E.8 30.09.2000
u Ubung Ergebnis-Tabelle ausgeben I hinzugefgt u Ubung Ergebnis-Tabelle ausgeben II hinzugefgt ORDER BY-Erklrung erweitert a mysql_num_rows und mysql_errno mit aufgenommen Vorwort etwas uberarbeitet
E.9 27.07.2000
Grundzustand
Christoph Reeg
Seite 273
Index
*=, 85 ++, 85 +=, 85 , 85 ., 85 .=, 85 .php, 83 .php3, 83 .phtml, 83 ::, 206 <, 93 <=, 93 ==, 93 ===, 93 >, 93 >=, 93 &, 51 &&, 51 0x, 87 0, 87 =, 143 ABS, 50 Alias, 41 Spalten, 41 Tabellen, 41 ALL, 36 allgemeiner Konstruktor, 195 ALTER TABLE, 59 AND, 51, 86 Anfhrungszeichen, 83, 88 u Apache, 8 argument swapping, 109 Argumente referenzieren, 109 Argumente wiederverwenden, 109 Array, 89 Attribut, 197 Authentizieren, 142
AUTO INCREMENT, 31 AVG, 53 Basisklasse, 197 Batch-Betrieb, 27 Baumstruktur, 67 Bedingung, 42 BETWEEN, 47 Boolean, 88 break, 97, 98 Cache-Control, 148 CASE, 98 CONCAT, 51 Content-Disposition, 144 Content-Length, 145 Content-Transfer-Encoding, 145 Content-Type, 144, 146 Copyright, 2, 261 COS, 50 COUNT, 53 CREATE DATABASE, 30 CREATE TABLE, 31 create denition, 31 Data Base, 10 DATE FORMAT, 51, 52 Daten ndern, 59 a Daten ausgeben, 35 Daten einfgen, 34 u Daten lschen, 58 o Datenbank, 10 Datenbank anlegen, 30 Datenmodell, 15 Datenmodellierung, 14 DAYOFMONTH, 52 DAYOFWEEK, 52 DAYOFYEAR, 52 DB, 10
Christoph Reeg
Seite 274
Index
Index
DBMS, 10 DBS, 10 DDL, 13 dene(), 91 DEGREES, 50 DELETE FROM, 58 Denormalisieren, 21 Destruktor, 195, 198 dialogorientiert, 27 DISTINCT, 36 DO . . . WHILE, 96 Double, 88 Download, 1 DROP TABLE, 34 echo, 83 Eindeutigkeit, 15 ELSE, 94 ELSEIF, 94 Equi-Join, 54 ereg, 151 escaped, 88 Exklusiv-ODER, 86 expat, 224 Expires, 149 Float, 88 oor, 89 FOR, 96 FOREIGN KEY, 31 Fremdschlssel, 17 u function, 105 Funktionen, 50, 105 get parent class, 208 Gleich, 93 Grer, 93 o Grergleich, 93 o GROUP BY, 39 header, 141 Cache-Control, 148 Content-Disposition, 144 Content-Length, 145 Content-Transfer-Encoding, 145 Content-Type, 144, 146 Expires, 149
Last-Modied, 149 Location, 141 Not Found, 142 Unauthorized, 142 WWW-Authenticate, 142 Hexadezimalzahl, 87 HTTP-Status 200, 142 302, 141 401, 142 404, 142 IF, 93, 95 in SQL, 77 IN, 48 include(), 102 include once, 105 INSERT INTO, 34 Instanz, 197 Instanzvariablen in PHP, 201 Integer, 87 JavaDoc, 161 Join Equi, 54 Outer, 56 Joins, 54 kartesisches Produkt, 54 Klasse, 196, 197 Klassen kommentieren, 202 Klassendenition auslagern, 203 Klassenvariablen in PHP, 201 Kleiner, 93 Kleinergleich, 93 Kommentar, 87 MySQL, 29 Kommentieren, 161 Konstanten, 91 Konstruktor, 195, 198 Kurschlulogik, 86 Last-Modied, 149 LCASE, 51 LEFT, 51 LIKE, 47 LIMIT, 39
Christoph Reeg
Seite 275
Index
Index
Location, 141 LOWER, 51 LTRIM, 51 MAX, 53 Mehrfach-Argumentierung, 109 Methode, 197 MIN, 53 MOD, 50 MONTH, 52 mysql, 27 MySQL Nutzer, 66 mysql close(), 133 mysql connect(), 132 mysql errno(), 135 mysql error(), 135 mysql fetch array(), 134 mysql fetch row(), 134 mysql eld name(), 247 mysql insert id(), 135 mysql num elds(), 247 mysql num rows(), 135 mysql query(), 133 Nested Set, 69 NICHT, 51, 86 Normalform, 16 1., 16 2., 18 3., 18 4., 20 5., 21 NOT, 43, 51 Not Found, 142 NULL, 31, 45, 46, 58 Objekt, 196, 197 Objekte in PHP, 203 Objektorientierte Programmierung, 192 Objektorientierung, 192 ODER, 51, 86 Oktalzahl, 87 OO, 192 OO in PHP, 200 OOP, 192 Open Publication License, 261 deutsch, 263
englisch, 261 Operatoren, 85 OR, 51, 86 ORDER BY, 37 Outer-Join, 56 parent, 206 PCRE, 151 PHP, 82 PHP 5, 209 PHP objektorientiert, 200 PHP-Code-Markierungen, 83 PHPDoc, 161 PHPMyAdmin, 66 PI, 50 POW, 50 preg, 151 Primrschlssel, 13, 17 a u PRIMARY KEY, 31 printf, 106 private, 196 protected, 196 Prozedaten, 15 public, 196 RAND, 50 RAND(), 65 Redundanz, 14 reference denition, 31 Referenzierung in PHP, 203 regulre Ausdrcke, 151 a u regular expressions, 151 Rekursion, 114 Relationen, 24 require, 102 require once, 105 REVERSE, 51 ROUND, 50 Schlssel, 13, 17 u Script, 82 SELECT, 35 Self-Join, 55 Serverseitig, 82 Sessions, 217 SHOW, 33 Spalten-Alias, 41
Christoph Reeg
Seite 276
Index
Index
sprintf, 110 SQRT, 50 Standardkonstruktor, 195 static, 206 Stil, 111 Stilistisches, 111 Streifendiagramm, 21 String, 88 Strings verbinden, 85 SUM, 53 SWITCH, 98 Tabelle andern, 59 Tabelle lschen, 34 o Tabellen Alias, 41 Table xx doesnt exist, 64 Templates, 234 TRUNCATE, 50 UCASE, 51 UND, 51, 86 Ungleich, 93 UNIQUE, 31 UNIX TIMESTAMP, 52 UPDATE, 59 UPPER, 51 URL, 1, 149 USE, 28, 30 VAR DUMP, 157 Variable Variablen, 92 Vererbung, 194 Vergleiche mit 0, 93 Version, 1 Webserver, 8 WEEK, 52 WHERE, 42 where denition, 42 WHILE, 95 XML, 224 XML-Dokumente, 224 XML-Parser, 224 XOR, 86 YEAR, 52
Christoph Reeg
Seite 277