Sie sind auf Seite 1von 320

Philipp Rieber

PHP & MySQL


Schnelleinstieg

Programmieren lernen in 14 Tagen


Einfach und ohne Vorkenntnisse
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Natio-
nalbibliografie; detaillierte bibliografische Daten sind im Internet über <http://dnb.d-
nb.de> abrufbar.

ISBN 978-3-7475-0396-6
1. Auflage 2022

www.mitp.de
E-Mail: mitp-verlag@sigloch.de
Telefon: +49 7953 / 7189 - 079
Telefax: +49 7953 / 7189 - 082

© 2022 mitp-Verlags GmbH & Co. KG, Frechen

Mit freundlicher Genehmigung der HfG Schwäbisch Gmünd verwenden einige Abbildun-
gen Emojis von OpenMoji (https://openmoji.org) – dem Open-Source Emoji- und Icon-
Projekt.

Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Ver-
wertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung
des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen,
Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektro-
nischen Systemen.
Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in die-
sem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass
solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu
betrachten wären und daher von jedermann benutzt werden dürften.

Lektorat: Janina Bahlmann


Sprachkorrektorat: Claudia Fluor
Covergestaltung: Janina Bahlmann, Christian Kalkert
Covergrafik & Icons: Tanja Wehr, sketchnotelovers
Satz: Petra Kleinwegen
Inhalt

Inhalt
Einleitung
E.1 Programmieren lernen in 14 Tagen ............................................. 11
E.2 Der Aufbau des Buchs .............................................................. 11
E.3 Programmtexte und Lösungen zum Download .............................. 12
E.4 Fragen und Feedback ............................................................... 12

Erste Schritte mit PHP


1.1 Wofür wird PHP eingesetzt? ...................................................... 13
1.2 Stärken von PHP ..................................................................... 22
1.3 Schwächen von PHP ................................................................ 23
1.4 Was ist für die Entwicklung mit PHP notwendig? .......................... 24
1.5 Quelltext und PHP-Interpreter ................................................... 25
1.6 Kommandozeile nutzen ............................................................ 26
1.7 PHP installieren ...................................................................... 31
1.8 Quelltext-Editor verwenden: Visual Studio Code ........................... 36
1.9 Das erste PHP-Skript ausführen ................................................. 38
1.11 Wie entstand PHP? .................................................................. 41
1.12 Übungen ............................................................................... 42

Variablen, Datentypen und Konstanten


2.1 Daten in einer Variablen erfassen .............................................. 43
2.2 Einfache Datentypen ............................................................... 50
2.3 Der spezielle Datentyp: NULL .................................................... 58
2.4 Der vielseitige Datentyp: Array .................................................. 59
2.5 Datentypen umwandeln ............................................................ 63
2.6 Programmstrukturen ................................................................ 66

5
Inhalt

2.7 Konstanten ............................................................................ 67


2.8 Übungen ............................................................................... 70

Programmablauf mit Kontrollstrukturen steuern


3.1 Programmablauf verzweigen ..................................................... 73
3.2 Bedingungen formulieren ......................................................... 81
3.3 Programmabschnitte mit Schleifen wiederholen ........................... 85
3.4 Alternative Syntax für Kontrollstrukturen .................................... 94
3.5 Übungen ............................................................................... 95

Programmierfehler und PHP-Konfiguration


4.1 Programmierfehler ................................................................... 97
4.2 PHP konfigurieren: die php.ini-Datei .......................................... 99
4.3 Fehlerstufen ........................................................................... 102
4.4 Fehlersichtbarkeit einstellen ..................................................... 103
4.5 Übungen ............................................................................... 104

Funktionen
5.1 Native Funktionen aus der PHP-Bibliothek verwenden ................... 105
5.2 Eigene Funktionen definieren .................................................... 122
5.3 Übungen ............................................................................... 127

6
Inhalt

Webseiten entwickeln und veröffentlichen


6.1 Was geschieht beim Abruf einer Webseite? .................................. 129
6.2 Webserver auf dem eigenen Computer betreiben .......................... 132
6.3 HTML-Grundgerüst ................................................................... 138
6.4 Anfragen und Antworten mit dem HTTP-Protokoll ......................... 140
6.5 Webspace mieten und Webseite veröffentlichen ........................... 146
6.6 Übungen ............................................................................... 148

Dynamische Webseiten und Formulare


7.1 $_GET: Daten aus dem Query-String der URL ............................... 149
7.2 Formulardaten im Query-String der URL übermitteln ..................... 158
7.3 Einsatzgebiete der GET-Methode ................................................ 165
7.4 $_POST: Formulardaten unsichtbar übermitteln ............................ 166
7.5 Vergleich zwischen GET und POST .............................................. 169
7.6 Übungen ............................................................................... 170

Mit Dateien arbeiten


8.1 Quelltext in mehreren Dateien strukturieren ................................ 173
8.2 Dateien schreiben und lesen ..................................................... 180
8.3 Dateien über das Internet laden ................................................ 182
8.4 Datei-Uploads: $_FILES ............................................................ 184
8.5 Datei-Zugriffsrechte ................................................................ 190
8.6 Übungen ............................................................................... 192

7
Inhalt

Cookies und Sessions


9.1 Cookies ................................................................................. 195
9.2 Sessions: Benutzersitzungen ..................................................... 202
9.3 Übungen ............................................................................... 210

Einstieg in die objektorientierte Programmierung (OOP)


10.1 Grenzen der prozeduralen Programmierung .................................. 211
10.2 Grundbegriffe der »OOP« .......................................................... 212
10.3 Vererbung .............................................................................. 221
10.4 Ausnahmen (Exceptions) .......................................................... 224
10.5 Native Objektorientierung in PHP .............................................. 227
10.6 Übungen ............................................................................... 229

Datenverwaltung mit MySQL


11.1 Was ist eine relationale Datenbank? ........................................... 231
11.2 Was ist ein Datenbankmanagementsystem? ................................. 232
11.3 Was ist SQL? .......................................................................... 233
11.4 Erste Schritte mit MySQL .......................................................... 234
11.5 Kommentare ........................................................................... 241
11.6 Datenbanken .......................................................................... 241
11.7 Tabellen ................................................................................ 243
11.8 Bezeichner ............................................................................. 247
11.9 Daten schreiben, lesen, ändern und löschen ................................ 248
11.10 Übungen ............................................................................... 253

8
Inhalt

Fortgeschrittene Daten­bankabfragen mit MySQL


12.1 Grafische Datenbankverwaltung mit phpMyAdmin ......................... 255
12.2 MySQL beim Webhoster ............................................................ 259
12.3 Fortgeschrittene Datenabfragen ................................................ 260
12.4 Alias-Namen für Tabellenspalten ................................................ 264
12.5 SQL-Funktionen ...................................................................... 265
12.6 Performance und Indizes .......................................................... 269
12.7 Übungen ............................................................................... 272

PHP und MySQL kombinieren


13.1 PHP mit MySQL verbinden ........................................................ 273
13.2 Beispiel: MySQL-Version abfragen .............................................. 278
13.3 Beispiel: Benutzer-Accounts ..................................................... 278
13.4 Passwörter sicher speichern ...................................................... 284
13.5 Sicherheitslücke: SQL-Injection ................................................. 285
13.6 Übungen ............................................................................... 288

Abschlussprojekt: Ein Blog programmieren


14.1 Installation ............................................................................ 289
14.2 Übersicht: Das fertige Blog ....................................................... 290
14.3 Verwendete Techniken ............................................................. 291
14.4 So funktioniert das Blog .......................................................... 296
14.5 Anwendungsbeispiele .............................................................. 301
14.6 Ausblick: Was kommt als Nächstes? ............................................ 308
14.7 Übungen ............................................................................... 311

Stichwortverzeichnis

9
Einleitung
E.1 Programmieren lernen in 14 Tagen
Mit diesem Buch haben Sie sich für einen einfachen, praktischen und fundier-
ten Einstieg in die Welt der Programmierung entschieden. Sie lernen ohne
unnötigen Ballast alles, was Sie wissen müssen, um PHP und MySQL effektiv
für Projekte in Ihrem Berufs- und Interessensgebiet einzusetzen.
Wenn Sie Zeit genug haben, können Sie jeden Tag ein neues Kapitel durchar-
beiten und so innerhalb von zwei Wochen Programmieren lernen. Alle Erklä-
rungen sind leicht verständlich und setzen keine Vorkenntnisse voraus. Am
besten lesen Sie das Buch neben der Computer-Tastatur und probieren die
Programmbeispiele und Übungen gleich aus. Sie werden schnell erste Erfolge
erzielen und Freude an der Programmierung finden.

E.2 Der Aufbau des Buchs


Die Kapitel bauen Schritt für Schritt aufeinander auf. Das Buch beschreibt zu-
nächst die Einsatzgebiete von PHP und die Einrichtung Ihres Computers zur
Entwicklung des ersten PHP-Programms. Anschließend lernen Sie wichtige
Sprachelemente und Einstellungen von PHP kennen. Sie werden vertraut mit
der Verwendung von Funktionen, der Entwicklung und Veröffentlichung dy-
namischer Webseiten, dem Lesen und Schreiben von Dateien, der Anbindung
eines externen Webservices sowie dem Einsatz von Cookies und Sessions.
Nach einer Einführung in die objektorientierte Programmierung erlernen
Sie den Umgang mit einem MySQL-Datenbanksystem und der Datenbank-
sprache SQL. Das letzte Kapitel vereint alles Gelernte in einem Projekt zur
Programmierung eines Weblogs und gibt Anregungen zur Weiterentwicklung
Ihrer Programmierkenntnisse nach dem Schnelleinstieg.
Gelegentlich stoßen Sie auf kleine Aufgaben oder Zwischenfragen, die als
Lernaktivierung gedacht sind. Ihr Tagespensum schließt mit praktischen Pro-
grammierübungen, in denen Sie Ihr neu gewonnenes Wissen vertiefen kön-
nen. Die Lösungen zu diesen Übungen und die Antworten zu den Zwischen-
fragen stehen in einem Online-Kapitel zum Download zur Verfügung. Mehr
dazu im nächsten Abschnitt.

11
Einleitung

Am Ende des Buchs finden Sie ein Stichwortverzeichnis, das Ihnen hilft, be-
stimmte Themen im Buch schneller zu finden.

E.3 Programmtexte und Lösungen zum Download


Das Buch enthält viele kleine Beispielprogramme. Sie sind als »Starterpro-
jekte« gedacht und sollen Sie ermuntern, den Code weiterzuentwickeln und
selbst etwas Neues auszuprobieren.
Der Code aller Beispielprogramme sowie die Lösungen zu den Übungen und
Zwischenfragen stehen Ihnen auf der Webseite des Verlags unter www.mitp.
de/0395 zum Download zur Verfügung.
Dort finden Sie außerdem ein praktisches Glossar mit den wichtigsten Fach-
begriffen.

E.4 Fragen und Feedback


Unsere Verlagsprodukte werden mit großer Sorgfalt erstellt. Sollten Sie trotz-
dem einen Fehler bemerken oder eine andere Anmerkung zum Buch haben,
freuen wir uns über eine direkte Rückmeldung an lektorat@mitp.de.
Falls es zu diesem Buch bereits eine Errata-Liste gibt, finden Sie diese unter
www.mitp.de/0395 im Reiter Downloads.
Wir wünschen Ihnen viel Erfolg und Spaß bei der Programmierung mit PHP
und MySQL!
Philipp Rieber und das mitp-Lektorat

12
Erste Schritte mit PHP
Dieses Kapitel gibt eine praxisnahe Einführung in eine der populärsten Pro-
grammiersprachen des Internets: PHP. Um die 80 % aller Webseiten werden
von PHP erzeugt. Das Spektrum reicht von Internetpräsenzen, Blogs, Porta-
len, Online-Shops und spezialisierten Web-Anwendungen bis zu Schnittstel-
len für die Datenverarbeitung von Mobile Apps und dem Internet of Things.
PHP ist für Hobby-Anwender und den professionellen Einsatz in geschäfts-
kritischen Softwaresystemen gleichermaßen geeignet. PHP-Kenntnisse eröff-
nen Ihnen die Welt hinter den graphischen Benutzeroberflächen des Internets
und unzählige Möglichkeiten, um selbst privat oder beruflich in die Web­
entwicklung einzusteigen.
Nach einem Überblick zu den Einsatzgebieten von PHP führe ich Sie in die-
sem Kapitel zur erfolgreichen Ausführung Ihres ersten PHP-Programms auf
dem eigenen Computer. Dabei erlernen Sie wichtige Grundkenntnisse und
die Einrichtung einer Entwicklungsumgebung. Durch erste Programmbei-
spiele machen Sie sich »hands-on« an der Tastatur Ihres Computers mit den
Grundeigenschaften von PHP vertraut. Zum Abschluss des Kapitels erhalten
Sie einen Überblick zur Entstehungsgeschichte von PHP.
Wo vorhanden, verwendet dieses Buch deutsche Fachbegriffe. Da die
englischen Entsprechungen für Recherchen, Fehlersuchen oder in der
Kommunikation mit anderen Programmierern unerlässlich sind, ma-
che ich Sie nebenbei auch mit den englischen Begriffen vertraut.

1.1 Wofür wird PHP eingesetzt?


PHP ist eine kostenlose, universell einsetzbare Programmiersprache. Sieht
man von Nischen wie der Programmierung von Alexa Skills oder Desktop-
Programmen ab, konzentriert sich der Einsatz auf drei Einsatzgebiete:

13
Kapitel 1 Erste Schritte mit PHP

ƒ Erzeugung dynamischer Webseiten


ƒ Bereitstellung von Webservices
ƒ Kommandozeilenprogramme
Die nächsten Abschnitte erklären Grundlagen zu den verschiedenen Gebie-
ten. Stellen Sie sich zur Veranschaulichung eine fiktive Zeitungsredaktion vor,
die eine Präsenz im Internet aufbaut. Schrittweise entwickelt sich die Inter-
netpräsenz von einer reinen Text-Webseite über eine ansehnlichere HTML-
Webseite zu einer fortschrittlichen dynamischen Webseite. Anschließend
veröffentlicht die Zeitung ihre eigene Mobile App und automatisiert wieder-
kehrende Aufgaben.

1.1.1 PHP zur Erzeugung dynamischer Webseiten


Was ist der Unterschied zwischen einer statischen und einer dynamischen
Webseite? Der Abruf einer statischen Webseite von einer Internetadresse im
Web-Browser liefert das immer gleiche, »statische« Ergebnis. Eine dynami-
sche Webseite hingegen wird erst im Zuge des Abrufs erzeugt. Dabei werden
Inhalte aus verschiedenen Quellen wie Datenbanken oder externen Web-
diensten zusammengetragen und zur Anzeige aufbereitet. Eingaben des Be-
nutzers oder dessen Kontext (Identität, Standort, Tageszeit etc.) können den
Inhalt beeinflussen.
Eine fiktive Zeitungsredaktion schreibt für ihre ersten Schritte zu einer Inter-
netpräsenz alle Artikel in die einfache Textdatei articles.txt und veröffent-
licht sie auf einem Computer im Internet, dem Webserver.

Abb. 1.1: Die Textdatei articles.txt im Text-Editor

Interessierte Leser rufen die Datei anhand der passenden Internetadresse


(URL, Uniform Resource Locator) in einem Browser auf. Die Datei wird vom
Webserver auf den eigenen Computer, den Client, übertragen und im Brow-
serfenster angezeigt. Solange die Redaktion die Text-Datei nicht durch eine
aktualisierte Version ersetzt, führt jeder weitere Aufruf zur immer gleichen,
»statischen« Anzeige des Inhalts – auch für jeden anderen Besucher.

14
Wofür wird PHP eingesetzt? 1.1

Abb. 1.2: Statische Text-Webseite articles.txt im Browser

Da die Gestaltungsmöglichkeiten mit reinem Text begrenzt sind, wechselt


die Redaktion auf die Nutzung der Auszeichnungssprache HTML (Hypertext
Markup Language).

Abb. 1.3: Die HTML-Datei articles.html im Text-Editor

HTML ermöglicht die Strukturierung der Inhalte mit Hilfe von maschinen­
lesbaren Hinweisen, den HTML-Tags. Die HTML-Tags markieren einge-
schlossene Inhalte dabei mit einer gewünschten Bedeutung, zum Beispiel
Überschrift, Link, Liste etc. Dies nennt man semantische Strukturierung. Die
Redaktion verwendet im Beispiel Elemente für eine Überschrift ersten Grades
(heading 1 = h1), Hyperlinks (anchor = a) und eine ungeordnete Liste (unor­dered
list = ul) mit Listenelementen (list item = li). Die Auszeichnungen beginnen
mit einem öffnenden Tag <element> und enden mit einem schließenden Tag
</element>:

<h1>Überschrift ersten Grades</h1>


<a href="https://www.google.de">Link zu Google</a>
<ul>
<li>Erstes Listenelement</li>
<li>Zweites Listenelement</li>
</ul>

HTML-Tags bleiben für den menschlichen Betrachter unsichtbar. Der Brow-


ser versteht jedoch die versteckten Auszeichnungen, stellt die Inhalte entspre-
chend dar und schafft einfache Interaktion durch klickbare Links.

15
Kapitel 1 Erste Schritte mit PHP

Abb. 1.4: Statische HTML-Webseite articles.html im Browser

Eine genaue Kontrolle über Formatierungen (Farben, Schriftgrößen,


Positionierungen usw.) ermöglicht die ergänzende Formatierungs-
sprache CSS (Cascading Style Sheets). Die CSS-Formatierungsanga-
ben sind für den Betrachter ebenso unsichtbar wie HTML-Tags, der
Browser nutzt sie jedoch zur Anpassung der Darstellung. Mehr zu
HTML und CSS erfahren Sie z.B. unter https://wiki.selfhtml.org.
Abbildung 1.5 zeigt den Kreislauf aus Anfrage des Browsers an einen Webser-
ver und dessen Antwort. Dieser Kreislauf wiederholt sich bei jeder angefrag-
ten Webseite.

Abb. 1.5: Der Kreislauf aus HTTP-Anfrage und -Antwort

Bald kommt in der Zeitungsredaktion eine neue Idee auf: Sie möchte das
aktuelle Tagesdatum ohne tägliche manuelle Bearbeitung einblenden. Doch
HTML kann keine Inhalte erzeugen und hat keinen Zugriff auf eine Uhr mit
dem aktuellen Datum. Für diesen Zweck ist Programmierlogik erforderlich.
Die Zeitung engagiert eine Webagentur. Die Agentur aktiviert PHP auf dem
Webserver, benennt articles.html in articles.php um und beginnt PHP-
Programmlogik zur Anzeige des aktuellen Datums in das HTML einzubetten:
<h1>Nachrichten</h1>
<p>Heute ist der <?php echo date('d.m.Y'); ?>!</p>
<ul>...</ul>

Im Gegensatz zur HTML-Datei liefert der Webserver die PHP-Datei nicht


direkt an den Browser zurück, sondern lässt zunächst den enthaltenen PHP-

16
Wofür wird PHP eingesetzt? 1.1

Programmcode ausführen. Alle PHP-Bereiche werden durch die dabei gene-


rierten Ausgaben ersetzt.

<?php Hier beginnt PHP-Programmlogik


echo Anweisung zur Ausgabe
date('d.m.Y') Aufruf der in PHP eingebauten Funktion date(), die das ak-
tuelle Datum in einem gewünschten Format liefert. Im Beispiel
wird das Format d.m.Y = day.month.Year = Tag.Monat.Jahr
verwendet.
; Ende der Anweisung
?> Hier endet PHP-Programmlogik

Aus dem bestehenden HTML und den durch PHP dynamisch ergänzten
Inhalten ergibt sich die gewünschte Webseite, die an den Browser zurück-
geschickt wird. Bei einem Abruf der Webseite am 13. März 2022 lautet der
generierte Inhalt:
<h1>Nachrichten</h1>
<p>Heute ist der 13.03.2022!</p>
<ul>...</ul>

Aufgabe 1

Können Sie die Dokumentation zur PHP-Funktion date() auf https://


www.php.net finden?

Aus Sicht des Browsers auf dem eigenen Computer, des Clients, erscheint die
empfangene Webseite genauso statisch wie zuvor. Die dynamische Erzeugung
erfolgte bereits serverseitig auf dem entfernten Webserver. Eine Installation
von PHP ist daher nur auf dem Webserver erforderlich, nicht auf den Compu-
tern der Webseiten-Besucher. Der Browser kümmert sich wie zuvor nur um
die Darstellung, unabhängig von der Entstehung des Inhalts.

Abb. 1.6: Der Kreislauf aus Anfrage und Antwort mit PHP

17
Kapitel 1 Erste Schritte mit PHP

Die Pflege der Zeitungsartikel in der Datei durch die Redakteure erfordert
HTML-Kenntnisse, Absprachen zwischen den Redakteuren und ständige
Übertragungen neuer Versionen auf den Webserver. Mit fortgeschrittenen
Methoden der PHP-Entwicklung kann die Webagentur den nächsten Wunsch
der Zeitungsredaktion realisieren: Eine Vereinfachung der Artikel-Verwal-
tung, die von den Redakteuren keine technischen Kenntnisse mehr erfordert.
Die Artikelinhalte werden nicht länger in der Datei articles.php gepflegt,
sondern in eine Datenbank ausgelagert. PHP kann die Artikel zum Zeitpunkt
des Abrufs der Webseite aus der Datenbank lesen und ähnlich wie zuvor das
Tagesdatum dynamisch in das HTML einbauen. Weitere Abrufe der Webseite
wiederholen die Generierung der Inhalte und damit den Abruf der Artikel aus
der Datenbank. Neu in die Datenbank eingepflegte Artikel erscheinen somit
automatisch auf der Webseite.
Zur Erstellung neuer Artikel durch Redakteure ergänzt die Webagentur einen
durch Login geschützten Bereich mit einem Eingabeformular für neue Arti-
kel. PHP überträgt die Eingaben in die Datenbank. Außer der Browserbedie-
nung benötigen die Redakteure keine weiteren technischen Kenntnisse.

Aufgabe 2

Was geschieht, wenn Sie eine PHP-Datei direkt im Browser öffnen? Zie-
hen Sie zum Ausprobieren eine PHP-Datei in das Browserfenster (Drag &
Drop). Vergleichen Sie mit Dateien vom Typ TXT, HTML, ZIP und PDF.

1.1.2 PHP zur Bereitstellung von Webservices


Die fiktive Zeitungsredaktion aus dem vorigen Abschnitt stellt einen großen
Anteil an Smartphone-Besuchern auf ihrer Webseite fest. Sie erweitert das
Angebot daher um eine eigene Mobile App. Diese folgt bezüglich Gestaltung
und Bedienung anderen Gesetzen als eine Webseite für den Browser. Die App
ist nicht am Abruf einer HTML-Webseite interessiert, sondern nur an den rei-
nen Artikeldaten, um damit die Gestaltungselemente einer Mobile App zu fül-
len. In der ersten Version baut die Webagentur einen einfachen »Nachrichten-
Ticker« zur Anzeige der Artikel mit Datum und Überschrift, der sich durch
Drücken einer Schaltfläche aktualisieren lässt.
Die Programmierung der Mobile App selbst erfolgt nicht in PHP, sondern in
einer Programmiersprache für die jeweilige Plattform, z.B. Java für Android-
oder Swift für iPhone-Apps. Wie die meisten Apps muss sie im Hintergrund
in Kontakt mit einem zentralen Service stehen, um aktuelle Daten abzurufen

18
Wofür wird PHP eingesetzt? 1.1

oder Benutzereingaben dorthin zu senden. Den unsichtbaren Kommunika-


tionspartner im Hintergrund bezeichnet man als _Backend_. Eine dem Be-
nutzer zugewandte Anwendung wie die Webseite oder die Mobile App heißt
_Frontend_.
Zur Kommunikation greift die App (das Frontend) ähnlich dem Browser an-
hand einer URL auf einen Webserver (das Backend) zu, etwa zum Abruf der
neuesten Artikel beim Start der App oder bei einer gewünschten Aktualisie-
rung. Statt einer HTML-Webseite mit vielen irrelevanten Bereichen ist die
App an einem maschinenlesbaren Format zum Datenaustausch interessiert.
Ein verbreiteter Standard dafür ist JSON (ausgesprochen: Jason). Die über-
mittelten Daten für den Nachrichten-Ticker könnten im JSON-Format so
aussehen:
[
{
"title": "ElePHPant entdeckt!",
"link": https://www.php.net/elephpant
},
{
"title": "PHP 8 veröffentlicht!",
"link": "https://www.php.net/releases/8.0/de"
},
]

Da sich mit PHP neben HTML auch jede andere Art von Ausgabe erzeugen
lässt, kann PHP der Mobile App als Backend dienen. Statt HTML gibt das
PHP-Skript JSON aus:
<?php
$articles = Abruf der Artikel aus der Datenbank;
echo json_encode($articles);
?>

<?php Beginn der PHP-Programmlogik


$articles = ... Definiert die Variable $articles – eine Art Daten-
Container für die Zeitungsartikel. Die Variable ist
eine Referenz zum späteren Zugriff auf die Daten.
Abruf der Artikel aus Da- Steht stellvertretend für Programmcode zum Abruf
tenbank der Daten aus einer Datenbank.

19
Kapitel 1 Erste Schritte mit PHP

; Ende der Anweisung


echo Anweisung zur Ausgabe
json_encode($articles) Eine in PHP eingebaute Funktion, die die über-
gebenen Zeitungsartikel-Daten im JSON-Format
zurückliefert.
; Ende der Anweisung
?> Ende der PHP-Programmlogik

Mit Hilfe von PHP stellt der Webserver die Artikel als Service über das In-
ternet zur Verfügung – als Webservice. Im Vergleich zu einer Webseite für
menschliche Benutzer richtet sich das Angebot eines Webservice an andere
Software – unabhängig vom endgültigen Verwendungszeck.
Später entscheidet sich die Zeitung zur Veröffentlichung ihres Webservice. Ein
Newsletter-Versender kann die verfügbaren Daten zum Beispiel unabhängig
von der Zeitungs-Webseite nutzen, um seinen wöchentlichen E-Mail-News-
letter automatisiert mit Inhalten der Zeitung anzureichern. In umgekehrter
Weise erweitert die Zeitung ihr eigenes Angebot um einen Wetterbericht, des-
sen Daten sie vom Webservice eines Drittanbieters abruft.
Ein Webservice wird oft auch technischer als Web-API (Web Ap-
plication Programming Interface) bezeichnet – eine Programmier-
Schnittstelle über das Internet.
Weitere Beispiele für Webservices beziehungsweise Web-APIs: Wäh-
rungs- und Aktienkurse, Fahrpläne, SMS- und E-Mail-Versand, Daten zu
Sportereignissen, Steuerung von Smart-Home-Geräten, Bonitätsauskünfte,
Validierung von Postadressen und vieles mehr. Auch die großen Tech-Un-
ternehmen wie Twitter, Facebook, Google, Amazon etc. stellen Web-APIs
zur Integration in eigene Angebote bereit.
Mit Hilfe von PHP können Sie sowohl eigene Webservices bereitstellen (pro-
duzieren) als auch fremde Webservices nutzen (konsumieren). Mit entspre-
chenden Maßnahmen kann ein Webservice auch gegen Bezahlung angeboten
werden.

1.1.3 Kommandozeilenprogramme mit PHP


Ein Kommandozeilenprogramm stellt die einfachste Form eines PHP-Pro-
gramms dar. Ist PHP auf einem Computer installiert, kann ein solches Pro-
gramm darauf ausgeführt werden. Ein Browser oder Webserver spielt dabei
keine Rolle, die Ausführung des Programms erfolgt rein clientseitig bezie-

20
Wofür wird PHP eingesetzt? 1.1

hungsweise lokal. PHP-Kommandozeilenprogramme können vielfältige Auf-


gaben übernehmen und dabei sichtbare Ausgaben erzeugen. Betrachten Sie
das Programm in der Datei dice.php zur Simulation eines Würfels:
Sie haben eine <?php echo random_int(1, 6); ?> gewürfelt.

Listing 1.1: PHP-Kommandozeilenprogramm dice.php

<?php Beginn der PHP-Programmlogik


echo Anweisung zur Ausgabe
random_int(1, 6) Eine in PHP eingebaute Funktion, die eine zufällige Ganzzahl
(random integer) im gewünschten Bereich zwischen 1 und 6
zurückliefert.
; Ende der Anweisung
?> Ende der PHP-Programmlogik

Abb. 1.7: Wiederholte Ausführung von dice.php

Wiederkehrende Aufgaben mit Cronjobs erledigen


In Webprojekten spielen PHP-Kommandozeilenprogramme zur Ausführung
wiederkehrender Aufgaben eine wichtige Rolle, sogenannte Cronjobs. Dabei
wird das Programm entsprechend einem gewünschten Zeitplan auf dem Web-
server aufgerufen.
Die fiktive Zeitungsredaktion aus dem vorigen Abschnitt möchte interessier-
ten Lesern abends eine Übersicht der Tagesnachrichten per E-Mail anbieten.
Statt eines mühsamen manuellen Versands richtet die Webagentur auf der
Webseite ein Formular zur Anmeldung für den Service ein. Die eingegebe-
nen E-Mail-Adressen werden durch PHP in einer Datenbank gespeichert. Die
Agentur konfiguriert auf dem Webserver einen Cronjob, der jeden Abend um
20 Uhr ein PHP-Kommandozeilenprogramm aufruft. Das Programm liest die

21
Kapitel 1 Erste Schritte mit PHP

Abonnenten und Artikel des letzten Tags aus der Datenbank und versendet
die dynamisch erstellte Artikelübersicht per E-Mail.
Weitere Anwendungsbeispiele für Cronjobs:
ƒ Erinnerungs-SMS an Kunden einer Autowerkstatt für einen bevorste-
henden Termin.
ƒ Nächtliche Generierung von Verkaufsberichten für einen Online-Shop.
ƒ Regelmäßige Löschung nicht mehr benötigter Daten gemäß DSGVO.

Hilfsprogramme für die Entwicklung


Es gibt auch PHP-Kommandozeilenprogramme zur Unterstützung der Ent-
wicklung mit PHP. Der Paketmanager Composer etwa ist ein in PHP geschrie-
benes Kommandozeilenprogramm, mit dessen Hilfe hunderttausende kosten-
lose PHP-Pakete zur Unterstützung eigener Projekte geladen werden können.
Im weiteren Verlauf des Buchs erlernen Sie zunächst die Grundlagen anhand
kurzer PHP-Kommandozeilenprogramme. Anschließend kommt die server-
seitige Web-Programmierung zur Erzeugung dynamischer Webseiten und
Webservices zum Einsatz. Das Abschlussprojekt im letzten Kapitel verwendet
Composer-Pakete zur PDF-Generierung und zum E-Mail-Versand.

1.2 Stärken von PHP


ƒ PHP ist kostenlos.
ƒ PHP läuft auf allen gängigen Plattformen, darunter Windows, macOS und
Linux/Unix.
ƒ PHP ist mit allen gängigen Webservern einsetzbar, darunter nginx, Apache
und IIS.
ƒ PHP kann an alle gängigen Datenbanken angebunden werden. Am belieb-
testen ist die Kombination mit den kostenlosen Datenbanksystemen My­
SQL oder PostgreSQL.
ƒ Breite Unterstützung durch Webhoster, dadurch große Auswahl und güns-
tige Preise, um Projekte online zu stellen.
ƒ Leichte Erlernbarkeit durch: Spezialisierung auf Webprogrammierung;
Syntax ähnelt bekannten Sprachen; fehlertolerantes Verhalten; kostenlose
Verfügbarkeit; zugängliches Wissen; Einbettung von PHP-Code ist intuitiv.
ƒ Sehr umfangreiche eingebaute Funktionsbibliotheken, bei Bedarf erwei­
terbar.

22
Schwächen von PHP 1.3

ƒ Sehr fortschrittliche Features für den professionellen Einsatz. Da diese op-


tional sind, dennoch geringe Einstiegshürden.
ƒ Sehr große, weltweite Community, die umfangreiches Wissen und Unter-
stützung über Blogs, Foren, Video-Tutorials, Online-Kurse, Chats, Bücher,
User Groups, Consulting, Workshops, Konferenzen usw. garantiert.
ƒ Großes Ökosystem:
ƒ Vielfältige, kostenlose PHP-Standardsoftware, etwa für Shops (Magento,
Sylius), Content-Management-Systeme (Wordpress, TYPO3, Joomla)
oder Foren (phpBB, vBulletin).
ƒ Vielfältige, kostenlose Frameworks wie Symfony, Laminas oder Laravel
zur Unterstützung bei der Erstellung individueller Anwendungen.
ƒ Paketmanager Composer (https://getcomposer.org) zur Installation
hunderttausender nützlicher und kostenloser PHP-Software-Pakete.
Mit hunderten von Millionen PHP-basierter Webseiten und Webservices,
einer riesigen Community, der Fähigkeit sich kontinuierlich weiterzuentwi-
ckeln und neue Trends aufzugreifen, wird PHP auch in Zukunft eine große
Rolle in der Webprogrammierung spielen.
Die große Verbreitung von PHP führt zu einer großen Nachfrage nach
PHP-Entwicklern auf der ganzen Welt. Unter https://stackoverflow.com/
jobs?q=php können Sie z.B. einen Blick in den aktuellen Job-Markt werfen.

1.3 Schwächen von PHP


Als organisch gewachsene Sprache weist PHP einige Altlasten auf, etwa In-
konsistenzen in der Benennung von Sprachelementen oder unerwartetes Ver-
halten in speziellen Situationen. Glücklicherweise verringert sich die Anzahl
dieser Probleme mit jeder neuen PHP-Version oder sie spielen im Alltag keine
Rolle.
Im nächsten Abschnitt lernen Sie PHP als Skriptsprache kennen. Skriptspra-
chen haben im Vergleich zu klassischen Programmiersprachen wie Java oder
C einen Geschwindigkeitsnachteil bei der Programmausführung. Durch all-
gemein gestiegene Rechenleistungen und ständige Optimierungen in neuen
PHP-Versionen wurde der Unterschied deutlich verringert und spielt nur in
speziellen Anwendungsfällen eine Rolle.
Die niedrigen Einstiegshürden zu PHP sind Stärke und Schwäche zugleich.
Es ist einfach, in die PHP-Programmierung einzusteigen und Projekte online
zu stellen. Unzureichendes Hintergrundwissen kann jedoch zu Sicherheits-
problemen oder Schwierigkeiten bei der Instandhaltung der Software führen.

23
Kapitel 1 Erste Schritte mit PHP

Die große Verbreitung von PHP trägt auch zu einer Verbreitung zweifelhafter
Programmierpraktiken bei, weshalb der Sprache gelegentlich ein schlechter
Ruf anhaftet. PHP selbst ist jedoch sehr sicher und mit sauberer Programmie-
rung bestehen keine Bedenken. Im Laufe des Buchs lernen Sie den sicheren
Umgang mit einigen typischen Sicherheitsproblemen kennen.
Keine Schwäche der Programmiersprache PHP selbst ist die Tatsache, dass
JavaScript als vormals nur clientseitig im Browser eingesetzte Sprache auch
serverseitig einsetzbar geworden ist. JavaScript kann daher ohne Wechsel der
Sprache die Programmierung für das Frontend (den Webbrowser) und das
Backend (den Webserver) gleichzeitig abdecken; damit stellt es eine beliebte
Alternative zu PHP dar.

1.4 Was ist für die Entwicklung mit PHP


notwendig?
Die Entwicklung von Computerprogrammen erfordert die Einrichtung einer
Entwicklungsumgebung auf dem Computer des Software-Entwicklers. Durch
Installation und Konfiguration bestimmter Programme wird eine Umgebung
geschaffen, um Software herzustellen und zu testen. Eine Entwicklungsumge-
bung sieht für jede Programmiersprache unterschiedlich aus.
Die Umgebung, in der eine fertige Software zum echten Einsatz
kommt, heißt Produktionsumgebung – sie erlaubt nur die reine Aus-
führung und Überwachung der Software, nicht deren Entwicklung.
Für den Einstieg in PHP benötigen Sie zur Einrichtung einer Entwicklungs-
umgebung einen Computer mit Windows-, macOS- oder Linux-Betriebs-
system, eine PHP-Installation und einen Text-Editor. Je nach Betriebssystem
kann noch ein kleines Hilfswerkzeug nötig sein. Später im Buch ergänzen Sie
die Installation eines MySQL-Datenbanksystems.
Zwar existieren »All-in-One«-Lösungen zur Einrichtung einer Entwicklungs-
umgebung mit grafischen Installations- und Bedienoberflächen (z.B. XAMPP,
https://www.apachefriends.org) – tatsächlich lassen sich die Grundlagen je-
doch in einer sehr minimalistischen Umgebung erlernen. Dies sorgt für ein
besseres Grundverständnis aller Vorgänge und entwickelte Projekte können
ebenso problemlos online gestellt werden. Mit den verinnerlichten Grund-
prinzipien finden Sie sich anschließend in verschiedenen fortgeschrittenen
Umgebungen zurecht.

24
Quelltext und PHP-Interpreter 1.5

Die ersten PHP-Programme führen Sie unkompliziert direkt mit Hilfe der
Kommandozeile (Command Line Interface, abgekürzt CLI) aus. Später nutzen
Sie den in PHP eingebauten Webserver, um selbst programmierte, dynami-
sche Webseiten auf dem lokalen Computer zu simulieren, als wären sie über
das Internet verfügbar. Sie lernen auch, wie Sie diese Webseiten tatsächlich im
Internet veröffentlichen können.
Alle im Buch eingesetzten Entwicklungswerkzeuge sind entweder in
Ihrem Betriebssystem enthalten oder können kostenlos heruntergela-
den werden. Einfache Webseiten können Sie im Internet zu geringen
Kosten veröffentlichen.

1.5 Quelltext und PHP-Interpreter


Ein PHP-Programm besteht aus reinem Text (plain text), dem so genann-
ten Quelltext (auch Quellcode - source code). Quelltext kann grundsätzlich in
einem einfachen Text-Editor, den jedes Betriebssystem von Hause aus mit-
bringt, geschrieben werden. Unter Windows ist das etwa Notepad, unter mac­
OS TextEdit (im Reintext-Modus). Zum professionellen Schreiben von PHP-
Code gibt es – wie für jede andere Programmiersprache – jedoch auch spezi-
alisierte (kostenlose oder kommerzielle) Programme, die viel Unterstützung
bei der Entwicklung leisten und auf die kaum ein Programmierer verzichtet.
Nicht geeignet sind Textverarbeitungsprogramme wie Microsoft Word, da sie
neben dem Text auch Formatierungsanweisungen transportieren und speziel-
le Dateiformate verwenden.
Zunächst ein ganz einfacher Quelltext für Ihr erstes PHP-Programm:
Willkommen bei <?php echo 'PHP'; ?>!

Listing 1.2: Erstes PHP-Programm in der Datei welcome.php

Um das Programm im Sinne von PHP auszuführen, benötigen Sie auf Ihrem
Computer eine Software, die PHP-Quelltext einlesen, analysieren und die ent-
haltenen Anweisungen ausführen kann - den PHP-Interpreter. Er nimmt PHP-
Quelltext entgegen und führt diesen direkt aus. Dabei kann das Programm
verschiedenste Aufgaben erledigen, z.B. Dateien bearbeiten, Informationen
aus dem Internet abrufen, Daten aus einer Datenbank laden, Berechnungen
durchführen, E-Mails versenden, PDFs generieren, Daten aufbereiten und
vieles mehr. Im Laufe eines Programms erfolgen meist sichtbare Ausgaben an
den Benutzer, um Daten anzuzeigen oder Rückmeldung über den Erfolg bzw.
Misserfolg eines Vorgangs zu geben.

25
Kapitel 1 Erste Schritte mit PHP

Da der Quellcode von PHP direkt durch einen Interpreter ausgeführt wird,
zählt PHP zu den interpretierten Programmiersprachen. Im Gegensatz dazu
stehen kompilierte Programmiersprachen wie Java oder C. In kompilierten
Sprachen übersetzt zunächst ein Compiler das Programm in Maschinencode
für das entsprechende Betriebssystem. Das übersetzte (»kompilierte«) Pro-
gramm kann erst dann vom Betriebssystem ausgeführt werden. PHP macht
den Einstieg in dieser Hinsicht also sehr leicht. Interpretierte Programmier-
sprachen werden auch Skriptsprachen genannt. Deren Programme bzw. Quell-
text-Dateien heißen daher auch Skripte. PHP gehört zur Kategorie der Skript-
sprachen.
PHP-Skripte werden in Dateien mit der Endung .php abgespeichert. Dabei
bleibt der Inhalt weiterhin reiner Text. Mit Hilfe der passenden Endung kann
eine PHP-Datei in verschiedenen Situationen von Benutzern und Maschinen
(z.B. Editoren oder Webservern) als solche erkannt und entsprechend behan-
delt werden.
Zur Ausführung des Programms muss die Skriptdatei dem PHP-Interpreter
zugeführt werden. Nach der Installation von PHP kann der Interpreter über
den Befehl php auf der Kommandozeile Ihres Computers angewiesen werden,
die Datei auszuführen. Sehen wir uns daher zunächst Grundlagen im Umgang
mit einer Kommandozeile an.

1.6 Kommandozeile nutzen


Die Kommandozeile ist eine »textuelle« Benutzerschnittstelle zu Ihrem Com-
puter. Im Gegensatz zu einer grafischen Schnittstelle benutzen Sie dort keine
Maus, Menüs oder Schaltflächen, um Ihren Computer bzw. dessen Software
zu steuern. Stattdessen geben Sie Kommandos über die Tastatur ein, etwa zur
Anzeige des aktuellen Datums, zur Ausgabe eines Dateiinhalts oder zur Aus-
führung eines PHP-Skripts. Jedes Betriebssystem bringt von Haus aus bereits
eine Kommandozeilen-Anwendung mit.
Wenige Kenntnisse zur Nutzung einer Kommandozeile reichen für den Start
mit PHP aus. Auf der Kommandozeile befinden Sie sich im Kontext des
Dateisystems. Das bedeutet, Sie befinden sich wie im Windows Explorer oder
­macOS-Finder immer in einem Verzeichnis und können durch das Dateisys-
tem navigieren.
Die Kommandozeile erwartet Sie wie in Abbildung 1.8 immer mit der Auf-
forderung zu einer Eingabe – dem so genannten Prompt. Der Prompt wird
beispielsweise durch einen Pfeil oder ein Dollar-Symbol dargestellt. Häufig

26
Kommandozeile nutzen 1.6

zeigt der Prompt zur besseren Orientierung auch das Verzeichnis an, in dem
Sie sich aktuell befinden.

Abb. 1.8: Kommandozeile iTerm auf dem Mac mit Anzeige des aktuellen Verzeichnisses. Der
Cursor wartet auf die Eingabe eines Kommandos.

Eine fertige Eingabe bestätigen Sie mit der Eingabetaste. Der Computer führt
Ihr Kommando aus - oder quittiert es mit einer Fehlermeldung, falls es fehl-
schlägt bzw. keinen Sinn ergibt.

Abb. 1.9: Ausgeführte Befehle auf der Kommandozeile

1.6.1 Kommandozeile starten


Der Start einer Kommandozeile unterscheidet sich je nach Betriebssystem.
Die anschließende Verwendung ist jedoch ähnlich.

Windows
Windows bringt mit der Eingabeaufforderung ein Kommandozeilen-Pro-
gramm mit, welches allerdings nicht sehr komfortabel ist. Sehr gut geeignet
hingegen ist die Git Bash, eine Nachahmung (»Emulation«) des Kommando-
zeilen-Programms Bash aus der Unix-Welt. Dazu installieren Sie zunächst Git
for Windows (https://gitforwindows.org) mit allen vorausgewählten Stan-
dardoptionen. Git selbst ist eine Software zur Quelltext-Verwaltung (siehe Ka-
pitel 14), wir interessieren uns an dieser Stelle jedoch lediglich für die nützli-
che »Beigabe« der Git Bash.
Drücken Sie nach der Installation die [Windows]-Taste, suchen Sie nach »Git
Bash« und starten Sie das Programm. Die Git Bash startet im Kontext des Be-
nutzerverzeichnisses, abgekürzt durch die Tilde ~.

27
Kapitel 1 Erste Schritte mit PHP

Abb. 1.10: Git Bash im Benutzerverzeichnis C:\Users\Philipp

Alternativ können Sie die Git Bash aus dem Kontextmenü des Windows-Ex-
plorers in einem vorausgewählten Verzeichnis öffnen. Klicken Sie im Explorer
mit der rechten Maustaste auf das gewünschte Verzeichnis und wählen Sie aus
dem Kontextmenü den Eintrag Git Bash Here.

Abb. 1.11: Start der Git Bash über das Kontextmenü

In der Git Bash funktionieren die gewöhnlichen Shortcuts für


Copy&Paste ([Strg]+[C], [Strg]+[V]) nicht. Die Funktionen stehen
aber über das Kontextmenü (rechte Maustaste) zur Verfügung.
macOS
macOS bringt mit der App Terminal ein Kommandozeilen-Programm mit.
Optisch ansprechender und komfortabler ist die kostenlose App iTerm2
(https://iterm2.com). Laden Sie iTerm2 herunter, verschieben Sie es ins Ver-
zeichnis Programme und starten Sie es.

Linux
Linux-Distributionen bringen bereits ein geeignetes Kommandozeilen-Pro-
gramm mit, z.B. unter Ubuntu-Linux das Programm _Terminal_.

1.6.2 Kommandozeilen-Einmaleins
Im Rahmen des Buchs reicht es aus, wenn Sie Befehle ausführen und sich im
Dateisystem bewegen können.

28
Kommandozeile nutzen 1.6

Die Git Bash unter Windows arbeitet wie unter Unix üblich mit
dem gewöhnlichen Schrägstrich (/) als Verzeichnistrenner. So lautet
etwa der Pfad zum Benutzerverzeichnis C:\Benutzer\Philipp dort
/C/'Benutzer/Philipp.

Nach dem Start des Kommandozeilen-Programms befinden Sie sich im Hei-


matverzeichnis des eingeloggten Benutzers. Der Befehl pwd (print working
­directory) zeigt das aktuelle Verzeichnis an:
> pwd
/Users/philipp.rieber

Mit dem Befehl ls (list) listen Sie den Inhalt des aktuellen Verzeichnisses auf.
Im Beispiel sind drei Unterverzeichnisse zu sehen:
> ls
Desktop Downloads my-php-project

Mit dem Befehl cd (change directory) wechseln Sie das Verzeichnis. Das ge-
wünschte Zielverzeichnis übergeben Sie mit Leerzeichen getrennt als so ge-
nanntes Argument an den Befehl. Möchten Sie eine Verzeichnisebene höher
wechseln, in das übergeordnete Verzeichnis, drücken Sie dies mit zwei Punk-
ten (..) als Argument aus.
> cd my-php-project
> pwd
/Users/philipp.rieber/my-php-project
> cd ..
> pwd
/Users/philipp.rieber

Verzeichnisebenen werden mit einem Schrägstrich getrennt. Ein Verzeichnis-


pfad, der mit einem Schrägstrich beginnt, ist ein absoluter Pfad und beschreibt
den Ort eines Verzeichnisses oder einer Datei aus Sicht der Wurzel (root) des
Dateisystems:
/Users/philipp.rieber/my-php-project

Egal, wo Sie sich gerade im Dateisystem befinden, ein absoluter Pfad ist im-
mer eindeutig. Ohne Schrägstrich zu Beginn handelt es sich um einen relati-
ven Pfad aus Sicht des aktuellen Verzeichnisses. Die Beispiele verdeutlichen
den Unterschied:

29
Kapitel 1 Erste Schritte mit PHP

> pwd
/Users
> cd philipp.rieber/my-php-project
> pwd
/Users/philipp.rieber/my-php-project
> cd /Users/philipp.rieber/Dokumente
> pwd
/Users/philipp.rieber/Dokumente
> cd ../my-php-project
> pwd
/Users/philipp.rieber/my-php-project
> cd /
> pwd
/

Das Heimatverzeichnis des eingeloggten Benutzers kann mit der Tilde (~) ab-
gekürzt werden. Dies ist hilfreich, wenn Sie auf schnellstem Weg »nach Hau-
se« wollen.
> pwd
/usr/local/bin
> cd ~
> pwd
/Users/philipp.rieber

Auf der Git Bash unter Windows gelangen Sie von der Wurzel des Dateisys-
tems (/) aus in die einzelnen Festplattenpartitionen, d.h. Laufwerk C unter /C,
Laufwerk D unter /D usw.
> pwd
/C/Users/Philipp
> cd /D/my-php-projects
> pwd
/D/my-php-projects

Haben Sie einige Zeichen eines Befehls oder Verzeichnispfads einge-


geben, löst die [ÿ]-Taste (Tabulator) eine Auto-Vervollständigung
aus. Mit [½] und [¼] blättern Sie durch die Historie Ihrer bisherigen
Befehlseingaben. Diese Tricks sparen Ihnen viel Tipparbeit.
Neben Argumenten kennen die meisten Befehle für die Kommandozeile ver-
schiedene Optionen (auch Schalter oder Flags genannt), die mit einem einfa-

30
PHP installieren 1.7

chen oder doppelten Bindestrich beginnen. Beispielsweise gibt der Befehl ls


mit der Option -l eine detaillierte Listenansicht mit Angaben zu Dateirech-
ten, Besitzer, Änderungszeitpunkt und Größe (im Beispiel gekürzt):
> ls
Desktop Downloads welcome.php
> ls -l
drwx------ p.rieber 6 Mär 09:54 Desktop
drwx------ p.rieber 3 Mär 20:01 Downloads
drwx------ p.rieber 4 Mär 23:13 welcome.php

Zur Entwicklung von PHP-Programmen fehlt anschließend der wichtigste


Befehl: php.

1.7 PHP installieren


Mit dem Vorwissen zur Kommandozeile können Sie im nächsten Schritt PHP
installieren, die Version überprüfen und mit der Ausführung von Skripten
beginnen.
Installationsanleitungen können veralten oder je nach Vorkonfigura-
tion des Rechners gibt es individuelle Probleme. Lassen Sie sich in
einem solchen Fall nicht entmutigen, eine Recherche der Fehlermel-
dung im Internet fördert mit Sicherheit eine Lösung zutage.

1.7.1 Windows
Laden Sie PHP von der Website https://windows.php.net/download als ZIP-
Archiv herunter, siehe Abbildung 1.12. Wählen Sie dabei die Variante x64 Non
Thread Safe. »x64« steht für 64-bit Betriebssysteme, den heutigen Standard.
Threadsicherheit (thread safety) ist nur in Kombination mit bestimmten Web-
servern erforderlich. Das Archiv hat einen ähnlichen Namen wie php-8.0.12-
nts-Win32-vs16-x64.zip mit der zum Zeitpunkt des Downloads aktuellsten
PHP-Versionsnummer. Entpacken Sie das Archiv in ein Verzeichnis und ver-
schieben Sie das ganze Verzeichnis nach C:\Programme (diesen Vorgang müs-
sen Sie als Administrator bestätigen). Benennen Sie das Verzeichnis um in
php. Die PHP-Installation befindet sich somit in C:\Programme\php.
Beachten Sie die Hinweise in der linken Spalte der Downloads-Website: Zum
Zeitpunkt des Schreibens dieses Buchs ist zusätzlich die Installation von
Visual C++ Redistributable for Visual Studio 2015-2019 erforderlich. Der
Download zur einer entsprechenden Installationsdatei ist verlinkt. Laden Sie
diese herunter und führen Sie die Installation durch.

31
Kapitel 1 Erste Schritte mit PHP

Abb. 1.12: PHP-Downloads für Windows

Windows verwendet für gewisse Verzeichnisse deutsche Aliase. So


referenzieren C:\Programme und C:\Program Files dasselbe Verzeich-
nis. Lassen Sie sich nicht verwirren, falls Ihnen einmal der englische
Name begegnet.
Im Anschluss steht PHP in der Git Bash zur Verfügung, der php-Befehl befin-
det sich im PHP-Installationsverzeichnis. Zur Überprüfung können Sie die
PHP-Version mit der Befehlsoption -v anzeigen:
> /C/Programme/php/php –v
PHP 8.0.12

32
PHP installieren 1.7

Die Angabe des vollständigen Pfads zum PHP-Befehl wäre auf Dauer um-
ständlich. Windows kann jedoch mitgeteilt werden, Befehle ohne vollständige
Pfadangabe automatisch auch im Verzeichnis C:\Programme\php zu suchen.
Dazu bearbeiten Sie die Umgebungsvariable mit dem Namen Path. Drücken
Sie die [Windows]-Taste und suchen Sie nach »Umgebungsvariablen«.

Abb. 1.13: Bearbeitung von Umgebungsvariablen starten

Öffnen Sie den Eintrag Systemumgebungsvariablen bearbeiten. Klicken


Sie im Dialogfester auf Umgebungsvariablen.

Abb. 1.14: Dialog zu Umgebungsvariablen öffnen

33
Kapitel 1 Erste Schritte mit PHP

Wählen Sie aus der Liste der Benutzervariablen im oberen Teilfenster den Ein-
trag Path und klicken Sie auf Bearbeiten.

Abb. 1.15: Umgebungsvariable »Path« bearbeiten

Im neuen Dialogfenster klicken Sie auf die Schaltfläche Neu und geben den
Pfad zum Verzeichnis der PHP-Installation ein: C:\Programme\php

Abb. 1.16: Pfad zur PHP-Installation eintragen

Bestätigen Sie alle Dialoge mit OK und starten Sie die Git Bash neu. Wieder-
holen Sie die Versionsprüfung mit dem verkürzten Befehl php:

34
PHP installieren 1.7

> php –v
PHP 8.0.12

Durch Ersetzen des Verzeichnisses C:\Programme\php mit einer neu herunter-


geladenen Version können Sie PHP später aktualisieren.

1.7.2 macOS
Mindestens bis zur macOS-Version 11 (Big Sur) ist PHP bereits vorinstalliert.
Allerdings handelt es sich immer um eine veraltete Version und zukünftige
macOS-Versionen werden ohne PHP erscheinen. Eine aktuelle PHP-Versi-
on installieren Sie am bequemsten mit Hilfe des Paketmanagers Homebrew
(https://brew.sh). Öffnen Sie die Kommandozeile und folgen Sie den Instal-
lationsanweisungen auf der Homebrew-Website. Nach Abschluss der Instal-
lation steht der Befehl brew zur Verwaltung von Software-Paketen auf Ihrem
Mac zur Verfügung. Zur Installation von PHP führen Sie folgenden Befehl
aus:
> brew install php

Starten Sie die Kommandozeile neu und prüfen Sie die PHP-Installation mit
der Befehlsoption -v zur Anzeige der Version:
> php -v
PHP 8.0.12

Zu einem späteren Zeitpunkt können Sie PHP aktualisieren:


> brew upgrade php

1.7.3 Linux
Diese Anleitung beschreibt die PHP-Installation unter Ubuntu-Linux und
sollte auf andere Linux-Distributionen übertragbar sein, die ebenfalls den
APT-Paketmanager verwenden (Debian, Mint u.a.). Unter Ubuntu können
Sie Software-Pakete mit dem Befehl apt auf der Kommandozeile verwalten.
Da dabei Änderungen am System erfolgen, muss allen apt-Befehlen der Befehl
sudo (»substitute user and do ...«) vorangestellt werden, um sie im Namen des
root-Benutzers (dem Haupt-Administrator) auszuführen. Beim ersten Befehl
werden Sie nach dem root-Passwort gefragt, danach gewährt Ihnen Ubuntu
ein Zeitfenster, in dem Sie das Passwort nicht erneut eingeben müssen.
Vor jeder Software-Installation aktualisieren Sie zunächst die interne Liste der
verfügbaren Software-Pakete:

35
Kapitel 1 Erste Schritte mit PHP

> sudo apt update

Das vorinstallierte Standard-Paketarchiv von Ubuntu bringt PHP bereits mit,


d.h. Sie könnten PHP mit folgendem Befehl direkt installieren:
> sudo apt install php

Diese PHP-Version ist allerdings meist etwas veraltet. Eine aktuelle Version
befindet sich in einem zusätzlichen, externen Paketarchiv. Zur Verwaltung ex-
terner Paketarchive installieren Sie zunächst das Paket software-properties-
common:

> sudo apt install software-properties-common

Mit dem nun verfügbaren Befehl add-apt-repository fügen Sie Ihrem System
das externe Paketarchiv ondrej/php hinzu. Das Präfix ppa steht dabei für »Per-
sonal Package Archive«:
> sudo add-apt-repository ppa:ondrej/php

Nach einer erneuten Aktualisierung der Paketliste können Sie PHP in der ak-
tuellen Version installieren:
> sudo apt update
> sudo apt install php

Anschließend steht der php-Befehl zur Verfügung. Prüfen Sie mit der Befehls­
option -v die installierte Version:
> php -v
PHP 8.0.12

Zu einem späteren Zeitpunkt können Sie PHP auch aktualisieren:


sudo apt install --only-upgrade php

1.8 Quelltext-Editor verwenden: Visual Studio


Code
Wie oben beschrieben, genügt zum Schreiben von PHP-Code grundsätzlich
ein einfacher Texteditor. Zur Unterstützung durch Syntax-Hervorhebung
(syntax highlighting) für bessere Lesbarkeit des Codes, Auto-Vervollständi-
gung von Sprachelementen oder automatischen Hinweisen auf Fehler emp-

36
Quelltext-Editor verwenden: Visual Studio Code 1.8

fiehlt sich jedoch die Verwendung eines spezialisierten Editors. Eine gute
Wahl für den Start ist Visual Studio Code (kurz VS Code), ein kostenloser
Quelltext-Editor für verschiedene Programmiersprachen von Microsoft. VS
Code gibt es für Windows, macOS und Linux zum Download unter https://
code.visualstudio.com. Nach dem ersten Start können Sie direkt von der
Willkommensseite eine Erweiterung zur PHP-Unterstützung aktivieren, siehe
Pfeil in Abbildung 1.17.

Abb. 1.17: Startseite des Quelltext-Editors VS Code

Standardmäßig läuft VS Code mit einer englischsprachigen Oberfläche. Über


den Reiter Extensions in der linken Seitenleiste können Sie ein deutsches
Sprachpaket installieren. Geben Sie dort German in das Suchfeld ein und ins-
tallieren Sie das gefundene German Language Pack for Visual Studio Code und
starten Sie VS Code neu.
Einzelne (PHP-)Dateien öffnen Sie über das Menü Datei|Öffnen oder per
Drag&Drop in das Editorfenster. Um alle Dateien eines Projekts bequem zu
verwalten, öffnen Sie ein ganzes Verzeichnis im »VS Code Explorer« über
Datei|Ordner Öffnen. Öffnen Sie auf diese Weise das Verzeichnis mit den
Code-Downloads zu diesem Buch und betrachten Sie die PHP-Datei welcome.
php.

37
Kapitel 1 Erste Schritte mit PHP

Abb. 1.18: Die Code-Downloads zum Buch in VS Code

Eigene PHP-Dateien legen Sie durch Klicken auf das kleine Datei-Symbol an.
Im professionellen Umfeld ist der kommerzielle Editor PhpStorm sehr
beliebt. Dieser bringt viele weitere Spezialfunktionen für die PHP-
Entwicklung mit, z.B. Werkzeuge zur Fehleranalyse oder eine Daten-
bank- und Quellcodeverwaltung. Einen solchen vollumfänglichen
Editor bezeichnet man als integrierte Entwicklungsumgebung (engl.
Integrated Development Environment, kurz: IDE).

1.9 Das erste PHP-Skript ausführen


Navigieren Sie auf der Kommandozeile in das Code-Verzeichnis zu diesem
Kapitel und führen Sie die Datei welcome.php mit Hilfe des php-Befehls aus:
> cd ~/php-mysql-schnelleinstieg/kapitel-01
> php welcome.php
Willkommen bei PHP!

Die Dateiendung spielt übrigens gar keine Rolle. Kopieren Sie welcome.php
in eine neue Datei mit einer beliebigen anderen oder ganz ohne Dateiendung
und führen Sie sie aus:
> php willkommen
Willkommen bei PHP!

38
Basiswissen in der Praxis 1.10

Dennoch dient die Endung einem Zweck: Editoren können die Datei richtig
zuordnen und PHP-Unterstützung anbieten. Webserver erkennen, dass die
Datei vor Auslieferung durch PHP interpretiert werden muss.

1.10 Basiswissen in der Praxis


In diesem Abschnitt experimentieren Sie mit Basiswissen zur Ausführung von
PHP-Skripten.

1.10.1 <?php PHP-Code markieren ?>


Bestimmte Markierungen in einer PHP-Datei, so genannte Tags, zeigen dem
PHP-Interpreter an, bei welchen Bereichen der Datei es sich um PHP-Code
handelt und er aktiv werden muss. Ein solcher PHP-Bereich beginnt mit
einem öffnenden Tag <?php und endet mit dem schließenden Tag ?>. Alles
außerhalb der PHP-Tags bleibt unberührt und wird bei der Ausführung des
Programms eins zu eins in die Ausgabe übernommen.
Dieser Text erscheint eins zu eins in der Ausgabe.

<?php echo 'Diesen Text gibt PHP aus.' ?>

Ohne "echo" kein Datum: <?php date('Y-m-d'); ?>...

Hier das Datum: <?php echo date('Y-m-d'); ?>

Listing 1.3: PHP-Tags erklärt: php-tags.php

Aufgabe 3

Was fällt Ihnen bei der Ausführung von php-tags.php auf? Achten Sie auf
die ausgegebenen Zeilenumbrüche.

Das Short echo Tag »<?= ...« ist eine Kurzform für einfache Ausgaben, es
entspricht »<? echo ...«:
<?= date('Y-m-d'); ?>

Ein schließendes Tag ist nicht immer erforderlich. Das Weglassen des schlie-
ßenden Tags ist bei reinen Programmdateien, die ausschließlich PHP-Code
enthalten, sogar üblich. Es verhindert versehentliche Ausgaben außerhalb der

39
Kapitel 1 Erste Schritte mit PHP

Tags, z.B. leicht zu übersehende Leerzeichen oder Zeilenumbrüche. Folgendes


Beispiel kann nach dem PHP-Code sicher nichts versehentlich ausgeben:
<?php echo 'Hallo PHP';

1.10.2 PHP-Anweisungen mit dem Semikolon beenden;


Das Semikolon markiert das Ende einer Anweisung, danach kann eine weite-
re Anweisung folgen. Nach der Anweisung vor einem schließenden PHP-Tag
kann das Semikolon entfallen:
Mehrere Anweisungen pro Zeile sind möglich,
<?php echo 'aber'; echo ' unleserlich.' ?>

<?php
echo 'Besser nur ';
echo 'eine je Zeile';

Listing 1.4: Anweisungen beenden: semicolon.php

1.10.3 PHP-Code kommentieren


Zum besseren Verständnis eines Skripts können Kommentare in den Quell-
text eingefügt werden, die vom PHP-Interpreter ignoriert werden. Es gibt drei
Möglichkeiten, Kommentare zu notieren:
<?php
// einzeiliger Kommentar, gilt bis Zeilenende
# Alternative für einzeiligen Kommentar
/* Mehrzeiliger Kommentar,
gilt bis zum Abschluss durch */

1.10.4 PHP-Code auf die Schnelle testen


Ein hilfreiches Feature des php-Befehls ist die Option -r zur direkten Ausfüh-
rung von PHP-Code ohne Umweg über eine Datei. Damit lässt sich auf die
Schnelle etwas ausprobieren. Die Angabe von PHP-Tags entfällt:
> php –r 'echo random_int(1, 6);'
4

40
Wie entstand PHP? 1.11

Aufgabe 4

Was bewirken die Optionen -m und -i? Tipp: Die Option -h bietet Hilfe.

1.11 Wie entstand PHP?


Bevor Sie mit dem nächsten Kapitel tiefer in PHP einsteigen, gibt dieser Ab-
schnitt zum besseren Verständnis einen Überblick über die Entstehungsge-
schichte von PHP.
Die erste Version von PHP wurde vom dänisch-kanadischen Programmierer
Rasmus Lerdorf entwickelt und 1995 unter dem Namen Personal Home Page
Tools (PHP Tools) zur kostenfreien Verwendung veröffentlicht. Es handelte
sich zunächst nicht um die Erfindung einer neuen Programmiersprache, son-
dern um eine kleine Skriptsammlung zur bequemeren Protokollierung von
Zugriffen auf seine persönliche Homepage.
In der Folge wuchs der Bedarf an Funktionalität und Lerdorf ergänzte Dinge
wie Variablen, Datenbankunterstützung, den bequemen Zugriff auf Formu-
lar- und Cookie-Daten und sowie eine Syntax zur Einbettung in HTML. Mit
diesen Änderungen entwickelte sich PHP in eine richtige Programmierspra-
che, die 1997 in der zweiten Version unter dem Namen PHP/FI (FI für »Forms
Interpreter«) erschien.
In der noch jungen Geschichte der Webentwicklung war der Bedarf an geeig-
neten Werkzeugen groß und PHP erfreute sich schnell großer Beliebtheit. Die
Anlehnung an die Syntax der damals geläufigen Sprachen C und Perl erleich-
terte die Verbreitung. Der Durchbruch von PHP als unabhängige Program-
miersprache gelang mit einer Neuimplementierung des Sprachkerns durch
eine Kooperation von Lerdorf mit den israelischen Programmierern Andi
Gutmans und Zeev Suraski. Die Veröffentlichung der daraus hervorgehenden
dritten Version erfolgte 1998 unter dem neuen Namen PHP 3.
Um dem Eindruck einer Sprache für den persönlichen Gebrauch entgegen-
zuwirken, stand PHP zukünftig für das rekursive Akronym »PHP: Hypertext
Preprocessor«. Neu geschaffene Erweiterungsmöglichkeiten erlaubten es an-
deren Entwicklern zusätzliche Module einzubringen, was die Anwendungs-
möglichkeiten erhöhte und damit die Verbreitung beschleunigte.
PHP 3 war der Grundstein für die heutige Form von PHP und die Entwick-
lung verlief fortan deutlicher konsistenter zur jeweiligen Vorgängerversion.
Die Features von PHP 3 ermöglichten bereits komplexe Webanwendungen,

41
Kapitel 1 Erste Schritte mit PHP

der Sprachkern war hierfür jedoch oft nicht leistungsfähig genug. So began-
nen Gutmans und Suraski bereits kurz darauf mit einer Überarbeitung des
Kerns, der die Bezeichnung »Zend Engine« erhielt. Zusammen mit weiteren
neuen Sprachelementen und grundlegenden Features wie den Benutzer-Ses-
sions für personalisierte Webseiten (Logins) wurde die Zend Engine im Jahr
2000 mit PHP 4 eingeführt. Dieser Zeitpunkt fiel mit dem Boom des Internets
zusammen. Der Bedarf an leistungsfähigen, dynamischen Webanwendungen
und damit auch Webentwicklern stieg. Die Spezialisierung von PHP auf die
Webentwicklung und die daraus resultierende einfache Erlernbarkeit ver-
drängten daraufhin andere Sprachen wie das zuvor häufig eingesetzte, aber
für diesen Zweck unhandliche Perl.
Neben einer erneuten Verbesserung der Zend Engine verlagerte sich 2004 mit
der Veröffentlichung von PHP 5 der Fokus auf die objektorientierte Program-
mierung (OOP). Ein Trend, der bis heute anhält und PHP zu einer profes­
sionellen Programmiersprache ausgebaut hat. Die für PHP 6 begonnene Uni-
code-Unterstützung erwies sich als nicht durchführbar, die Versionsnummer
wurde übersprungen. Anschließend standardisierte man den unregelmäßigen
Release-Prozess, neue Veröffentlichungen folgen seitdem einem festgelegten,
transparenten Rhythmus.
PHP 7 brachte 2015 eine beachtliche Leistungssteigerung von 30 %. Die Un-
terversionen bauten nach dem Vorbild anderer Sprachen wie Java die Unter-
stützung für striktere Daten-Typisierung und objektorientierte Programmie-
rung weiter aus.
PHP 8 erschien Ende 2020. Neben weiteren fortschrittlichen Sprachfeatures
brachte es dem allgemeinen Trend anderer Sprachen folgend einen Just-in-
Time (JIT) Compiler mit, der weitere Leistungsverbesserungen ermöglichen
soll.

1.12 Übungen
Übung 1
Erweitern Sie das Programm dice.php aus Abschnitt 1.1.3 für zwei Würfel.

Übung 2
Schreiben Sie ein PHP-Kommandozeilenprogramm datetime.php zur Ausga-
be des aktuellen Datums mit Uhrzeit.

42
Variablen, Datentypen
und Konstanten
Computer-Programme verarbeiten Daten. Daten können dazu in zwei Arten
von »Containern« erfasst werden: Variablen für veränderliche und Konstanten
für unveränderliche Daten.
Variablen sind eine Art Platzhalter im Quelltext. Sie stehen stellvertretend für
Daten, die erst bei der Ausführung eines Programms konkret werden, z.B.
Benutzereingaben, Datenbankinhalte oder Zwischenergebnisse bei Berech-
nungen. Konstanten beinhalten Daten, die fester Bestandteil eines Programms
sind, etwa das Zugangspasswort zu einer Datenbank oder eine feste mathema-
tische Größe wie die Kreiszahl π (Pi).
Daten gehören immer einer bestimmten Art an, dem Datentyp. Jeder Da-
tentyp weist andere Eigenschaften auf und kann unterschiedlich eingesetzt
werden. Einfache Datentypen sind Zeichenketten (Texte), Zahlen und Wahr-
heitswerte (wahr oder falsch). Mit dem zusammengesetzten Datentyp Array
werden Daten-Sammlungen dargestellt, z.B. eine Liste aus Zahlen.
Dieses Kapitel zeigt Ihnen den Umgang mit Variablen, Konstanten und Da-
tentypen. Sie erfahren außerdem Grundlegendes zu Programmstrukturen.

2.1 Daten in einer Variablen erfassen


Das erste Programmbeispiel aus dem vorigen Kapitel kombinierte einen fes-
ten Text mit einem durch PHP ausgegebenen Text:
Willkommen bei <?php echo 'PHP'; ?>!

Die Ausführung erfolgt auf der Kommandozeile mit:


> php welcome.php
Willkommen bei PHP!

43
Kapitel 2 Variablen, Datentypen und Konstanten

Das Ergebnis kann natürlich auch ohne PHP-Code erreicht werden. Aber be-
obachten Sie im Folgenden, wie sich das Beispiel mit Hilfe einer Variablen
in ein Programm mit dynamischer Ausgabe entwickelt. Um das bislang fest
vorgegebene Thema der Begrüßung (»PHP«) dynamisch austauschbar zu ma-
chen, wird die Zeichenkette durch eine Variable als Platzhalter ersetzt. Eine
Variable beginnt mit einem Dollarzeichen ($), dem ein beliebiger Name folgt.
»$topic« (engl. Thema) erscheint als passender Variablenname:
Willkommen bei <?php echo $topic; ?>

Der Inhalt der Variable muss zuvor erfasst werden. Er wird der Variablen mit
einem Gleichheitszeichen zugewiesen:
<?php $topic = 'PHP'; ?>
Willkommen bei <?php echo $topic; ?>!

Die Variable lässt sich auch mehrfach im Programm einsetzen:


<?php $topic = 'PHP'; ?>
Willkommen bei <?php echo $topic; ?>!
<?php echo $topic; ?> ist wunderbar.

Listing 2.1: welcome-to-topic.php

Die Ausführung des Programms ergibt:


> php welcome-to-topic.php
Willkommen bei PHP!
PHP ist wunderbar.

Eine einzige Anpassung im Quelltext beeinflusst nun alle Ausgaben:


<?php $topic = 'MySQL'; ?> ...

Die Ausgabe lautet dann:


Willkommen bei MySQL!
MySQL ist wunderbar.

Letztlich ist die Ausgabe des Programms immer noch statisch, die Variab-
le $topic enthält einen unveränderlichen Wert. Mit der in PHP eingebauten
Funktion readline() kann es jedoch dem Aufrufer des Programms bei jeder
Ausführung selbst überlassen werden, den Inhalt der Variable zu bestimmen.
Der Funktionsaufruf unterbricht die Programmausführung zur Erfassung ei-

44
Daten in einer Variablen erfassen 2.1

ner Benutzereingabe. Der in Klammern angegebene Text fordert zur passen-


den Eingabe auf:
<?php $topic = readline('>> Ihr Thema? '); ?>
Willkommen bei <?php echo $topic; ?>!
<?php echo $topic; ?> ist wunderbar.

Listing 2.2: welcome-to-your-topic.php

Nach Drücken der Eingabetaste fährt das Programm mit der Ausführung fort
und weist die Eingabe der Variablen $topic zu. Abbildung 2.1 zeigt einige
Beispielaufrufe.

Abb. 2.1: Die Benutzereingabe beeinflusst dynamisch die Ausgabe

2.1.1 EVA: Grundprinzip der Datenverarbeitung


Mit dem Beispielprogramm aus dem vorigen Abschnitt haben Sie das Grund-
prinzip der Datenverarbeitung kennengelernt:

Abb. 2.2: EVA-Prinzip (Eingabe-Verarbeitung-Ausgabe)

Dieses EVA-Prinzip wiederholt sich ständig, auch in der Webprogrammie-


rung:
ƒ Das Programm nimmt Daten als Eingabe entgegen. Die Eingabekanäle
können sehr unterschiedlich sein.
ƒ Das Programm verarbeitet die Daten entsprechend seiner Anweisungen.
ƒ Das Programm gibt das Ergebnis der Verarbeitung aus.

45
Kapitel 2 Variablen, Datentypen und Konstanten

Auf der Kommandozeile können Daten wie gezeigt interaktiv vom Benutzer
erfasst werden. Im Browser können die Daten z.B. aus einem Formular, Coo-
kies oder der URL stammen.

Hinweis

Wo möglich verzichten die Code-Listings im weiteren Verlauf des Buchs


aus Platzgründen auf die PHP-Tags (<?php ... ?>). PHP-Code ist immer
im Kontext eines PHP-Bereichs zu verstehen. Die vollständigen Skripte fin-
den Sie im Download zum Buch unter www.mitp.de/0395.

2.1.2 Variablen erzeugen und verwenden


Die Erzeugung einer Variablen heißt Deklaration. Die Deklaration ist eine
Anweisung, bei der ein Name für die Variable festgelegt und ihr per Zuwei-
sungsoperator (dem Gleichheitszeichen =) ein Ausgangswert zugewiesen wird:
$topic = 'PHP';

Im weiteren Programmverlauf dient die Variable als Referenz für den Zugriff
auf die enthaltenen Daten, z.B. zur Ausgabe:
echo $topic; // => PHP

Hinweis

Einfache Skript-Ausgaben finden Sie direkt in einem Code-Kommentar –


angedeutet mit einem Pfeil (=>). Das letzte Skript führt somit zur Ausgabe
PHP.

Eine Variable kann überall verwendet werden, wo der enthaltene Wert und
dessen Datentyp Sinn ergeben. Folgendes Beispiel mit der bereits bekannten
date()-Funktion zeigt eine indirekte Übergabe des gewünschten Datumsfor-
mats mit einer Variablen:
$format = 'd.m.Y';
echo date($format); // z.B. => 22.03.2022

Eine Deklaration kann auch mehrere Variablen initialisieren:

46
Daten in einer Variablen erfassen 2.1

$topic = $language = 'PHP';


echo $topic . ', ' $language; // => PHP, PHP;

Listing 2.3: define-multiple-variables.php

Eine neue Zuweisung überschreibt den Wert einer Variablen:


$format = 'd.m.Y';
echo date($format); // z.B. => 22.03.2022
$format = 'Y-m-d';
echo date($format); // z.B. => 2022-03-22

Listing 2.4: format-date.php

2.1.3 Regeln für Variablennamen


Variablen beginnen in PHP immer mit einem Dollarzeichen ($), gefolgt von
einem individuellen Namen, dem so genannten Variablen-Bezeichner. Der
Bezeichner unterliegt folgenden Regeln:
ƒ Er muss mit einem Buchstaben oder Unterstrich beginnen.
ƒ Danach können Buchstaben, Zahlen und Unterstriche folgen.
ƒ Groß- und Kleinschreibung wird unterschieden (engl. case-sensitive).
Beispiele für gültige Variablen:
$name = 'Eva';
$Name = 'Tim'; // Ungleich $name!
$my_favorite_city = 'Neustadt';
$myFavoriteCity = 'Neustadt';
$_ = 's3cr3t';
$käse = 'Camembert';
$r2d2 = 'err-zwo deh-zwo';

Beispiele für ungültige Variablen:


name = '...'; // Kein Dollarzeichen
$4gewinnt = '...'; // Beginnt mit Zahl
$r2-d2 = '...'; // Enhält unzulässiges Zeichen

Für eine gute Lesbarkeit empfiehlt sich die Verwendung einer einheitlichen
Schreibweise der Variablen-Bezeichner, zum Beispiel der sogenannten camel-
Case-Schreibweise (»Kamelhöcker-Schreibweise«). Dabei wird grundsätzlich
klein geschrieben, nur neue Wörter beginnen mit einem Großbuchstaben:

47
Kapitel 2 Variablen, Datentypen und Konstanten

$theMostBeautifulCity = 'Neustadt';

Die gewählten Variablen-Bezeichner sollten aussagekräftig sein und den In-


halt der Variablen widerspiegeln. Eine Faustregel: nicht geläufige Abkürzun-
gen sind zu vermeiden. »$fn« zur Erfassung eines Vornamens (»first name«)
mag während der Programmierung logisch erscheinen. Für Anpassungen des
Programms in der Zukunft oder durch andere Programmierer ist »$firstName«
die verständlichere Wahl.

2.1.4 Datentypen und die »dynamische Typisierung«


Die bisherigen Beispiele wiesen einer Variablen eine Zeichenkette (d.h. einen
Text) zu, eingefasst in einfache Anführungszeichen:
$topic = 'PHP'; // Zeichenkette; in Anführungszeichen!

Statt einer Zeichenkette ist es auch möglich, Zahlen zuzuweisen, die frei von
Anführungszeichen stehen:
$number = 8; // Zahl; keine Anführungszeichen!

Mit Zahlen können Sie z.B. eine Addition durchführen:


$number1 = 5; $number2 = 3;
echo $number1 + $number2; // => 8

Knifflig wird es allerdings bei der Addition zweier Zeichenketten:


$topic1 = 'PHP'; $topic2 = 'MySQL';
echo $topic1 + $topic2; // => ???

An dieser Stelle kommt das Konzept der Datentypen ins Spiel. Jeder Wert im
Programm kann einem Datentyp zugeordnet werden. Bislang sahen Sie die
Typen Zeichenkette (engl. String) und Ganzzahl (engl. Integer). PHP erkennt
Datentypen praktischerweise automatisch im Hintergrund. Kommt es zur
Addition zweier Zahlen, kann PHP die Rechnung nach den Regeln der Ma-
thematik lösen. Kommt es zu einer Addition der Zeichenketten PHP und MySQL
wie im letzten Listing, reagiert PHP mit einem Fehler, da eine Addition zweier
Wörter nicht sinnvoll möglich ist:
PHP Fatal error: Uncaught TypeError: Unsupported operand types:
string + string

Die Funktion var_dump() macht Datentypen (und Werte) sichtbar:

48
Daten in einer Variablen erfassen 2.1

$topic = 'PHP'; $number = 8;


var_dump($topic);
var_dump($number);

Listing 2.5: var_dump.php

Die Ausgabe lautet:


string(3) "PHP" 1
int(8) 2

ƒ 1 Der Datentyp der Variablen $topic ist String (Zeichenkette). Der String
hat 3 Zeichen und lautet PHP.
ƒ 2 Der Datentyp der Variable $number ist Integer (Ganzzahl, Abkürzung
int), ihr Wert ist die Zahl 8.
Der Datentyp einer Variablen kann sich im Laufe eines Skripts durch eine
neue Zuweisung auch einfach ändern:
$number = 8; // Datentyp: Integer
$number = 'Acht'; // Neuer Datentyp: String

Programmierer müssen den Datentyp bei der Deklaration nicht angeben und
eine Variable ist nicht dauerhaft auf einen bestimmten Datentyp festgelegt.
Dieses Merkmal von Skriptsprachen heißt dynamische Typisierung und macht
den Umgang bequem und einsteigerfreundlich.
Die dynamische Typisierung sorgt in vielen Situationen auch für automati-
sche Umwandlungen des Datentyps. Die Anpassungen erscheinen häufig völ-
lig logisch, wie hier die Ausgabe einer Zahl:
$number = 8; echo $number; // => 8

Tatsächlich können jedoch nur Strings ausgegeben werden. PHP übernimmt


die Typumwandlung der Zahl 8 in den String '8' im Hintergrund.
Reagierte PHP bei der Addition zweier Strings auf Grund inkompatibler Ope-
randen mit einem Fehler, sieht es in diesem Fall anders aus:
$number1 = '5'; $number2 = '3'; // String-Variablen
$result = $number1 + $number2; // Ergebnis ist Integer
var_dump($result); // => int(8)

Listing 2.6: auto-type-conversion.php

49
Kapitel 2 Variablen, Datentypen und Konstanten

PHP erkennt die beiden Strings in der Situation der Addition als numerisch
und führt während der Skriptausführung eine implizite, d.h. nicht ausdrück-
lich sichtbare Umwandlung der Strings in Ganzzahlen durch. Auch das Er-
gebnis ist ein Wert vom Typ Integer; die ursprünglichen Variablen bleiben
Strings. Wie im weiteren Verlauf des Buchs zu sehen, versucht PHP in vielen
Situationen, die Datentypen dynamisch anzupassen. Nicht immer sind die
Umwandlungen so logisch zu erklären wie in den letzten Beispielen; oft führt
dieses bequeme PHP-Feature auch zu unerwartetem Verhalten oder Sicher-
heitsproblemen.
Zum Vergleich: In Programmiersprachen mit statischer Typisierung (z.B. Java,
C#, Swift) muss der Datentyp einer Variablen bereits bei der Deklaration ei-
ner Variablen festgelegt werden und kann sich später auch nicht ändern. Eine
String-Variable kann nur Strings aufnehmen, eine Integer-Variable nur eine
Ganzzahl usw. Für die Ausgabe einer Zahl etwa muss der Programmierer die
Umwandlung der Zahl in einen String explizit anweisen. Dieser strikte Um-
gang mit Datentypen ermöglicht die Erkennung bestimmter Fehler bereits bei
der Kompilierung, d.h. bevor das Programm überhaupt ausgeführt werden
kann, und macht Programme dadurch sehr zuverlässig. Der Einstieg in eine
Programmiersprache mit solch strikter Typisierung fällt jedoch schwerer, da
mehr Hintergrundwissen, Schreibarbeit und Achtsamkeit erforderlich ist.
Auch PHP unterstützt mittlerweile in weiten Teilen eine strenge Ty-
pisierung, die optional eingeschaltet werden kann. Damit erlaubt
PHP einen einfachen Einstieg und professionelle Programmierung
gleichermaßen. Im Verlauf des Buchs lernen Sie Beispiele für strenge
Typisierung kennen.

2.2 Einfache Datentypen


Einfache Datentypen sind elementare Datentypen, die nicht weiter zerlegbar
sind. Sie enthalten nur einen einzigen Wert. Man bezeichnet sie auch als ska-
lare Datentypen:
ƒ Zeichenkette (Text) oder englisch String, z.B. 'PHP'
ƒ Ganzzahl oder englisch Integer, z.B. 8
ƒ Gleitkommazahl oder englisch Float, z.B. 3.14
ƒ Wahrheitswert oder englisch Boolean: true oder false
Im Gegensatz zu einfachen Datentypen stehen zusammengesetzte Datentypen
wie Arrays (Abschnitt 2.4) und Objekte (Kapitel 10), welche sich aus anderen
Datentypen zusammensetzen.

50
Einfache Datentypen 2.2

2.2.1 Daten wörtlich aufschreiben: Literale


Werte verschiedener Datentypen können unabhängig von einer Variablen di-
rekt notiert werden. Eine solche direkte Darstellung eines Werts heißt Literal.
echo 'PHP'; // String-Literal: 'PHP'
echo 8; // Integer-Literal: 8

Beispiele für die literale Darstellung der einfachen Datentypen:


ƒ 'PHP' – die Zeichenkette PHP
ƒ 3 – die Ganzzahl Drei
ƒ 3.5 – die Gleitkommazahl 3,5 (englischer Dezimalpunkt!)
ƒ true bzw. false – die Wahrheitswerte wahr bzw. falsch
Die Funktion var_dump() macht Datentyp und Wert eines Literals sichtbar.
Für den Boolean-Datentyp verwendet PHP die Abkürzung bool.
var_dump('PHP'); // => string(3) "PHP"
var_dump(3); // => int(3)
var_dump(3.5); // => float(3.5)
var_dump(true); // => bool(true)

Listing 2.7: literals.php

Die folgenden Abschnitte stellen wichtige Eigenschaften der einfachen Daten-


typen vor.

2.2.2 Zeichenkette (String)


Bei Text spricht man in der Programmierung von einer Zeichenkette – einer
Aneinanderreihung beliebiger Zeichen. Sehr geläufig ist der englische Begriff
String (Englisch für »Kette«). Die beiden wichtigsten Möglichkeiten, einen
String in PHP zu notieren, sind der Einschluss der Zeichen in:
ƒ einfache Anführungszeichen, z.B. 'PHP'
ƒ doppelte Anführungszeichen, z.B. "PHP"
Strings in einfachen Anführungszeichen
Zeichen eines Strings in einfachen Anführungszeichen werden exakt so wie-
dergegeben, wie sie notiert wurden:

51
Kapitel 2 Variablen, Datentypen und Konstanten

$name = 'Max';
echo $name; // => Max
echo 'Hallo $name!'; // => Hallo $name!

Listing 2.8: single-quotes.php

Beachten Sie im letzten Beispiel: Variablen innerhalb einfacher Anführungs-


zeichen werden nicht durch ihren Wert ersetzt!
Soll ein einfaches Anführungszeichen selbst auftreten, ohne die Zeichenkette
zu beenden, ist dem Anführungszeichen ein Backslash (\) voranzustellen:
echo 'Wie geht\'s?'; // => Wie geht's?

Man sagt, der Backslash maskiert das Anführungszeichen und hebt damit sei-
ne besondere Bedeutung auf.

Doppelte Anführungszeichen
Die Verwendung doppelter Anführungszeichen aktiviert bestimmte Sonder-
funktionen. Zum einen ersetzt PHP Variablen, die in einen String eingebettet
sind, durch ihre Werte. Man sagt auch, Variablen werden in ihre Werte »auf-
gelöst«:
$name = 'Max';
echo "Hallo $name!"; // => Hallo Max!

Listing 2.9: double-quotes.php

Zum anderen interpretiert PHP so genannte Escape-Sequenzen. Dies sind spe-


zielle Zeichenkombinationen zur Repräsentation nicht darstellbarer Zeichen
wie der Tabulator (\t) oder Zeilenumbruch (\n). Escape-Sequenzen werden
mit einem Backslash eingeleitet.
$name = 'Max';
echo "Hallo $name!\n"; // Zeilenumbruch am Ende
echo "\t\tHallo $name!\n"; // Zwei Tabulatoren zu Beginn

Die Ausgabe:
Hallo Max!
Hallo Max!

Die Maskierung mit einem Backslash unterbindet die Aktivierung von Son-
derfunktionen und auch die Bedeutung eines doppelten Anführungszeichens:

52
Einfache Datentypen 2.2

$name = 'Max';
echo "Hallo \"\$name\"!\n";
echo "Tabulator: \\t\n";
echo "Zeilenumbruch: \\n\n";
echo "Laufwerk C:\\\n";

Listing 2.10: double-quotes-with-escaping.php

Die Ausgabe:
Hallo "$name"!
Tabulator: \t
Zeilenumbruch: \n
Laufwerk C:\

Durch die standardmäßige Verwendung einfacher Anführungszeichen


beugen Sie Überraschungen vor. Doppelte Anführungszeichen sollten
bewusst zum Einsatz kommen, wenn die Sonderfunktionen sicher er-
wünscht sind.

Der Verknüpfungsoperator
Der Punkt (.) verknüpft (bzw. konkateniert) zwei Werte zu einer Zeichenket-
te, z.B. einen String-Literal mit einer String-Variablen:
$topic = 'PHP';
echo 'Hallo ' . $topic; // => Hallo PHP

Listing 2.11: concatenation.php

Dank der dynamischen Typisierung sind Verknüpfungen auch mit unter-


schiedlichen Datentypen möglich, wie hier zwischen Strings und Zahlen:
echo 3 . " x " . '3 = '. 9; // => 3 x 3 = 9

PHP erkennt den Text-Kontext und konvertiert die Zahlen entsprechend zu


Strings.

Verknüpfender Zuweisungsoperator
Der Verknüpfungsoperator kann mit dem Zuweisungsoperator zu einem
»verknüpfenden Zuweisungsoperator« kombiniert werden (.=). Der Inhalt
einer Variablen wird dadurch erweitert statt überschrieben:

53
Kapitel 2 Variablen, Datentypen und Konstanten

$text = 'Hallo PHP ';


$text .= 8; // Automatische Umwandlung in String
$text .= ' und MySQL!';
echo $text; // => Hallo PHP 8 und MySQL!

Listing 2.12: concatenating-assignment.php

Dies entspricht der längeren Schreibweise:


$text = 'Hallo PHP ';
$text = $text . 8;
$text = $text . ' und MySQL!';

Sprachelemente wie der verknüpfende Zuweisungsoperator, die nur


der Vereinfachung von Schreibweisen dienen, aber nicht die Funk­
tionalität der Sprache erweitern, bezeichnet man als syntaktischen
­Zucker (engl. syntactic sugar).

2.2.3 Ganzzahl (Integer)


Der Datentyp Integer stellt negative und positive Ganzzahlen dar, zum Bei-
spiel:
$a = 89; $b = 0; $c = -192;

Listing 2.13: integers.php

Zur besseren Lesbarkeit können große Zahlen optional mit Unterstrichen un-
terteilt werden:
$amount = 1_455_001; // enspricht: 1455001

Arithmetische Operationen
Die arithmetischen Operatoren erlauben das Rechnen mit Zahlen:

Rechnung Beschreibung Beispiel


-$a Negation: Gegenzahl von $a -7
$a + $b Addition: »$a plus $b« 4 + 2 = 6
$a - $b Subtraktion: »$a minus $b« 3 – 5 = -2
$a * $b Multiplikation: »$a mal $b« 3 * 4 = 12
$a / $b Division: »$a durch $b« 15 / 3 = 5

54
Einfache Datentypen 2.2

Rechnung Beschreibung Beispiel


$a ** $b Potenz: »$a hoch $b« 4 ** 2 = 16
$a % $b Modulo: »Rest von $a durch $b« 23 % 4 = 3

Tab. 2.1: Die arithmetischen Operatoren: arithmetic-operations.php

Der weniger geläufige Modulo-Operator % berechnet den Rest einer Division.


Das Beispiel 23%4 ergibt 3, da die Zahl 4 fünfmal vollständig in die 20 passt
und der Rest 3 bleibt.
Runde Klammern drücken den Vorrang einer Rechenoperation aus und ver-
bessern die Lesbarkeit:
echo ((10 + 3) * 2) / 13; // => 2

Ist eine Division zweier Ganzzahlen nicht ohne Rest möglich, entsteht eine
Gleitkommazahl (siehe Abschnitt 2.2.4):
echo 10 / 3; // => 3.3333333333333

Arithmetische Zuweisungsoperatoren
Möchte man für eine existierende Variable eine neuen Wert berechnen, wo-
bei die Variable selbst Teil der Berechnung ist, gibt es für jede arithmetische
Operation als Abkürzung einen »kombinierten Zuweisungsoperator«. Diese
Operatoren sparen häufig Schreibarbeit:

Zuweisung Entsprechung Beispiel mit $a = 10


$a += $b $a = $a + $b $a += 2 // $a ist 12
$a -= $b $a = $a - $b $a -= 2 // $a ist 8
$a *= $b $a = $a * $b $a *= 2 // $a ist 20
$a /= $b $a = $a / $b $a /= 2 // $a ist 5
$a %= $b $a = $a % $b $a %= 3 // $a ist 1

Tab. 2.2: Arithmetische Zuweisungsoperatoren: arithmetic-assignments.php

Inkrement- und Dekrement-Operatoren


Der Inkrement-Operator (++) erhöht eine Zahlen-Variable um Eins:
$x = 2; ++$x; echo $x; // => 3

Listing 2.14: increment-decrement.php

55
Kapitel 2 Variablen, Datentypen und Konstanten

Die Operation ++$x ist äquivalent zu $x=$x+1 bzw. $x+=1.


Der Dekrement-Operator (--) verringert eine Zahlen-Variable um Eins:
$x = 2; --$x; echo $x; // => 1

Die Operation --$x ist äquivalent zu $x=$x-1 bzw. $x-=1.


Steht der Operator wie in den obigen Beispielen vor der Variablen, bezeichnet
man ihn als Prä-Inkrement- bzw. Prä -Dekrement-Operator: ++$x bzw. --$x.
Schreibt man den Operator nach der Variablen, bezeichnet man ihn als Post-
Inkrement- bzw. Post-Dekrement-Operator: $x++ bzw. $x--.
Bei Verwendung der Operatoren in eigenständigen Anweisungen gibt es kei-
nen Unterschied zwischen Prä- und Post-Schreibweise, wie die Wiederholung
des vorigen Beispiels mit den Post-Operatoren zeigt:
$x = 2; $x++; echo $x; // wie ++$x => 3
$x = 2; $x--; echo $x; // wie --$x => 1

Die Verwendung der Operatoren in einer anderen Anweisung (im Folgenden


mit echo) offenbart jedoch Unterschiede:
$x = 2;
// Erhöht erst $x um 1 und gibt es dann aus:
echo ++$x; // Entspricht 2 Anweisungen: $x+=1; echo $x; => 3
// Gibt erst $x aus und erhöht es dann um 1:
echo $x++; // Entspricht 2 Anweisungen: echo $x; $x+=1; => 3
echo $x; // Erst hier hat $x den zuletzt erhöhten Wert => 4

$x = 2;
// Verringert erst $x um 1 und gibt es dann aus:
echo --$x; // Entspricht 2 Anweisungen: $x-=1; echo $x; => 1
// Gibt erst $x aus und verringert es dann um 1:
echo $x--; // Entspricht 2 Anweisungen: echo $x; $x-=1; => 1
echo $x; // Erst hier hat $x zuletzt verringerten Wert => 0

Die gewählte Schreibweise bestimmt den Zeitpunkt der Wertänderung der


Variable. Prä-Operatoren werden vor der Auflösung einer Variablen in ihren
Wert auf die Variable angewendet, Post-Operatoren danach.
echo ++$x; // Entspricht $x = $x + 1; echo $x;
echo $x++; // Entspricht echo $x; $x += 1;

56
Einfache Datentypen 2.2

2.2.4 Gleitkommazahl (Float)


Eine Gleitkommazahl (float) stellt eine nicht-ganze Zahl dar, z.B. die Kreiszahl
π (Pi):
$pi = 3.141592653589793;
echo $pi * $pi; // 9.8696044010894

Gleitkommazahlen verwenden den im Englischen üblichen Dezimalpunkt


statt des im Deutschen üblichen Kommas und können wie Ganzzahlen in
Berechnungen verwendet werden. Sie können auch das Ergebnis einer Rech-
nung mit Ganzzahlen sein:
echo 1 / 3; // => 0.33333333333333

Besonders große oder kleine Zahlen lassen sich auch in der wissenschaftli-
chen Exponentialschreibweise ausdrücken. Dabei wird der Dezimalpunkt nach
links bzw. rechts verschoben und durch eine Multiplikation mit einer positi-
ven bzw. negativen Zehnerpotenz ausgeglichen. Der Exponent wird mit dem
Buchstaben E von der Zahl separiert. Üblicherweise verschiebt man das Kom-
ma so lange, bis eine Zahl zwischen 1 und 10 entstanden ist.

Große Zahl Darstellung Kleine Zahl Darstellung


299792 299792 0.00009 0.00009
29979.2 x 101 29979.2E1 0.0009 x 10-1 0.0009E-1
2997.92 x 10 2
2997.92E2 0.009 x 10 -2
0.009E-2
299.792 x 10 3
299.792E3 0.09 x 10 -3
0.09E-3
29.9792 x 10 4
29.9792E4 0.9 x 10-4
0.9E-4
2.99792 x 105 2.99792E5 9.0 x 10-5 9.0E-5

Tab. 2.3: Beispiel für Exponentialschreibweise: exponential-notation.php

2.2.5 Wahrheitswert (Boolean)


Ein Wahrheitswert (boolean) drückt aus, ob eine Aussage wahr oder falsch ist.
Die Bezeichnung »Boolean« geht auf den Mathematiker George Boole zurück.
Der »boolesche« Datentyp nimmt wie eine Binärzahl (1 oder 0) nur zwei fest
definierte Werte an: true oder false. Groß- und Kleinschreibung spielen kei-
ne Rolle, TRUE ist äquivalent zu true usw.

57
Kapitel 2 Variablen, Datentypen und Konstanten

$sunIsShining = true;
$ticketIsPaid = false;

Wahrheitswerte sind auch Ergebnis von Vergleichsausdrücken, z.B. bei Zah-


lenvergleichen:
$age = 18;
var_dump($age > 17); // => bool(true)
var_dump($age < 18); // => bool(false)

Vergleiche spielen im Programmablauf eine elementare Rolle, um Entschei-


dungen zu treffen (Kapitel 3). An dieser Stelle vermittelt der ternäre (dreiteili-
ge) Operator (?:) bereits eine Idee der Möglichkeiten:
Bedingung ? Wert1 : Wert2

Ist die Bedingung wahr, ist das Ergebnis des Operators der Wert1, ansonsten
der Wert2.
$age = 18;
echo ($age > 17) ? 'Ja' : 'Nein'; // => Ja

Listing 2.15: ternary-operator.php

2.3 Der spezielle Datentyp: NULL


Der spezielle Datentyp NULL kann nur einen einzigen Wert annehmen: null.
Groß- und Kleinschreibung spielen keine Rolle, NULL ist äquivalent zu null.
Der NULL-Datentyp ist ein Feature vieler Programmiersprachen (und Daten-
banken), um einen fehlenden oder ungültigen Wert zu repräsentieren.
Stellen Sie sich ein Formular zur Abfrage von Personenangaben vor, in dem
die Angabe des Geschlechts optional ist. Ohne Angabe des Geschlechts bie-
tet es sich an diese Tatsache durch null auszudrücken, da eine Angabe nicht
vorhanden ist.
$gender = 'female'; // Geschlecht angegeben
$gender = null; // Geschlecht nicht angegeben

Der NULL-Datentyp und sein einziger Wert null sind nicht mit der
Ganzzahl Null (0) zu verwechseln!

58
Der vielseitige Datentyp: Array 2.4

2.4 Der vielseitige Datentyp: Array


Der Datentyp Array nimmt mehrere Datenelemente gleichzeitig auf und bil-
det eine Sammlung (bzw. Kollektion, engl. collection) aus Daten, z.B. eine Liste
von Planetennamen:
$planets = ['Merkus', 'Venus', 'Erde'];

Die Schreibweise mit eckigen Klammern ist eine Kurzform für:


$planets = array('Merkus', 'Venus', 'Erde');

Die Kurzform ist handlicher zu verwenden und üblich.

2.4.1 Numerisch indizierte Arrays


Jedes Element eines Arrays ist ein Schlüssel-Wert-Paar, d.h. auf jeden Wert
eines Elements kann mit Hilfe seines Schlüssels zugegriffen werden. Wurden
bei der Deklaration keine expliziten Schlüssel vergeben, vergibt PHP automa-
tisch eine ganzzahlige Nummer als Schlüssel für jedes Element. Sie beginnen
bei der Zahl Null.

Abb. 2.3: Numerisch indiziertes Array, beginnend bei Null

Der Zugriff auf ein Element erfolgt durch Angabe des Schlüssels in eckigen
Klammern hinter der Array-Variable:
$planets = ['Merkur', 'Venus', 'Erde'];
echo $planets[0]; // => Merkur
echo $planets[1]; // => Venus
echo $planets[2]; // => Erde

Listing 2.16: planets.php

Mit Angabe des Schlüssels lässt sich auch ein neues Element einfügen:
$planets[3] = 'Mars';

Bei Angabe einer leeren Klammerpaars fügt PHP automatisch den Wert mit
dem nächsthöheren Schlüssel in das Array ein:
$planets[] = 'Jupiter'; // enspricht $planets[4]

59
Kapitel 2 Variablen, Datentypen und Konstanten

Ein Element kann wie eine Variable verwendet und daher auch überschrieben
werden:
$planets[2] = 'Unsere';
$planets[2] .= ' Erde';

Alle Details eines Arrays zeigt ein var_dump()-Aufruf:


var_dump($planets);

Die Ausgabe:
array(5) {
[0]=> string(6) "Merkur"
[1]=> string(5) "Venus"
[2]=> string(11) "Unsere Erde"
[3]=> string(4) "Mars"
[4]=> string(7) "Jupiter"
}

Jeden Schlüssel kann es nur genau einmal im Array geben, d.h. ein Schlüssel
ist immer eindeutig. Unter verschiedenen Schlüsseln können gleiche Werte
jedoch mehrfach auftreten:
$earths = ['Erde', 'Erde', 'Erde'];

Abb. 2.4: Numerisch indiziertes Array mit gleichen Werten

Schlüssel können bei der Deklaration durch die Zuordnung Schlüssel=>Wert


auch explizit vergeben werden. Die Einhaltung einer exakt aufsteigenden Fol-
ge numerischer Schlüssel ist nicht erforderlich, d.h. Schlüssel können auch
einfach ausgelassen werden:
$planets = [
10 => 'Merkur',
12 => 'Venus',
25 => 'Erde',
];
echo $planets[12]; // => Venus

60
Der vielseitige Datentyp: Array 2.4

Auch nach der Deklaration kann ein Element mit bisher nicht vergebenem
Schlüssel erzeugt werden:
$planets[44] = 'Jupiter';
echo $planets[44]; // => Jupiter

Aufgabe 1

Der Aufruf unset($planets[Schlüssel])) entfernt das Element mit dem


entsprechenden Schlüssel aus dem Array $planets. Was geschieht dabei
mit den übrigen Schlüsseln?

2.4.2 Assoziative Arrays


Neben Ganzzahlen können auch Strings als Schlüssel verwendet werden. Ar-
rays mit String-Schlüsseln bezeichnet man als assoziative Arrays.
$numberOfMoons = [
'Merkus' => 0,
'Venus' => 0,
'Erde' => 1,
'Mars' => 2,
'Jupiter' => 79,
];
echo 'Monde des Mars: ' . $numberOfMoons['Mars'];
// => Monde Mars: 2

Listing 2.17: associative-arrays.php

Numerische und assoziative Schlüssel können gemischt auftreten. Elemente


ohne expliziten Schlüssel erhalten von PHP einen numerischen Schlüssel.
$randomElements = [
'speedOfLight' => 299792458,
true,
'home' => 'Erde',
3.33
];
var_dump($randomElements);

61
Kapitel 2 Variablen, Datentypen und Konstanten

Die Ausgabe:
array(4) {
["speedOfLight"] => int(299792458)
[0] => bool(true)
["home"] => string(4) "Erde"
[1] => float(3.33)
}

Das Beispiel zeigt auch, dass Arrays Elemente verschiedener Datentypen auf-
nehmen können.

2.4.3 Mehrdimensionale Arrays


Ein Array kann wiederum selbst Arrays enthalten, man spricht dann von ei-
nem mehrdimensionalen Array. Die Dimension gibt die Verschachtelungstiefe
an. Die bisher gezeigten Arrays ohne Verschaltung waren eindimensional –
wie eine einfache Liste. Enthält das Array ein weiteres Array, ist es zweidi-
mensional – wie eine Tabelle. Die Verschachtelung kann beliebig fortgesetzt
werden und dadurch beispielweise Baumstrukturen abbilden – z.B. das Ver-
zeichnissystem eines Computers.
Folgendes zweidimensionales Array schlüsselt die Mitarbeiter einer Firma
nach Abteilungen auf. Die erste Dimension ist der Abteilungsname, die zweite
Dimension der Name eines Mitarbeiters in dieser Abteilung:

Abb. 2.5: Mehrdimensionales Array

62
Datentypen umwandeln 2.5

$employees = [
'IT' => ['Mark', 'Lisa', 'Frank'],
'Personal' => ['Jana'],
'Verkauf' => ['Elena', 'Peter'],
];

Listing 2.18: multi-dimensional-array.php

Der Zugriff auf tiefere Ebenen erfordert für jede Dimension einen weiteren
Schlüssel in eckigen Klammern:
echo $employees['Verkauf'][1]; // => Peter

2.5 Datentypen umwandeln


Dieser Abschnitt vertieft das Thema der dynamischen Typisierung und zeigt,
wie sich Datentypen auch explizit umwandeln lassen.

2.5.1 Type Juggling


Betrachten Sie ein weiteres Beispiel zur dynamischen Typisierung: Im folgen-
den Skript entscheidet der ternäre Operator (Abschnitt 2.2.5) anhand des Ver-
gleichs $age>17, ob der erste oder zweite Wert (volljährig bzw. minderjährig)
ausgegeben wird:
echo ($age > 17) ? 'volljährig' : 'minderjährig';

Die Ausgabe hängt vom Wert der Variablen $age ab. Ist ihr Wert größer als 17,
ist der Vergleich wahr (true) und es kommt zur Ausgabe des ersten Werts, an-
sonsten ist der Vergleich falsch (false) und der zweite Wert wird ausgegeben.
Interessanterweise führt das Skript auch zum erwarteten Ergebnis, wenn der
Variablen $age ein String zugewiesen wird. Damit findet in der Bedingung des
ternären Operators ein Vergleich zwischen String und Integer statt:
$age = '21'; // $age ist ein String!
echo ($age > 17) ? 'volljährig' : 'minderjährig'; // => volljährig

Listing 2.19: type-juggling.php

PHP greift hier im Hintergrund automatisch ein und führt einen Vergleich
durch, als ob $age eine Ganzzahl wäre. Das heißt, PHP konvertiert $age für
diesen Vergleich intern in eine Ganzzahl.

63
Kapitel 2 Variablen, Datentypen und Konstanten

Das selbstständige »Jonglieren« von PHP mit den Datentypen in verschiede-


nen Kontexten wird entsprechend als »Type-Juggling« (engl. für Jonglieren mit
Typen) bezeichnet. Oft gerät man jedoch in weniger intuitive Situationen:
$age = null; // Alter "unbekannt"
echo 'Alter: ' . $age; // null als String? => ???
echo $age + 5 // null als Integer? => ???
echo $age ? ':-)' : ':-('; // null als Boolean => ???

Wie könnte PHP den null-Wert in den verschiedenen Situationen interpre-


tieren? PHP folgt dazu einem umfangreichen Regelwerk1, das nicht selten zu
überraschendem Verhalten führt. Fallstricken der dynamischen Typisierung
kann durch die Vermeidung unklarer Situationen begegnet werden. Ist etwa
das Alter eines Benutzers für eine Berechnung zwingend nötig, sollte sicher-
gestellt sein, dass einem Skript für diese Berechnung auch eine Ganzzahl für
das Alter zur Verfügung steht, so dass nicht aus Versehen auch mit dem Wert
null oder einem leeren String »gerechnet« wird.
Wie der nächste Abschnitt zeigt, ist in jedem Fall eine Vorstellung davon hilf-
reich, wie Datentypen auf Typ-Umwandlungen reagieren.

2.5.2 Type Casting


Allgemein heißt die Operation zur Umwandlung eines Datentyps Type Cas-
ting oder kurz Casting. Das Verständnis der Casting-Regeln ist wichtig, um die
dynamische Typisierung von PHP auch in weniger trivialen Fällen nachvoll-
ziehen oder in eine gewünschte Richtung lenken zu können.
Zur expliziten, d.h. zur sichtbar gewollten Umwandlung eines Datentyps in ei-
nen anderen, schreibt man einen Casting-Operator vor den Wert oder die Va-
riable, die man umwanden möchte. Der Casting-Operator ist der gewünschte
Ziel-Datentyp in runden Klammern, zum Beispiel (string), (int), (bool),
(array) und so weiter. Bei einigen Castings bleibt der Wert vollständig erhal-
ten:
var_dump((string) 3.14); // => string(4) "3.14"
var_dump((int) '46'); // => int(46)

Listing 2.20: type-casting.php

Andere Castings führen hingegen zu Datenverlust, da ein Wert für den neuen
Datentyp passend gemacht werden muss. Die Umwandlung einer Gleitkom-

1
https://www.php.net/manual/language.types.type-juggling

64
Datentypen umwandeln 2.5

mazahl in eine Ganzzahl führt etwa zum Verlust des Dezimalanteils (Run-
dung auf 0):
var_dump((int) 3.9); // => int(3)

Bei der Konvertierung eines Strings in eine Zahl versucht PHP eine Zahl zu
interpretieren, wenn sie am Anfang steht. Ansonsten ergibt das Casting die
Zahl 0:
var_dump((int) '4 Gewinnt'); // => int(4)
var_dump((int) 'Die Wilde 13'); // => int(0)

Für das Treffen von Entscheidungen (siehe Kapitel 3) sind Umwandungen in


den booleschen Datentyp besonders wichtig. Was könnte der »Wahrheitsge-
halt« einer Zahl, eines Strings oder von null sein? PHP folgt hierbei den Re-
geln, dass neben dem Wert false selbst folgende Werte anderer Datentypen
den Wahrheitsgehalt false haben:
ƒ Die Zahlen 0 und 0.0.
ƒ Der leere String '' und der String '0'.
ƒ Der Wert null.
ƒ Ein leeres Array, d.h. ein Array ohne Elemente.
Folgendes Skript zeigt die Castings in false:
var_dump((bool) 0); // => bool(false)
var_dump((bool) 0.0); // => bool(false)
var_dump((bool) ''); // => bool(false)
var_dump((bool) '0'); // => bool(false)
var_dump((bool) null); // => bool(false)
var_dump((bool) []); // => bool(false)

Alle übrigen Werte ergeben true, einige Beispiele:


var_dump((bool) 13); // => bool(true)
var_dump((bool) ' '); // => bool(true)
var_dump((bool) ['Mars']); // => bool(true)

Aufgabe 2

In welche Strings bzw. Ganzzahlen konvertiert PHP die booleschen Werte


true und false? Probieren Sie es aus.

65
Kapitel 2 Variablen, Datentypen und Konstanten

2.6 Programmstrukturen
Sie haben bereits einige Sprachelemente von PHP kennengelernt. Dieser Ab-
schnitt setzt die verschiedenen Elemente in den Kontext der Programmstruk-
tur und vermittelt dabei wichtige Begriffe.

2.6.1 Ausdrücke
Ein Ausdruck (engl. expression) ist ein Sprachbaustein, der durch Auswertung
bestimmter Regeln einen Wert ergibt. Die Auswertung eines Ausdrucks wird
auch Auflösung oder Evaluierung des Ausdrucks genannt.
Bereits Literale sind Ausdrücke: Der Ausdruck 3 steht für den Zahlenwert
Drei, der Ausdruck 'PHP' für den Text PHP, der Ausdruck true für den Wahr-
heitswert wahr. Literale Ausdrücke stehen folglich einfach für sich selbst.
Interessanter wird die Bedeutung von Ausdrücken beim Zusammenspiel von
mehreren Literalen oder Variablen. Der Ausdruck 3+4 verknüpft zwei nume-
rische Literale mit dem +-Operator, die Auflösung des Ausdrucks ergibt den
Wert 7. Der Ausdruck 'Hallo'.$name verknüpft ein String-Literal mit einer
Variablen und ergibt einen neuen String. Der Ausdruck $age>18 vergleicht
zwei Zahlen und resultiert in einem Wahrheitswert. Der Funktionsaufruf
date('d.m.Y') stellt ebenfalls einen Ausdruck dar: Die Evaluierung ergibt
eine Zeichenkette mit dem aktuellen Datum.
Ein Ausdruck lässt sich also stets in einen Wert übersetzen, entfaltet jedoch
selbst keine Wirkung. Ein Ausdruck führt nicht zu einer Ausgabe, er schreibt
nichts in eine Datenbank, er versendet keine E-Mail usw. Ausdrücke sind
Bausteine der nächstgrößeren Einheit in der Struktur eines Programms: den
Anweisungen.

2.6.2 Anweisungen
Eine Anweisung (engl. statement) ist ein einzelner Teilschritt im Programm-
code, der mit einem Semikolon abschließt. Ein Programm besteht aus einer
Folge von Anweisungen. Ein Ausdruck kann als Anweisung verwendet wer-
den, bewirkt für sich allein jedoch nichts. Das folgende Programm ist korrekt,
erzeugt aber keine Ausgabe oder irgendeinen anderen Seiteneffekt:
3 < 5; // Ausdruck als Anweisung ohne Wirkung

Eine sinnvolle Anweisung führt eine Aktion mit einer Wirkung aus, wobei
Ausdrücke Teile der Anweisung sind. Betrachten Sie folgende drei Programm­

66
Konstanten 2.7

anweisungen zur Simulation eines Würfelwurfs. Der Spieler erhält bei einer
Sechs den Hinweis, nochmal würfeln zu dürfen:
$number = random_int(1, 6); // "Würfelwurf" 1
echo "Gewürfelt: $number\n"; 2
echo ($number < 6) ? 'Nächster' : 'Nochmal'; 3

Listing 2.21: statements.php

ƒ 1 Die literalen Ausdrücke 1 und 6 sind Argumente für den Funktionsauf-


ruf. Der Funktionsaufruf wird als Ausdruck in eine Zufallszahl zwischen 1
und 6 aufgelöst. Das Ergebnis wird einer Variablen zugewiesen und PHP
merkt sich die Zuweisung im Arbeitsspeicher (Wirkung!).
ƒ 2 Der String-Ausdruck in doppelten Anführungszeichen wird entspre-
chend den Regeln aus Abschnitt 2.2.2 aufgelöst. Das Ergebnis wird ausge-
geben (Wirkung!).
ƒ 3 Das boolesche Ergebnis des inneren Vergleichsausdrucks bestimmt die
Auswertung des äußeren Ausdrucks (der ternäre Operator). Je nach Zu-
fallszahl ergibt sich der erste oder zweite String. Das Ergebnis wird ausge-
geben (Wirkung!).
Eine Anweisung darf sich auch über mehrere Zeilen erstrecken, meist um die
Lesbarkeit von langen Anweisungen zu erhöhen. Erst das Semikolon schließt
die Anweisung ab:
echo (random_int(1, 6) < 6)
? 'Nächster'
: 'Nochmal';

2.7 Konstanten
Im Vergleich zu einer Variablen ist eine Konstante während der gesamte Pro-
grammausführung unveränderlich und daher konstant.

2.7.1 Literale Konstanten


Mit Literalen kennen Sie bereits eine Form von Konstanten:
echo 'PHP'; // 'PHP'ist unveränderliche Zeichenkette im Quelltext

Der Ausdruck 'PHP' für sich allein ist eine literale Konstante, sie steht unver-
änderlich im Quelltext. Eine literale Konstante ist eine unbenannte Konstan-
te, da sie nicht von einer anderen Stelle im Quelltext aus referenziert werden

67
Kapitel 2 Variablen, Datentypen und Konstanten

kann. Auch die Wahrheitswerte true/false und der spezielle Wert null sind
für sich allein literale Konstanten.

2.7.2 Benannte Konstanten


Die Funktion define() definiert eine benannte Konstante. Durch Vergabe ei-
nes Bezeichners kann die Konstante wie eine Variable anhand eines Namens
referenziert werden.
define('VERSION', '3.1'); // Deklaration der Konstante VERSION
echo 'Version: ' . VERSION; // => Version: 3.1

Listing 2.22: constant.php

Eine Konstante kann nicht überschrieben werden, der Versuch gibt eine War-
nung aus:
define('VERSION', '3.1');
define('VERSION', '3.2');
// => Warning: Constant VERSION already defined
echo VERSION; // => 3.1

Benannte Konstanten werden häufig eingesetzt, um feste Einstellungen eines


Programms zu definieren, z.B. die Zugangsdaten zu einer Datenbank.
Abgesehen vom einleitenden Dollarzeichen unterliegen Bezeichner für Kon-
stanten den gleichen Regeln wie Variablen. Per Konvention schreibt man
Bezeichner für Konstanten jedoch in Großbuchstaben und trennt einzelne
Wörter per Unterstrich, z.B. DATABASE_PASSWORD. »Per Konvention« bedeutet,
dass dies kein Muss, jedoch üblich ist. Konstanten lassen sich auf diese Weise
schnell im Quelltext erkennen.

2.7.3 Vordefinierte Konstanten


Neben selbst definierten Konstanten kennt PHP viele vordefinierte, d.h. in
PHP eingebaute Konstanten. Sie stehen jedem Programm automatisch zur
Verfügung.
Einige Beispiele:
echo M_PI; // Kreiszahl Pi => 3.1415926535898
echo PHP_VERSION; // PHP-Version => 8.0.12
echo DATE_ATOM; // Standard-Datumsformat => Y-m-d\TH:i:sP

Listing 2.23: built-in-constants.php

68
Konstanten 2.7

Jedes Skript kann vordefinierte Konstanten direkt verwenden:


echo 'Kreisumfang r=3m: ' . (2 * M_PI * 3) . "m\n";
echo 'Sie verwenden PHP ' . PHP_VERSION . "\n";
echo 'Aktuelles Datum: ' . date(DATE_ATOM) . "\n";

Die Ausgabe:
Kreisumfang r=3m: 18.849555921539m
Sie verwenden PHP 8.0.12
Aktuelles Datum: 2021-04-06T20:27:13+00:00

Aufgabe 3

Welche Bedeutung hat die vordefinierte Konstante PHP_INT_MAX?

2.7.4 Magische Konstanten


Neben den tatsächlich unveränderlichen vordefinierten Konstanten stellt PHP
so genannte »magische« Konstanten zur Verfügung. Sie beginnen und enden
mit je zwei Unterstrichen und ihr Wert hängt vom Kontext der Verwendung
ab, d.h. von der Stelle, an der sie im Quelltext verwendet werden:

Magische Konstante Beschreibung


__FILE__ Pfad zum Skript
__DIR__ Pfad zum Skript-Verzeichnis
__LINE__ Zeile im Skript
Tab. 2.4: Beispiele für magische Konstanten

Liegt folgendes Skript magic-constants.php z.B. unter /projects/book/magic.


php, ergibt sich:

<?php
echo __FILE__; // Skriptname => /projects/book/magic.php
echo __DIR__; // Verzeichnisname => /projects/book
echo __LINE__; // Vierte Zeile => 4
echo __LINE__; // Fünfte Zeile => 5

Listing 2.24: magic-constants.php

69
Kapitel 2 Variablen, Datentypen und Konstanten

__DIR__ kommt häufig zur Bildung absoluter Pfade zum Einsatz (siehe Kapitel 8
und 14), die sich dynamisch der Umgebung anpassen, in der die Software aus-
geführt wird:
$uploadDir = __DIR__ . '/../uploads';
echo $uploadDir; // => /projects/book/../uploads

Der absolute Pfad in $uploadDir passt sich dynamisch an, wenn das Skript
in ein anderes Verzeichnis (auch auf einem anderen Computer) verschoben
wird.

2.8 Übungen
Übung 1
Welche Variablen-Bezeichner sind erlaubt?
§book, $eBook, $_1, $1_, $Irobot, $te$t, $bücher, VERSION

Übung 2
Schreiben Sie ein Skript zur Volumen-Berechnung eines Quaders:
ƒ Eingabe: Erfassen Sie die Angaben zu Höhe, Breite und Tiefe in Zentime-
tern interaktiv mit der Funktion readline().
ƒ Verarbeitung: Berechnung Sie das Volumen mit der Formel Höhe (cm) x
Breite (cm) x Tiefe (cm) = cm3.
ƒ Ausgabe: Geben Sie das Ergebnis in Kubikzentimetern (cm3) und Litern
aus (1cm3 = 1/1000 Liter).

Übung 3
Schreiben Sie ein Skript, das mitteilt, ob es sich bei einem übergebenen Argu-
ment um eine gerade oder ungerade Zahl handelt:
ƒ Eingabe: Erfassen Sie die fragliche Zahl interaktiv mit der Funktion read-
line().
ƒ Verarbeitung: Der Rest einer Division durch zwei (Modulus-Operator %)
verrät eine grade oder ungerade Zahl.
ƒ Ausgabe: Der dreiteilige (ternäre) Operator unterscheidet wahr und falsch.

70
Übungen 2.8

Übung 4
Notieren Sie folgenden Verzeichnisbaum als mehrdimensionales Array in der
Variable $directoryTree.
Hinweis: Jede Verzeichnisebene erzeugt eine neue Array-Dimension.
/home
/max
img1.jpg img2.jpg
/lisa
hausarbeit.doc rechnung.pdf
/system
/programs
firefox word

ƒ Überprüfen Sie den Inhalt von $directoryTree mit var_dump().


ƒ Zeigen Sie mit var_dump() nur Inhalt aus dem Unterverzeichnis /home/max.
ƒ Ergänzen Sie im Array ein neues Benutzerverzeichnis /home/ben mit eini-
gen ausgedachten Dateien und prüfen Sie den Erfolg.

Übung 5
Wie verhält sich der Wert null in einem String-, Integer- oder Boolean-Kon-
text?
Hinweis: Prüfen Sie durch explizites Casting von null in den String-, Inte-
ger- und Boolean-Datentyp. Visualisieren Sie die Ergebnis-Datentypen mit
var_dump().

71
Programmablauf mit
Kontrollstrukturen
steuern
Die bisherigen Skripte haben ihre Anweisungen linear abgearbeitet, d.h. von
oben nach unten, Zeile für Zeile. Jeder Programmlauf führt dabei jede Anwei-
sung genau einmal aus, die Möglichkeiten zur Problemlösung sind begrenzt.
Anspruchsvollere Aufgaben erfordern Kontrolle über den Programmablauf.
Kontrollstrukturen erlauben es, vom linearen Fluss abzuweichen:
ƒ Verzweigungen führen Programmabschnitte nur unter bestimmten Bedin-
gungen aus. Sie werden mit den Schlüsselwörtern if, else und elseif for-
muliert.
ƒ Schleifen führen Programmabschnitte wiederholt aus. Sie werden mit den
Schlüsselwörtern foreach, for und while formuliert.

3.1 Programmablauf verzweigen


Geschweifte Klammern fassen einen Programmabschnitt aus einer oder meh-
reren Anweisungen zu einem so genannten Anweisungsblock oder kurz Block
zusammen:
{
Anweisung 1;
Anweisung 2;
// ...
}

Die Ausführung eines Blocks lässt sich an eine Bedingung knüpfen. Ist die
Bedingung nicht erfüllt, wird der Block nicht ausgeführt. Das erlaubt Abwei-
chungen vom linearen Programmablauf, es kommt zu einer Verzweigung.

73
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

3.1.1 Bedingte Anweisung mit if


Eine bedingte Anweisung (»if-Anweisung«) trifft die Entscheidung, ob ein An-
weisungsblock zusätzlich zum normalen Programmablauf ausgeführt werden
soll oder nicht:
if (Bedingung) {
Anweisung(en);
}

Falls die Bedingung in runden Klammern zutrifft, kommt es zur Ausführung


des Blocks (auch if-Klausel genannt), ansonsten nicht. Abbildung 3.1 ver-
gleicht den linearen Programmfluss mit einer bedingten Anweisung.

Abb. 3.1: Ablaufdiagramm einer bedingten Anweisung

Alltagssituationen veranschaulichen bedingte Anweisungen:


ƒ Falls du Hausaufgaben gemacht hast, bekommst du ein Eis.
ƒ Falls Sie mehr als drei Teile kaufen, gibt es 20 % Rabatt.
Die Bedingung einer if-Anweisung ist ein beliebiger Ausdruck, den PHP in
einen Wahrheitswert (true oder false) auswertet. Ergibt die Bedingung nicht
direkt einen booleschen Wert, führt PHP automatisch ein Type Casting in
einen booleschen Wert durch. Abschnitt 2.5 zeigte, welche Werte PHP dabei
als wahr oder falsch interpretiert.

3.1.2 Beispiel: Passwortabfrage


Die Ausgabe eines Katzen-Emojis soll durch eine Passworteingabe geschützt
sein. Das Emoji wird angezeigt, falls das Passwort korrekt ist, ansonsten nicht.

74
Programmablauf verzweigen 3.1

So läuft das fertige Programm


> php protected-kitty.php
Geschützter Inhalt!
Passwort bitte: kitty
(=•́‫)=̀•ܫ‬

Quelltext
echo "Geschützter Inhalt!\n";
$password = readline('Passwort bitte: ');   1  
if ($password == 'kitty') {   2  
echo "(=•́‫\)=̀•ܫ‬n";   3  
}

Listing 3.1: protected-kitty.php

Erklärungen
ƒ   1   Die Funktion readline() unterbricht den Programmlauf und fordert
den Benutzer zur Eingabe des Passworts auf. Das Drücken der Eingabetaste
setzt das Programm fort und weist die Eingabe der Variable $password als
String zu.
ƒ   2   Prüft mit dem Vergleichsoperator == (»ist gleich«; zwei Gleichheitszei-
chen!), ob das eingegebene Passwort mit dem String-Literal kitty überein-
stimmt. Die Evaluierung des gesamten Ausdrucks $password=='kitty' er-
gibt entweder true oder false.
ƒ   3   Anweisung zur Ausgabe des Emojis. Sie erfolgt nur, falls die Bedingung
zuvor true ergab.

3.1.3 Verzweigung mit if...else


Eine Verzweigung (»if-else-Anweisung«) ermöglicht die alternative Ausfüh-
rung zweier Programmblöcke, je nachdem, ob eine Bedingung zutrifft oder
nicht:
if (Bedingung) {
Anweisung(en);
} else {
Anweisung(en);
}

Falls die Bedingung in runden Klammern zutrifft, kommt der der ersten Code-
Block zur Ausführung, ansonsten der alternative »else«-Block.
75
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

Abb. 3.2: Ablaufdiagramm einer Verzweigung

Alltagssituationen zur Veranschaulichung einer Verzweigung:


ƒ Falls mindestens zehn Personen teilnehmen, findet der Kurs statt, sonst
wird eine Absage verschickt.
ƒ Falls ich Zeit habe, koche ich, sonst bestellen wir etwas.

3.1.4 Beispiel: Eintrittskarte Zoo


Ein Programm zur Berechnung des Eintrittspreises für den Zoo soll eine Er-
mäßigung für Kinder unter zehn Jahren berücksichtigen.
So läuft das fertige Programm
> php zoo-ticket.php
Willkommen beim Ticketkauf für den Zoo.
Alter des Besuchers: 7
Der Eintritt kostet €3

Quelltext
echo "Willkommen beim Ticketkauf für den Zoo.\n";
$age = (int) readline('Alter des Besuchers: '); 1
if ($age < 10) { 2
$price = 3; 3
} else {
$price = 5; 4
}
echo "Der Eintritt kostet €$price\n"; 5

Listing 3.2: zoo-ticket.php

76
Programmablauf verzweigen 3.1

Erklärungen
ƒ 1 Die Funktion readline() unterbricht den Programmlauf und fordert
den Benutzer zur Eingabe des Alters auf. Das Drücken der Eingabetaste
setzt das Programm fort und weist die Eingabe der Variable $age als Ganz-
zahl zu.
ƒ 2 Prüfung mit dem Vergleichsoperators < (»kleiner als«), ob das eingege-
bene Alter kleiner als 10 ist. Die Evaluierung des Ausdrucks $age<10 ergibt
true oder false.
ƒ 3 Zuweisung des ermäßigten Eintrittspreises an die Variable $price. Die
Anweisung wird nur ausgeführt, falls die Bedingung zuvor true ergab.
ƒ 4 Zuweisung des normalen Eintrittspreises an die Variable $price. Die An-
weisung wird nur ausgeführt, falls die Bedingung zuvor false ergab.
ƒ 5 Ausgabe des Eintrittspreises, unabhängig welchen Weg das Programm
zuvor durch die Verzweigung genommen hat.

3.1.5 Der ternäre Operator ?:


Der ternäre (oder dreiteilige) Operator wurde bereits in Kapitel 2 kurz vor-
gestellt. Er erinnert an eine if…else-Verzweigung, es handelt sich jedoch um
einen Ausdruck:
Bedingung ? Wert1 : Wert2;

Trifft die Bedingung zu, wird der gesamte Ausdruck zu Wert1 ausgewertet, an-
sonsten zu Wert2. Betrachten Sie folgende if…else-Verzweigung:
if ($age > 17) {
echo 'Erlaubt';
} else {
echo 'Verboten';
}

Der ternäre Operator kann dies kürzer ausdrücken:


echo ($age > 17) ? 'Erlaubt' : 'Verboten';

Listing 3.3: ternary-operator.php

Aufgabe 1

Wie verhält sich der Ausdruck »$name ? $name : 'Unbekannt'« wenn $name
einen leeren bzw. nicht-leeren String enthält?

77
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

3.1.6 Fallunterscheidung mit if…elseif...else


Die Verschachtelung von if...else-Konstrukten ermöglicht komplexere
Fallunterscheidungen mit mehr als zwei Alternativen. Die Unterscheidung von
drei Fällen ist schematisch in Abbildung 3.3 dargestellt und entspricht folgen-
der Code-Struktur:
if (Bedingung 1) {
Anweisung(en);
} else {
if (Bedingung 2) {
Anweisung(en);
} else {
Anweisung(en);
}
}

Verschachtelte Konstrukte werden schnell unübersichtlich. Zur Vereinfa-


chung bietet PHP das Schlüsselwort elseif an.
elseif ist eine Kombination aus if und else: Trifft die ursprüngliche if-Be-
dingung nicht zu, lässt sich als Alternative eine weitere Bedingung prüfen.
Trifft diese Bedingung zu, wird der zugehörige Anweisungsblock ausgeführt.
Es können beliebig viele weitere elseif-Anweisungen folgen. Die optionale
else-Klausel greift, falls keine der vorigen Bedingungen zutraf.

if (Bedingung 1) {
Anweisung(en);
} elseif (Bedingung 2) {
Anweisung(en);
} elseif (Bedingung 3) {
Anweisung(en);
} else {
Anweisung(en);
}

Sobald eine Bedingung zutrifft und der zugehörige Block ausgeführt wurde,
werden keine weiteren Bedingungen mehr geprüft und die gesamte Kontroll-
struktur ist beendet.

78
Programmablauf verzweigen 3.1

Fallunterscheidungen aus dem Alltag:


ƒ Falls Montag ist, soll der Wecker um 7 Uhr klingeln, falls Dienstag ist um
6 Uhr, falls Mittwoch ist um 8 Uhr usw.
ƒ Falls es über 25° Grad hat, ziehe ein T-Shirt an, falls es über 15° hat einen
Pullover, falls es über 5° hat eine Jacke usw.

Abb. 3.3: Ablaufdiagramm einer Fallunterscheidung

3.1.7 Beispiel: Bankautomat


Ein Bankautomat soll passende Gebühren auf den Auszahlungsbetrag er­
heben:
ƒ unter 200 €: 5 %
ƒ unter 500 €: 4 %
ƒ unter 1000 €: 2 %
ƒ ab 1000 €: 1 %
So läuft das fertige Programm
> php cash-machine.php
Willkommen am Bankautomat.
Gewünschte Auszahlung (€): 200
Die Gebühren betragen 4% = 8€

79
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

Quelltext
echo "Willkommen am Bankautomat.\n";
$amount = (int) readline('Betrag (€): '); 1
if ($amount < 200) { 2
$percent = 5;
} elseif ($amount < 500) { 3
$percent = 4;
} elseif ($amount < 1000) {
$percent = 2;
} else { 4
$percent = 1;
}
$fee = round($amount * $percent/100); 5
echo "Auszahlungsgebühr: $percent % = $fee €\n"; 6

Listing 3.4: cash-machine.php

Erklärungen
ƒ 1 Die Funktion readline() unterbricht den Programmlauf und fordert
den Benutzer zur Eingabe des Auszahlungsbetrags auf. Das Drücken der
Eingabetaste setzt das Programm fort und weist die Eingabe der Variable
$amount als Ganzzahl zu.
ƒ 2 Die Fallunterscheidung zur Gebührenstaffelung beginnt. Der erste Fall
mit 5 % Gebühr tritt ein, wenn die Bedingung $amount<200 zutrifft. Die
Variable $percent nimmt den den Wert 5 an und die Kontrollstruktur ist
beendet.
ƒ 3 Traf die erste Bedingung nicht zu, erfolgt die Prüfung der zweiten Be-
dingung $amount<500. Trifft sie zu, tritt der zweite Fall mit 4 % Gebühr ein,
die Variable $percent nimmt den Wert 4 an und die Kontrollstruktur ist
beendet. Trifft sie nicht zu, werden alle folgenden elseif-Bedingungen in
gleicher Weise geprüft.
ƒ 4 Traf keine der vorigen Bedingungen zu, handelt es sich um einen Abhe-
bungsbetrag größer gleich 1000 € und die Gebühr beträgt einheitlich 1 %.
Die Variable $percent nimmt den Wert 1 an.
ƒ 5 Die Auszahlungsgebühr wird berechnet und in der Variable $fee gespei-
chert. Die Funktion round() führt eine Rundung auf null Dezimalstellen
durch.
ƒ 6 Anzeige der berechneten Gebühren.

80
Bedingungen formulieren 3.2

3.2 Bedingungen formulieren


Entscheidungen basieren auf Bedingungen. Eine Bedingung ist ein Ausdruck,
der eine Aussage liefert, ob etwas zutrifft (true) oder nicht (false). Bedingun-
gen werden mit Hilfe von Vergleichsoperatoren formuliert. Logische Operato-
ren verknüpfen mehrere Bedingungen für komplexere Anforderungen.
Bedingungen und logische Verknüpfungen aus dem Alltag:
ƒ Eine Bedingung: Ist heute Werktag?
ƒ Verknüpfte Bedingungen: Ist heute Werktag und nach 8 Uhr?
3.2.1 Vergleichoperatoren
Vergleichsoperationen ergeben den Wahrheitswert true oder false – je nach-
dem, ob der Vergleich zutrifft oder nicht.

Vergleich Bedeutung Beispiel Wahrheitswert


$a < $b Kleiner als 1 < 2 true
2 < 2 false
$a <= $b Kleiner gleich 1 <= 1 true
2 <= 1 false
$a > $b Größer als 1 > 1 false
2 > 1 true
$a >= $b Größer gleich 1 >= 1 true
0 >= 1 false
$a == $b Gleich 1 == 2 false
1 == '1' true
[1, 2] == [1, 2] true
$a != $b Ungleich '1' != 1 false
1 != 2 true
$a === $b Identisch '1' === 1 false
'1' === '1' true
$a !== $b Nicht identisch '1' !== '1' false
'1' !== 1 true

Tab. 3.1: Vergleichsoperatoren

81
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

Beachten Sie, dass das einfache Gleichheitszeichen (=) für Variablen-Zu-


weisungen und nicht für Vergleiche genutzt wird.

3.2.2 Gleichheit (==) und Identität (===)


PHP unterscheidet Gleichheit (==) und Identität (===). Tests auf Gleichheit lie-
fern meist das erwartete Ergebnis:
var_dump('1' == '1'); // => bool(true)
var_dump('1' == 1); // => bool(true)
var_dump('php' == false) ; // => bool(false)
var_dump(null == 1); // => bool(false)

Bei unterschiedlichen Datentypen der beiden Operanden wendet PHP auto-


matisch Umwandlungen des Datentyps an (»Type-Juggling«, siehe Abschnitt
2.5). Dies kann in einigen Situationen überraschen:
var_dump(0 == false); // => true
var_dump(0 == null); // => true
var_dump(null == ''); // => true

PHP entscheidet anhand eines umfangreichen Regelwerks1, wie Operanden


mit unterschiedlichen Datentypen bei einem Vergleich zu konvertieren sind.
Bei 0==false etwa wird die Zahl 0 automatisch in den Wahrheitswert false
konvertiert, der Vergleich false==false ergibt anschließend true.
Bei einem Vergleich auf Identität hingegen vergleicht PHP neben dem Wert
auch den Datentyp. Operanden unterschiedlichen Datentyps sind bei diesem
»strengen« Vergleich niemals gleich:
var_dump('1' === '1'); // => bool(true)
var_dump('1' === 1); // => bool(false)
var_dump(0 === false); // => bool(false)
var_dump(0 === null); // => bool(false)
var_dump(null === ''); // => bool(false)

Faustregel: Ein strenger Vergleich auf Identität ist grundsätzlich


vorzuziehen. Ein Zeichen mehr zu tippen ist einfacher als mögliche
Konvertierungen zu bedenken bzw. später auf unerwartete Grenzfälle
(engl. edge cases) zu stoßen.

1
https://www.php.net/manual/types.comparisons

82
Bedingungen formulieren 3.2

3.2.3 Logische Operatoren


Die Verkettung und Verneinung von Ausdrücken ermöglichen komplexe Be-
dingungen.

Operation Bedeutung Beispiel Wahrheitswert


$a && $b Und 3 > 1 && 3 < 5 true
3 > 1 && 3 < 3 false
true && false false
true && true true
$a || $b Oder 3 > 1 || 3 < 3 true
true || false true
!$a Nicht !(3 === 3) false
!(3 !== 3) true

Tab. 3.2: Logische Operatoren

Ähnlich den Regeln arithmetischer Operatoren (z.B. »Punkt vor Strich«),


wertet PHP eine Verkettung mehrerer logischer Operatoren nach fester Rang-
folge aus. Der logische Und-Operator && steht z.B. in der Rangfolge über dem
logischen Oder-Operator || und bindet seine Operanden daher stärker. Man
sagt, dass && eine höhere Präzedenz als || hat.
false && false || true // ergibt true

Das Ergebnis des Ausdrucks ist true, da false&&false zunächst zu false und
dann false||true zu true ausgewertet wird. Klammern verbessern die Les-
barkeit in jedem Fall, auch wenn sie nicht notwendig sein sollten:
(false && false) || true // ergibt true

Um die Rangfolge zu ändern, sind Klammer zwingend erforderlich:


false && (false || true) // ergibt false!

3.2.4 Beispiel: Schaltjahrprüfung


Für eine Jahreszahl soll geprüft werden, ob es sich um ein Schaltjahr handelt.
Die Regeln für Schaltjahre lauten:

83
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

ƒ Ein Schaltjahr ist durch vier teilbar: 2016, 2020, 2024 ...
ƒ Es ist jedoch kein Schaltjahr, wenn es durch 100 teilbar ist: 1900, 2100, 2200
ƒ Ist das Jahr durch 400 teilbar, ist es dennoch ein Schaltjahr: 1600, 2000,
2400
So läuft das fertige Programm
> php leap-year-test.php
Willkommen beim Schaltjahrtest.
Jahreszahl: 2000
2000 ist ein Schaltjahr

Quelltext
echo "Willkommen beim Schaltjahrtest.\n";
$year = (int) readline('Jahreszahl: ');
if ($year % 4 === 0 && ($year % 100 !== 0 || $year % 400 === 0)) {
echo "$year ist ein Schaltjahr";
} else {
echo "$year ist kein Schaltjahr";
}

Listing 3.5: leap-year-test.php

Erklärungen
Die Erklärungen konzentrieren sich auf die if-Bedingung zur Formulierung
der Schaltjahr-Regeln. Die drei Regeln können zunächst unabhängig in PHP-
Code übersetzt werden:
ƒ Durch 4 teilbar, d.h. ohne Rest: $year % 4 === 0
ƒ Nicht durch 100 teilbar, d.h. mit Rest: $year % 100 !== 0
ƒ Durch 400 teilbar, d.h. ohne Rest: $year % 400 === 0
Die einzelnen Bedingungen müssen nun entsprechend der Regeln logisch
kombiniert werden. Ein Jahr ist ein Schaltjahr, wenn:
ƒ es durch 4 und nicht durch 100 teilbar ODER
ƒ es durch 4 und auch durch 400 teilbar ist.
Mit Hilfe logischer Operatoren übersetzt in PHP-Code:
($year % 4 === 0 && $year % 100 !== 0) ||
($year % 4 === 0 && $year % 400 === 0)

84
Programmabschnitte mit Schleifen wiederholen 3.3

Die Grundbedingung, dass ein Schaltjahr durch vier teilbar sein muss, lässt
sich für eine kompaktere Schreibweise »ausklammern«:
$year % 4 === 0 && ($year % 100 !== 0 || $year % 400 === 0)

Für die Jahre 2000, 2020, 2022 und 2100 ergeben sich nach Auflösung der
Vergleiche folgende logische Verkettungen:
2000: true && (false || true) => true
2020: true && (true || false) => true
2022: false && (true || false) => false
2100: true && (false || false) => false

Genau genommen wertet PHP logische Ausdrücke nicht weiter aus, sobald
das Ergebnis feststeht. Für das Jahr 2022, welches nicht durch vier teilbar ist,
steht das gesamte Ergebnis bereits unveränderlich nach Auswertung des ers-
ten Operanden zu false fest.

3.3 Programmabschnitte mit Schleifen


wiederholen
Eine Schleife dient zur mehrfachen Wiederholung eines Programmabschnitts.
Die Programmausführung dreht sich im Kreis, solange eine bestimmte Be-
dingung erfüllt ist. Ein einzelner Schleifenddurchlauf wird Iteration genannt
(Lateinisch für »Prozess des Wiederholens ähnlicher Handlungen«).

Abb. 3.4: Ablaufdiagramm einer Schleife

PHP bietet die Schleifentypen foreach, for und while. Oft können Aufgaben
mit verschiedenen Typen gleichermaßen gelöst werden, jeder Typ ist jedoch
auf bestimmte Fälle spezialisiert.

85
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

3.3.1 Arrays durchlaufen – foreach


Eine foreach-Schleife durchläuft die Elemente (engl. items) eines Arrays und
führt für jedes Element den gleichen Anweisungsblock aus.

Abb. 3.5: Ablaufdiagramm einer foreach-Schleife

Die foreach-Schleife iteriert nach dem folgenden Schema über die Menge der
Array-Elemente.
$array = [Wert1, Wert2, ...]; 1
foreach ($array as $element) { 2
// 1. Lauf mit $element = Wert1
// 2. Lauf mit $element = Wert2 usw.
Anweisung(en);
}

ƒ 1 $array steht für ein beliebiges Array, dessen Elemente durchlaufen wer-
den sollen.
ƒ 2 Der Bezeichner $element ist frei wählbar und nimmt in jeder Iteration
den Wert des nächsten Elements aus $array an.
Der Auftrag an PHP lautet: »Solange Elemente im Array sind, führe den An-
weisungsblock aus. Rücke nach jedem Durchlauf ein Element vor. Sind keine
weiteren Elemente mehr im Array, beende die Schleife. Sind gar keine Ele-
mente im Array (leeres Array), führe den Block nie aus«. Folgendes Skript
berechnet für alle Zahlen in einem Array die Quadratwurzel:

86
Programmabschnitte mit Schleifen wiederholen 3.3

$numbers = [1, 2, 3, 4, 5];


foreach ($numbers as $number) {
echo $number ** 2 . ' ';
}
// => 1 4 9 16 25

Listing 3.6: square-roots.php

Die erweiterte Syntax erlaubt Zugriff auf Schlüssel und Wert:


$array = [Key1 => Wert1, Key2 => Wert2, ...];
foreach ($array as $key => $element) {
// 1. Lauf mit $key=Key1 und $element=Wert1
// 2. Lauf mit $key=Key2 und $element=Wert2 usw.
Anweisung(en);
}

Hat das Array keine expliziten Schlüssel, verwendet PHP die intern vergebe-
nen, numerischen Schlüssel (0, 1, 2 …). Folgendes Skript gibt Schlüssel und
Werte eines Arrays aus:
$letters = ['A', 'B', 'C'];
foreach ($letters as $key => $letter) {
echo "$key:$letter ";
}
// => 0:A 1:B 2:C

Listing 3.7: foreach-loop-letters.php

Analogien einer foreach-Schleife aus dem Alltag:


ƒ Nimm jeden Apfel aus dem Korb und schneide ihn in Stücke.
ƒ Nimm jede Flasche aus der Tasche und gebe sie in den Pfandautomaten.
Aufgabe 2

Schreiben Sie eine foreach-Schleife über das Array ['A', 'B', 'C'], die zur
Ausgabe A-B-C führt (Bindestriche nur zwischen den Buchstaben!).
Tipp: Der Array-Schlüssel sagt Ihnen, in welcher Iteration Sie sich be­
finden. Unterscheiden Sie durch eine if-Anweisung.

87
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

3.3.2 Beispiel: Getränkekarte


Die Getränkekarte eines Restaurants soll aufgelistet werden. Die Karte enthält
Namen und Preise der Getränke.
So läuft das fertige Programm
> php drinks-menu.php
Wasser: 2,00€
Cola: 2,50€
Kiba: 3,50€

Quelltext
$drinks = [ 1
'Wasser' => 2,
'Cola' => 2.5,
'Kiba' => 3.5,
];
foreach ($drinks as $drink => $price) { 2
echo "$drink: " . number_format($price, 2, ',') . "€\n"; 3
}

Listing 3.8: drinks-menu.php

Erklärungen
ƒ 1 Initialisieren einer Getränkeliste als Array $drinks. Jedes Element ordnet
einem Getränkenamen (Schlüssel) einen Preis zu (Wert).
ƒ 2 Die foreach-Schleife iteriert über die Elemente des Arrays $drinks, der
Block wird für jedes Element einmal ausgeführt. Bei jedem Durchlauf ent-
hält $drink den Schlüssel des aktuellen Elements (Getränkename) und
$price den zugehörigen Wert (Getränkepreis).
ƒ 3 Für jedes Getränk in der Liste wird derselbe Code zur Ausgabe ausge-
führt. Die Funktion number_format() formatiert die Preise auf zwei Dezi-
malstellen und einem Komma als Dezimaltrenner.

3.3.3 Zählschleife – for


Bei einer for-Schleife durchläuft der Wert einer Zählvariablen einen Zahlen-
bereich von einem Anfangswert bis zu einem Endwert. Der Anweisungsblock
wird für jeden Wert der Zählvariablen einmal ausgeführt. Eine solche Zähl-

88
Programmabschnitte mit Schleifen wiederholen 3.3

schleife ist sinnvoll, wenn die Anzahl der Iterationen vorher bekannt ist. Ein
Beispiel zur Ausgabe der Ziffern 0 bis 9 verdeutlicht den Ablauf:
for ($i = 0; $i <= 9; $i++) {
echo "$i ";
}
// => 0 1 2 3 4 5 6 7 8 9

Listing 3.9: for-loop-numbers.php

Der Kopf der Schleife in der ersten Zeile enthält drei mit Semikolons getrenn-
te Bereiche:
ƒ $i=0: Einmalige Anweisung zur Initialisierung der Zählvariablen $i vor
Ausführung der Schleife.
ƒ $i<=9: Prüfbedingung vor jedem Schleifendurchlauf; solange sie wahr ist,
erfolgt eine Wiederholung der Schleife.
ƒ $i++: Anweisung zur Ausführung nach jedem Schleifendurchlauf; sie er-
höht die Zählvariable um eins.

Abb. 3.6: Ablaufdiagramm einer for-Schleife

Verallgemeinert lautet die Struktur:


for (Initialisierung; Bedingung; Fortsetzung) {
Anweisung(en);
}

89
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

ƒ Initialisierung: Einmalige Anweisung zu Beginn.


ƒ Bedingung: Prüfbedingung vor jedem Durchlauf.
ƒ Fortsetzung: Anweisung nach jedem Durchlauf.
Durch Anpassung der Fortsetzung kann die Zählweise der Schleife variieren,
zum Beispiel in Zweierschritten ($i=$i+2) oder rückwärts ($i--).
Beispiele aus dem Alltag verdeutlichen eine Zählschleife:
ƒ Laufe zehnmal um den Block.
ƒ Notiere die Zahlen 1 bis 10 im Quadrat.
ƒ Schlage fünf Eier auf und gib sie zum Teig.
3.3.4 Beispiel: Countdown
Ein Countdown soll von der Zahl 10 bis 0 jede Sekunde um eins herunter-
zählen.
So läuft das fertige Programm
> php countdown.php
Countdown läuft.
10 9 8 7 6 5 4 3 2 1 -- START!

Quelltext
echo "Countdown läuft.\n";
for($i = 10; $i > 0; $i--) { 1
echo "$i "; 2
sleep(1); 3
}
echo "-- START!\n";

Listing 3.10: countdown.php

Erklärungen
ƒ 1 Die Zählvariable $i wird mit dem Wert 10 initialisiert. Solange $i>0
wahr ist, soll sich die Schleife wiederholen. Nach jedem Durchlauf wird die
Zählvariable mittels $i-- um eins verringert.
ƒ 2 Ausgabe des aktuellen Zählerstands.
ƒ 3 Die Funktion sleep() pausiert das Programm für die übergebene Anzahl
an Sekunden.

90
Programmabschnitte mit Schleifen wiederholen 3.3

3.3.5 Bedingte Wiederholung – while


Die bedingte Wiederholung mit einer while-Schleife bietet sich für Situatio­
nen an, in denen im Gegensatz zur Zählschleife die Anzahl der Iterationen
unbekannt ist:
while (Bedingung) {
Anweisung(en);
}

Der Anweisungsblock wird solange wiederholt, bis die Bedingung nicht mehr
erfüllt ist.

Abb. 3.7: Ablaufdiagramm einer while-Schleife

Analogien aus dem Alltag verdeutlichen das Konzept der bedingten


Wiederholung:
ƒ Solange die Schraube locker ist, ziehe nach.
ƒ Solange Platz im Korb ist, pflücke eine Erdbeere.
ƒ Solange es nicht 25° im Raum sind, lege ein Scheit ins Feuer.
3.3.6 Beispiel: Lostopf
Es sollen beliebig viele Namen in einen Lostopf gegeben und anschließend ein
Name daraus ausgelost werden.
So läuft das fertige Programm
> php name-lottery.php
Bitte Namen eingeben.

91
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

Auslosung mit leerer Eingabe starten.


Name 1: Sina
Name 2: Jan
Name 3: Joana
Name 4:
Ausgeloster Name: Jan

Quelltext
echo "Bitte Namen eingeben.\n";
echo "Auslosung mit leerer Eingabe starten.\n";
$names = []; 1
while($name = readline('Name: ')) { 2
$names[] = $name; 3
}
$randomKey = random_int(0, count($names)-1); 4
echo "Ausgeloster Name: $names[$randomKey]\n"; 5

Listing 3.11: name-lottery.php

Erklärungen
ƒ 1 Initialisierung eines leeren Arrays $names zur Aufnahme der eingegebe-
nen Namen.
ƒ 2 readline() unterbricht den Programmablauf und wartet auf die Namens­
eingabe. Das Drücken der Eingabetaste setzt das Programm fort und weist
die Eingabe als String der Variablen $name zu. Die Auswertung von $name
dient gleichzeitig als Bedingung der while-Schleife: Ein nicht-leerer String
evaluiert zu true, ein leerer String zu false. Solange die Eingabe nicht leer
ist, folgt eine weitere Iteration.
ƒ 3 Jeder eingegebene Name wird dem Array $names als neues Element am
Ende hinzugefügt. Ohne explizite Schlüssel vergibt PHP automatisch ganz-
zahlige Schlüssel 0, 1, 2 usw. Bei n eingegebenen Namen enthält das Array
damit [0=>Name1, 1=>Name2, ..., n=>NameN].
ƒ 4 Die Auslosung. Die Funktion random_int() lost einen Zufallsschlüssel
des Arrays $names aus und speichert ihn in der Variable $randomKey. Der
Schlüssel muss zwischen dem ersten Schlüssel 0 und dem höchsten Schlüs-
sel des Arrays liegen. count() ermittelt die Anzahl von Elementen in einem
Array. Da die Schlüssel bei 0 starten, hat der höchste Schlüssel des Arrays
einen um eins geringeren Wert.

92
Programmabschnitte mit Schleifen wiederholen 3.3

ƒ 5 Verwendung von $randomKey zur Anzeige des ausgelosten Namens aus


$names.

3.3.7 Schleifen abbrechen – break


Das Schlüsselwort break bricht eine Schleife frühzeitig ab. Es finden keine
weiteren Iterationen mehr statt. Folgendes Skript durchläuft ein Array mit
Farben und bricht die Schleife ab, sobald es auf die Farbe rot trifft.
$colors = ['blau', 'grün', 'rot', 'gelb'];
foreach ($colors as $color) {
if ($color === 'rot') {
break;
}
echo "$color ";
}
// => blau grün

Listing 3.12: break.php

3.3.8 Schleife beim nächsten Durchlauf fortsetzen – continue


Das Schlüsselwort continue bricht die aktuelle Iteration ab und lässt eine
Schleife direkt zum nächsten Durchlauf springen. Folgendes Skript durchläuft
die Zahlen 1 bis 9, überspringt allerdings Zahlen, die durch drei teilbar sind:
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
foreach ($numbers as $number) {
if ($number % 3 === 0) {
continue;
}
echo "$number ";
}
// => 1 2 4 5 7 8

Abb. 3.8: continue.php

3.3.9 Endlosschleifen
Die Bedingung einer Schleife kann (ungewollt) zu einer endlosen Wieder-
holung des Anweisungsblocks führen, das Programm befindet sich in einer
Endlosschleife:

93
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

while (true) { // Bedingung ist immer erfüllt


echo 'PHP ';
}

Listing 3.13: inifinite-loop.php

Ein Kommandozeilen-Programm, das sich nicht selbst beendet, können Sie


mit der Tastenkombination (Strg)+(C) abbrechen. Im Browser käme es nach
einiger Zeit zu einer Zeitüberschreitung (engl. timeout) und dem Abbruch der
Anfrage.

3.4 Alternative Syntax für Kontrollstrukturen


Kontrollstrukturen können über mehrere PHP-Bereiche aufgeteilt werden.
Dies ist nützlich, wenn die Anweisungsblöcke größtenteils statischen Text
(oder HTML) ausgeben sollen:
<?php if ($planet === 'Merkur') { ?>
Der Merkur ist mit einem Durchmesser von knapp 4880 Kilometern der
kleinste, mit einer ...
<?php } elseif ($planet === 'Venus') { ?>
Die Venus ist mit einer durchschnittlichen Sonnenentfernung von
108 Millionen Kilometern ...
<?php } elseif ($planet === 'Erde') { ?>
...
<?php } else { ?>
Unbekannter Planet.
<?php } ?>

Die Notation mit geschweiften Klammern wird leicht unübersichtlich und die
Zuordnung schließender Klammern fällt schwer. Für bessere Lesbarkeit sorgt
eine alternative Syntax:
<?php if ($planet === 'Merkur'): ?>
Der Merkur ist ...
<?php elseif ($planet === 'Venus'): ?>
Die Venus ist ...
<?php elseif ($planet === 'Erde'): ?>
...
<?php else: ?>

94
Übungen 3.5

Unbekannter Planet.
<?php endif ?>

Listing 3.14: alternative-control-structures.php

Die öffnenden geschweiften Klammern werden durch Doppelpunkte ersetzt.


Die schließenden geschweiften Klammern entfallen, nur die letzte wird durch
ein Schlüsselwort passend zur Kontrollstruktur ersetzt, um den Bezug herzu-
stellen: endif, endforeach, endfor, endwhile.

Aufgabe 3

Schreiben Sie eine foreach-Schleife in alternativer Syntax die über ein Ar-
ray $planets mit Planetennamen iteriert und für jeden Namen Willkom-
men auf Planet $planet. ausgibt.

3.5 Übungen
Übung 1
Ermitteln Sie die Wahrheitswerte folgender Ausdrücke:
Vergleich Wahr oder falsch?
0 >= 2
'drei' == 'drei'
'3' === 3
[] === []
true || false
true && false
!false && true
7 > 4 && 7 < 6
7 > 4 && 7 < 6 || 7 < 9
'' == false
'' === false

95
Kapitel 3 Programmablauf mit Kontrollstrukturen steuern

Übung 2
Schreiben Sie ein Skript zur interaktiven Abfrage eines Geburtsjahrs. Akzep-
tieren Sie nur Geburtsjahre zwischen dem aktuellen Jahr und dem Jahr vor
125 Jahren. Teilen Sie dem Benutzer mit, ob seine Eingabe gültig ist oder nicht.
Tipp: Nutzen Sie readline() zur Abfrage des Geburtsjahrs und date('Y') zur
Ermittlung des aktuellen Jahrs.

Übung 3
Schreiben Sie ein Skript, das die Zahlen aus dem Array [2, 6, 8, 19] aufsum-
miert und das Ergebnis ausgibt. Nutzen Sie eine foreach-Schleife.

Übung 4
Schreiben Sie ein Skript, das mit Hilfe von zwei verschachtelten for-Schleifen
folgendes Muster ausgibt:
*
* *
* * *
* * * *
* * * * *

Tipp: Verwenden Sie die Zählvariable der äußeren Schleife zur Bestimmung
der Abbruchbedingung der inneren Schleife.

Übung 5
Schreiben Sie ein Skript, das interaktiv sechs verschiedene Lottozahlen zwi-
schen 1 und 49 vom Benutzer abfragt und am Ende anzeigt.
Tipps:
ƒ Sammeln Sie die getippten Zahlen im Array $lottoTip.
ƒ Nutzen Sie eine while-Schleife, die sich so lange wiederholt, bis sechs gül-
tige Zahlen eingesammelt wurden. Die Funktion count() ermittelt die An-
zahl an Elementen in einem Array. Bei einer ungültigen Zahl beenden Sie
die aktuelle Iteration mit continue vorzeitig.
ƒ Dopplungen können Sie mit der Funktion array_search(Zahl, $lottoTip)
erkennen. Sie liefert false, wenn Zahl noch nicht in $lottoTip existiert.

96
Programmierfehler und
PHP-Konfiguration
Haben Sie bereits versehentlich eine der erlernten Regeln zum Schreiben von
PHP-Skripten verletzt und einen Fehler bei der Programmausführung verur-
sacht? Einen Programmierfehler bezeichnet man als Bug (engl. für »Ungezie-
fer«), die Ursachensuche und Fehlerbeseitigung als Debugging. Bugs können
frustrierend sein, jedoch trägt kaum etwas mehr zum Verständnis bei als die
gewonnenen Erfahrungen bei der Suche nach Fehlerursachen.
Dieses Kapitel stellt unterschiedliche Arten von Programmierfehlern und das
System der Fehlerstufen in PHP vor. Außerdem lernen Sie durch Konfigura-
tion der php.ini-Datei das Verhalten einer PHP-Installation zu beeinflussen.

4.1 Programmierfehler
Es gibt drei Fehlerarten: Syntaxfehler, Laufzeitfehler und logische Fehler.

4.1.1 Syntaxfehler
Im folgenden Skript wurde bei der Verwendung der date()-Funktion verges-
sen, das runde Klammerpaar zu schließen:
echo 'Das aktuelle Datum: ';
echo date('d.m.Y';

Listing 4.1: syntax-error.php

Die Ausführung des Skripts bricht mit einem Syntaxfehler (engl. syntax error)
ab. Die Fehlermeldung gibt Aufschluss über das Problem:
> php syntax-error.php
Parse error: syntax error, unexpected token ";", expecting ")"

97
Kapitel 4 Programmierfehler und PHP-Konfiguration

Wie jede Sprache definieren auch Programmiersprachen eine Syntax, d.h. ein
Regelsystem, welche Zeichenfolgen gültig sind. Für PHP besagt eine dieser
Regeln, dass ein Funktionsaufruf aus einem Wort, gefolgt von einem runden
Klammerpaar, bestehen muss, etwa date(). PHP findet den Fehler bereits bei
der Analyse des Quelltexts (dem parsen) und bricht schon vor der Interpre-
tation des Codes ab. Die Ausgabe aus der korrekten ersten Zeile erfolgt daher
nicht. Syntaxfehler sind am leichtesten zu vermeiden, da sie ohne Programm­
ausführung bereits im PHP-Quelltext-Editor zu erkennen sind:

Abb. 4.1: Syntaxfehler im Quelltext-Editor VS Code

4.1.2 Laufzeitfehler
Ist die Syntax eines Skripts korrekt, beginnt der PHP-Interpreter die Anwei-
sungen abzuarbeiten – das Skript »läuft«. Kommt es bei einer Anweisung zu
einem Fehler, d.h. zur Laufzeit (engl. runtime) des Programms, spricht man
von einem Laufzeitfehler (engl. runtime error).
Folgendes Skript ermittelt die anteilige Gewinnsumme für jeden Gewinner
einer Lotterie, die einen Jackpot ausschüttet. Obwohl das Skript syntaktisch
korrekt ist, kann es zu einem Laufzeitfehler kommen:
$jackpot = (int) readline('Jackpot (€): ');
$winners = (int) readline('Anzahl Gewinner: ');
echo '€ je Gewinner: ' . ($jackpot / $winners);

Listing 4.2: jackpot.php

Die Anweisungen zum Einlesen der Benutzereingaben sind immer erfolg-


reich. Der Erfolg der letzten Anweisung zur Division zweier Zahlen hängt
jedoch von der Eingabe ab. Bei einer Eingabe von null Gewinnern kommt es

98
PHP konfigurieren: die php.ini-Datei 4.2

zu einem Laufzeitfehler und dem Abbruch des Programms, da eine Division


durch die Zahl Null mathematisch nicht definiert ist.
> php jackpot.php
Jackpot (€): 1000
Anzahl Gewinner: 0
Fatal error: Uncaught DivisionByZeroError: Division by zero

Umsichtige Programmierung beugt Laufzeitfehlern vor. Der Programmierer


muss im Beispiel vorhersehen, dass eine Gewinnerzahl kleiner eins nicht sinn-
voll ist und die Division durch null nie auftreten kann.
Nicht jeder Laufzeitfehler führt zu einem Skriptabbruch, PHP un-
terscheidet verschiedene Fehlerstufen (Abschnitt 4.3). Bei weniger
schwerwiegenden Fehlern läuft das Skript weiter.

Aufgabe 1

Verbessern Sie das Skript jackpot.php, um eine Division durch null auszu-
schließen. Prüfen Sie zuvor die Anzahl der Gewinner.

4.1.3 Logische Fehler


Logische Fehler sind am schwierigsten zu erkennen: Obwohl ein Programm
keinen Fehler verursacht, verhält es sich nicht korrekt. In folgendem Skript
zur Umrechnung von Grad Fahrenheit in Grad Celsius hat sich der Program-
mierer bei der Formel vertippt. Die leicht falschen Ergebnisse fallen womög-
lich länger gar nicht auf.
$fahrenheit = (int) readline('°F: ');
echo '°C: ' . ($fahrenheit - 31) * 5/9;
// Korret muss es heißen: $fahrenheit - 32

Listing 4.3: fahrenheit-to-celsius.php

Logische Fehler können nur durch sorgfältiges Programmieren und Maßnah-


men zur Qualitätskontrolle (z.B. gute Planung, 4-Augen-Prinzip, Testen) auf
ein Minimum reduziert werden.

4.2 PHP konfigurieren: die php.ini-Datei


Bisher konnten Sie beobachten, dass Syntax- und Laufzeitfehler offen an-
gezeigt wurden. Im nächsten Abschnitt lernen Sie durch Konfiguration der

99
Kapitel 4 Programmierfehler und PHP-Konfiguration

PHP-Installation Einfluss auf diese Anzeige zu nehmen. Dieser Abschnitt er-


klärt zunächst allgemein, wie die Konfiguration einer PHP-Installation über
die zentrale Konfigurationsdatei php.ini funktioniert.

4.2.1 Was ist die php.ini-Datei?


Die php.ini-Datei ist die zentrale, textbasierte Konfigurationsdatei einer PHP-
Installation. Sie bestimmt, mit welchen Einstellungen PHP ausgeführt wird.
Einstellungen werden durch so genannte Direktiven vorgenommen und bei
Bedarf individuell angepasst. Die Direktive memory_limit etwa bestimmt, wie-
viel Arbeitsspeicher der Lauf eines PHP-Skripts maximal nutzen darf (hier
128 Megabytes), um den Computer vor Überlastung zu schützen:
memory_limit = 128M

Reicht der Speicher für speicherintensive Aufgaben (z.B. das Verarbeiten sehr
großer Datenmengen) nicht aus, bricht das Skript ab:
Fatal error: Allowed memory size of XXXXX bytes exhausted

Wird ein höherer Speicherverbrauch erwartet und ist genügend Speicher vor-
handen, kann die Direktive angepasst werden (hier auf 256 Megabyte):
memory_limit = 256M

Eine Übersicht aller Direktiven gibt www.php.net/ini.list. Die php.ini ist


zudem umfangreich kommentiert. Ein Kommentar beginnt mit einem Semi­
kolon:
; Maximum amount of memory a script may consume
memory_limit = 128M

4.2.2 Wo ist die php.ini und wie wird sie bearbeitet?


Die Option --ini des php-Befehls verrät den Speicherort der php.ini-Datei:
> php --ini
Loaded Configuration File: C:\Programme\php\php.ini ...

Die php.ini kann mit einem Text-Editor bearbeitet werden, z.B. Visual Studio
Code (Kapitel 1). Der Befehl code öffnet die Datei direkt von der Kommando-
zeile aus in VS Code:
> code /C/Programme/php/php.ini

100
PHP konfigurieren: die php.ini-Datei 4.2

Bestimmte Direktiven in der sehr langen Datei finden Sie schnell mit der
Suchfunktion des Editors ((Strg)+(F)). Gespeicherte Änderungen an der
php.ini greifen sofort bei der nächsten Verwendung des php-Befehls.

Unter Windows werden Sie aufgefordert, Änderungen an der php.ini


als Administrator zu bestätigen.

Alle aktuellen PHP-Einstellungen zeigt die Option -i des php-Befehls:


> php -i
... memory_limit => 128M ...

Auf der Kommandozeile greifen php.ini-Änderungen sofort, im Webser-


ver (Kapitel 5) erst nach Neustart des Webservers!

4.2.3 PHP-Einstellungen zur Laufzeit


PHP-Einstellungen in der lokalen php.ini-Datei gelten nur in der lokalen Ent-
wicklungsumgebung. Läuft eine lokal entwickelte PHP-Anwendung später in
einer anderen Umgebung, z.B. beim Webhoster, könnten dort andere Einstel-
lungen herrschen.
Mit der Funktion ini_set() ist es möglich, die meisten PHP-Einstellungen
auch zur Laufzeit eines Skripts (bzw. einer ganzen Anwendung) vorzuneh-
men. Diese Einstellungen haben dann Vorrang vor den Einstellungen der php.
ini, und die PHP-Software wird unabhängig von den Einstellungen einer be-
stimmten Umgebung. Folgende Einstellung für display_errors (Fehlermel-
dungen anzeigen An/Aus) etwa garantiert, dass die Einstellung ab dem Aufruf
der Funktion gilt, unabhängig von der php.ini:
// ... hier gilt "display_errors" aus php.ini
ini_set('display_errors', false);
// ... hier gilt sicher "display_errors=false"

Listing 4.4: ini-set-and-get.php

Das Gegenstück ini_get() liest eine PHP Einstellung aus:


var_dump(ini_get('display_errors'));
// ergibt leeren String für "Aus" oder "1" für "An"

101
Kapitel 4 Programmierfehler und PHP-Konfiguration

4.2.4 PHP-Erweiterungen aktivieren


PHP-Erweiterungen erweitern die eingebauten Funktionen von PHP um
spezielle Themengebiete, z.B. Bildbearbeitung, Dateityp-Erkennung oder My­
SQL-Datenbankanbindung. Eine Übersicht der lokal aktivierten PHP-Erwei-
terungen zeigt der Befehl php -m bzw. die Übersicht mit phpinfo() (Kapitel 5).
Unter macOS sind die meisten Erweiterungen standardmäßig aktiviert. Unter
Windows aktivieren Sie eine Erweiterung durch Entfernen des Kommentar-
zeichens (Semikolon) vor der entsprechenden Zeile in der php.ini:
extension=Erweiterungsname

Unter Linux installieren Sie zusätzliche Erweiterungen wie zuvor PHP selbst
(Abschnitt 1.7.3) mit dem Paketmanager:
> sudo apt install php-Erweiterungsname

Aufgabe 2

Im weiteren Verlauf des Buchs werden die Erweiterungen fileinfo und gd


genutzt. Stellen Sie ihre Aktvierung sicher.

4.3 Fehlerstufen
Nicht jeder von PHP erkannte Fehler führt zum Abbruch der Programmaus-
führung. PHP unterscheidet die Schwere von Fehlern in verschiedenen Feh-
lerstufen (engl. error levels). Die wichtigsten Stufen lauten:

Fehlerstufe Beschreibung Auswirkung


E_PARSE Syntax-Fehler Skriptabbruch
E_ERROR Schwerer Laufzeitfehler Skriptabbruch
E_WARNING Warnung zur Laufzeit Skript läuft weiter
Tab. 4.1: Fehlerstufen in PHP

Mit der Warnung kennt PHP einen Laufzeitfehler, der nicht zum Skriptab-
bruch führt, sondern lediglich auf ein mögliches Problem aufmerksam ma-
chen soll. Folgendes Skript verwendet eine zuvor nicht deklarierte Variable:

102
Fehlersichtbarkeit einstellen 4.4

echo $myUndefinedVariable;
echo '*** Skript läuft noch ***';

Listing 4.5: undefined-variable-warning.php

Der PHP-Interpreter gibt eine Warnung aus, sobald er auf die undefinierte
Variable trifft:
> php undefined-variable-warning.php
Warning: Undefined variable $myUndefinedVariable
*** Skript läuft noch ***

Die Toleranz von PHP gegenüber vermeintlich leichten Fehlern vereinfacht


den Einstieg, sollte aber nicht dazu verleiten, diese zu ignorieren. Jede Fehler-
meldung deutet auf ein potenzielles Problem hin. Skripte sollten frei von allen
Fehlermeldungen laufen. Um auf Fehler aufmerksam zu werden, müssen sie
sichtbar sein.

4.4 Fehlersichtbarkeit einstellen


Eine frisch installierte PHP-Entwicklungsumgebung zeigt Fehler bei der Aus-
führung eines Skripts direkt an, sobald der Interpreter den Fehler bemerkt.
Direktiven in der php.ini können die Anzeige von Fehlern jedoch unterdrü-
cken. Das kann zu einem Skriptabbruch ohne sichtbare Fehlermeldung füh-
ren und die Fehlersuche erschweren. Grundsätzlich gilt:
ƒ Eine Entwicklungsumgebung sollte dem Programmierer so viele Fehlerde-
tails wie möglich direkt anzeigen.
ƒ Eine Produktionsumgebung sollte Benutzern einer Software anstelle tech-
nischer Fehlerdetails benutzerfreundliche Fehlermeldungen anzeigen.

Öffentlich sichtbare Fehlerdetails können Sicherheitslücken zum Miss-


brauch einer Software offenbaren.

In einer Produktionsumgebung werden Fehlerdetails durch versteckte Auf-


zeichnungen, dem so genannten Logging, nachvollzogen. Das Logging zeich-
net technische Fehlermeldungen im Hintergrund auf, z.B. in einer Datei oder
Datenbank..
Die Konfigurationsdatei php.ini bietet Einstellungen zur Fehlersichtbarkeit
und dem Logging. Die Direktive display_errors bestimmt durch Zuweisen
des Werts On oder Off das Ein- bzw. Ausschalten der Fehleranzeige. Folgende
Konfiguration schaltet die direkte Fehleranzeige aus:

103
Kapitel 4 Programmierfehler und PHP-Konfiguration

display_errors = Off ; Fehleranzeige: Aus

Die Direktive error_log konfiguriert den Dateipfad zu einer Log-Datei. Mit


folgender Konfiguration protokolliert PHP Fehler in die Datei php_errors.log
im Benutzerverzeichnis:
error_log = /C/Users/Philipp/php_errors.log

Die Direktive error_reporting konfiguriert, welche Fehlerstufen PHP (Ab-


schnitt 4.3) meldet. Sie sollte zur Meldung aller Fehler auf E_ALL eingestellt
sein.
error_reporting = E_ALL ; Alle Fehlerstufen melden

Beachten Sie, dass error_reporting nur konfiguriert, welche Fehlerstufen ge-


meldet werden sollen, nicht aber, ob Fehler angezeigt (display_errors) oder
geloggt (error_log) werden. Falls Sie mit »unerklärlichen« Fehlern in PHP-
Skripten konfrontiert sind, denken Sie immer an die Direktiven zur Fehleran-
zeige – häufig ist die gesuchte Fehlermeldung lediglich nicht sichtbar.

Aufgabe 3

Wie lautet die Direktive zum generellen Ein- bzw. Ausschalten des Log-
gings?
Tipp: www.php.net/ini.list

4.5 Übungen
Übung 1
Stellen Sie in der php.ini sicher, dass alle Fehler in eine Log-Datei protokolliert
werden und schalten Sie die direkte Fehleranzeige aus. Verursachen Sie Fehler
und beobachten Sie die Logdatei.

Übung 2
Schreiben Sie ein Skript, das den erlaubten Speicherverbrauch anzeigt. Än-
dern Sie den erlaubten Speicherverbrauch zur Laufzeit auf 500MB und prüfen
Sie die veränderte Einstellung erneut. Hinweis: Direktiven mit Angaben zu
Speicherkapazitäten erfolgen in Bytes. Abkürzungen sind mit den Suffixen K
(für Kilobyte), M (für Megabyte) und G (für Gigabyte) möglich.

104
Funktionen
Eine Funktion ist ein hinterlegtes »Rezept« zur Lösung einer Programmierauf-
gabe, das bei Bedarf angewendet werden kann. PHP ist bekannt für die Viel-
falt seiner eingebauten Funktionen, die standardmäßig zur Verfügung stehen.
Darüber hinaus können Sie eigene Funktionen definieren, um selbstgeschrie-
benen Code wiederzuverwenden und zu strukturieren. Ein Codeblock zur
Lösung einer Aufgabe wird dabei als eine Art Unterprogramm in einer Funk-
tion zusammengefasst. Durch den Aufruf der Funktion von einer beliebigen
anderen Stelle im Programm aus führen Sie den Codeblock bei Bedarf aus.
Dieses Kapitel stellt die Verwendung von fest in PHP eingebauten Funktionen
und die Definition eigener Funktionen vor.

5.1 Native Funktionen aus der PHP-Bibliothek


verwenden
Aus dem Alltag kennen Sie viele Dinge mit eingebauten Funktionen, die bei
Bedarf ausgeführt werden:
ƒ Auf Anfrage erzeugt eine Heizung Wärme.
ƒ Auf Anfrage erzeugt eine Türklingel ein Geräusch.
Häufig beeinflussen Parameter die Ausführung einer Funktion:
ƒ Auf Anfrage zahlt ein Bankautomat Geld aus. Als Parameter teilen Sie
den gewünschten Betrag mit.
ƒ Auf Anfrage wäscht eine Waschanlage ein Auto. Als Parameter wählen
Sie ein Waschprogramm.
Ähnlich den Dingen aus dem Alltag bringt PHP so genannte native Funktio-
nen mit. Eine Sammlung aus Funktionen bezeichnet man im Programmier-
Jargon als Bibliothek. Die Installation einer PHP-Erweiterung fügt eine weitere
Funktionsbibliothek für spezielle Zwecke hinzu, etwa zur Bildbearbeitung.

105
Kapitel 5 Funktionen

In den vorigen Kapiteln sind Sie bereits der PHP-Funktion date() begegnet,
um das aktuelle Datum bzw. die aktuelle Uhrzeit in einem gewünschten For-
mat zu erhalten. Sie können die Funktion beliebig oft aufrufen und dabei das
erzeugte Datumsformat mit einem Parameter steuern:
echo date('d.m.Y H:i'); // z.B. => 06.05.2022 20:33
echo date('Y-m-d'); // z.B. => 2022-05-06
echo date('l, F jS Y'); // z.B. => Friday, May 6th 2022

Listing 5.1: format-date.php

Die Formatierungsoptionen der Funktion date() beschreibt die PHP-


Dokumentation unter www.php.net/date.
Die allgemeine Syntax zum Aufruf einer Funktion lautet:
Funktionsname(Argument1, Argument2, ...);

Der Funktionsname beschreibt den Zweck der Funktion und dient als Bezeich-
ner, um die Funktion im Programm anzusprechen. Je nach Funktion übergibt
man beim Aufruf in runden Klammern kommasepariert so genannte Argu-
mente. Argumente sind konkrete Werte, die aus dem Programmlauf in die
Funktion übergeben werden und bei jedem Aufruf verschieden sein können.
Die übergebenen Argumente stehen dem Anweisungsblock der Funktion als
Variablen bzw. Parameter zur Verfügung.
Die Begriffe Argument und Parameter werden oft synonym verwen-
det. Genau genommen ist ein Argument der konkrete Übergabewert
aus dem Programmlauf in eine Funktion. Ein Parameter ist die Va-
riable im Kontext der Funktion, die ein übergebenes Argument auf-
nimmt (unabhängig des konkreten Werts).
Funktionsaufrufe sind Ausdrücke, die in einen Wert aufgelöst werden, den
so genannten Rückgabewert der Funktion. Der Rückgabewert kann wie jeder
andere Ausdruck verwendet werden, z.B. in einer Ausgabe, als Zuweisung an
eine Variable, als Bedingung einer Kontrollstruktur oder als Argument für ei-
nen anderen Funktionsaufruf.
Folgender Code rundet die Kreiszahl π (aus der von PHP vordefinierten Kon-
stante M_PI) mit der Funktion round() auf eine zufällige Anzahl an Dezimal-
stellen zwischen 3 und 8. Die Variable $result nimmt den Rückgabewert auf,
der anschließend ausgegeben wird:

106
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

$result = round(M_PI, random_int(3, 8));


echo $result; // z.B. => 3.14159

Listing 5.2: random-round-pi.php

Der zweite Parameter der Funktion round() gibt die Anzahl der Dezimal-
stellen für die Rundung an, die durch die Funktion random_int() zufällig be-
stimmt wird.
Die PHP-Dokumentation beschreibt jede Funktion im Detail und gibt nützli-
che Code-Beispiele.

Abb. 5.1: PHP-Dokumentation der Funktion date()

Die Kurzlinks www.php.net/Funktionsname führen am schnellsten zur Doku-


mentation einer Funktion, z.B.:
ƒ www.php.net/date
ƒ www.php.net/round
ƒ www.php.net/random_int usw.

107
Kapitel 5 Funktionen

5.1.1 Signatur einer Funktion


Die Kombination aus Funktionsname, Parametern und Rückgabedatentyp
heißt Signatur einer Funktion. Die PHP-Dokumentation zeigt die Signatur
jeder Funktion, um ihre Verwendung zu beschreiben. Das Schema lautet (auf
mehrere Zeilen aufgeteilt):
Funktionsname(
Datentyp $parameter1,
Datentyp $parameter2 = Vorgabewert,
// ...
): Rückgabedatentyp

Code-Editoren unterstützen während des Programmierens mit der Anzeige


der Signaturen:

Abb. 5.2: Eingeblendete Funktionssignatur im Code-Editor

Quellcode-Editoren blenden eine Signatur beim Schreiben einer öff-


nenden Klammer automatisch ein. Auch später kann die Signatur
mit einem Shortcut erneut eingeblendet werden, in VS Code z.B. mit
(Strg)+(ª)+(Leertaste).

Die vollständige Signatur der Funktion date() lautet zum Beispiel:


date(string $format, int $timestamp = time()): string

Die Signatur dokumentiert:


ƒ date als Bezeichner der Funktion.
ƒ $format als ersten Parameter vom Datentyp string. Da kein Vorgabewert
zugewiesen ist, muss beim Aufruf zwingend ein Argument für diesen Para-
meter übergeben werden.

108
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

ƒ $timestamp als zweiter Parameter vom Datentyp int. Da der Vorgabewert


time() zugewiesen ist, muss beim Aufruf nicht zwingend ein Argument
übergeben werden. Ohne Übergabe eines Arguments wird automatisch der
Rückgabewert eines Aufrufs der Funktion time() verwendet. Einen Para-
meter mit Vorgabewert bezeichnet man als optional, den Vorgabewert als
Default-Wert.
ƒ am Ende der Signatur, mit Doppelpunkt abgetrennt, findet sich der Daten-
typ des Rückgabewerts: string
Ein Funktionsaufruf ohne erforderliches Argument schlägt fehl:
echo date();
Fatal error: Uncaught ArgumentCountError: date()
expects at least 1 argument, 0 given ...

Die Übergabe eines unpassenden Datentyps führt ebenfalls zu einem Fehler:


echo date(['d.m.Y']);
Fatal error: Uncaught TypeError: date(): Argument
#1 ($format) must be of type string, array given

Die Signatur der date()-Funktion gibt eindeutige Hinweise auf die Datenty-
pen ihrer Parameter und des Rückgabewerts:
ƒ string $format: Der erste Parameter ist ein String.
ƒ int $timestamp: Der zweite Parameter ist eine Ganzzahl.
ƒ date(…): string: Der Rückgabewert ist ein String.
Es gibt auch Parameter, denen Werte eines beliebigen Datentyps übergeben
werden können. In einer Signatur sind sie mit dem »unechten« Datentyp
mixed gekennzeichnet. Zum Beispiel prüft isset() unabhängig vom Daten-
typ, ob eine oder mehrere übergebene Variablen im Programm initialisiert
wurden:
isset(mixed $var, mixed ...$vars): bool

Die Übergabe des ersten Arguments ist zwingend. Die drei Punkte vor dem
zweiten Parameter signalisieren, dass optional beliebig viele weitere Argu-
mente folgen dürfen. Dadurch kann isset()auch mehrere Variablen gleich-
zeitig prüfen:
$firstName = 'Erika';
var_dump(isset($firstName)); // => true
var_dump(isset($lastName)); // => false

109
Kapitel 5 Funktionen

var_dump(isset($firstName, $lastName)); // => false


$lastName = 'Mustermann';
var_dump(isset($lastName)); // => true
var_dump(isset($firstName, $lastName)); // => true

Listing 5.3: isset.php

5.1.2 Rückgabewerte
Es gibt Funktionen, die:
ƒ immer einen Rückgabewert desselben Datentyps liefern, d.h. der Rückga-
bedatentyp ist immer eindeutig, z.B. ein String.
ƒ einen Rückgabewert verschiedener Datentypen (»mixed«) liefern können,
d.h. der Rückgabedatentyp ist mehrdeutig, z.B. entweder ein String oder
eine Ganzzahl.
ƒ gar keinen Rückgabewert liefern (»void«).

Eindeutiger Rückgabewert
Mit der Funktion date() kennen Sie eine Funktion mit eindeutigem Rück-
gabewert. Die Signatur verrät, dass jeder korrekte Aufruf immer einen String
(ein formatiertes Datum) zurückliefern wird:
date(string $format, int $timestamp = null): string

Mehrdeutiger Rückgabedatentyp
Mehrdeutige Rückgabedatentypen werden genutzt, um mehrere Anwen-
dungsfälle, Sonderfälle oder Fehler auszudrücken. Die Funktion array_
search() durchsucht z.B. ein Array nach einem Wert und liefert bei einem
Treffer den passenden Schlüssel zurück. Der Schlüssel kann eine Ganzzahl
(numerisch indiziertes Array) oder ein String (assoziatives Array) sein. Ist der
Wert gar nicht im Array vorhanden, wird dies mit dem Rückgabewert ­false
ausgedrückt. Die Signatur zählt die verschiedenen möglichen Rückgabe-­
Datentypen auf, getrennt mit einem senkrechten Strich, dem Pipe-Symbol (|):
array_search(mixed $needle, array $haystack): int|string|false

Folgendes Skript zeigt Beispiele für jeden Rückgabe-Datentyp:


$colors = ['rot', 'blau', 'green' => 'grün'];
echo array_search('blau', $colors); // int => 1

110
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

echo array_search('grün', $colors); // string => grün


var_dump(array_search('gelb', $colors)) // => bool(false)

Listing 5.4: search-array.php

Der Rückgabewert kann z.B. eine Ausgabe beeinflussen:


$colors = ['rot', 'blau', 'grün'];
$key = array_search('gelb', $colors); // $key=false
echo $key ? 'Treffer!' : 'Niete!'; // => Niete!

Der ternäre Operator (Abschnitt 3.1.5) liefert den ersten Ausdruck (Treffer!),
wenn seine Bedingung ($key) wahr ist, ansonsten den zweiten (Niete!).
Auf Grund der dynamischen Typisierung von PHP ist Vorsicht geboten:
$colors = ['rot', 'blau', 'grün'];
$key = array_search('rot', $colors); // $key=0
echo $key ? 'Treffer!' : 'Niete!'; // => Niete!

Was ist passiert? Zur Auswertung der Bedingung ($key) im ternären Operator
führt PHP eine automatische Konvertierung in einen booleschen Wert durch.
Explizit bedeutet dies:
echo ((bool) $key) ? 'Treffer!' : 'Niete!';

Der gefundene Schlüssel 0 für die Farbe rot interpretiert PHP als false, da der
Wahrheitsgehalt der Zahl Null false ist (siehe Abschnitt 2.5):
var_dump((bool) 0); // bool(false)

Die sichere Lösung ist ein strikter Vergleich auf Identität (Abschnitt 3.2.2), die
den Datentyp mit einbezieht:
$colors = ['rot', 'blau', 'grün'];
$key = array_search('rot', $colors); // $key=0
echo ($key !== false) ? 'Treffer!' : 'Niete!'; // => 'Treffer!'

Beim strikten Vergleich sind automatische Konvertierungen durch PHP aus-


geschlossen: 0 und false sind auf Grund ihres unterschiedlichen Datentyps
niemals gleich. Die Klarstellung der Erwartungen durch strikte Vergleiche
schützt vor Überraschungen!

111
Kapitel 5 Funktionen

Kein Rückgabewert: void


Funktionen ohne Rückgabewert sorgen für andere Effekte, z.B. eine Ausgabe,
die Aktualisierung einer Datei oder das Versenden einer E-Mail. Die Funktion
var_dump() beispielsweise liefert keinen Rückgabewert, hat jedoch den Effekt,
Informationen zu einer übergebenen Variable auszugeben.
$age = 8;
var_dump($age); // => int(8)

Die Zuweisung eines Funktionsaufrufs an eine Variable macht somit keinen


Sinn, die Funktion gibt nichts zurück. In folgendem Beispiel wird PHP die
Variable $return immer mit null initialisieren:
$return = var_dump($age); // $result immer "null"

Die Funktionssignatur drückt einen nicht vorhandenen Rückgabewert mit


void (engl. für nichtig, leer) aus:

var_dump(mixed $value, mixed ...$values): void

Die nächsten Abschnitte beschreiben die wichtigsten Gruppen nativer PHP-


Funktionen zur Verarbeitung von Zahlen, Strings, Arrays und Datumsan­
gaben.

5.1.3 Mit Zahlen arbeiten: Mathematische Funktionen


Neben den arithmetischen Operatoren gibt es zahlreiche Funktionen zur
Durchführung mathematischer Operationen. Tabelle 5.1 zeigt einige Bei­
spiele.

Funktionsaufruf Rückgabewert Beschreibung


ceil(2.1) 3 Aufrundung auf Ganzzahl
floor(2.1) 2 Abrundung auf Ganzzahl
round(2.86, 1) 2.9 Rundung auf eine Stelle
sin(M_PI/6); 0.5 Sinus
sqrt(16) 4 Quadratwurzel (engl. square root)
max(3, 7, 4, 6) 7 Ermittelt höchstes Argument
min(3, 7, 4, 6) 3 Ermittelt niedrigstes Argument

Tab. 5.1: Beispiele für mathematische Funktionen

112
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

Aufgabe 1

Schreiben Sie eine Anweisung, die die Wurzel aus 13 auf zwei Nachkom-
mastellen gerundet berechnet.

5.1.4 Mit Texten arbeiten: String-Funktionen


String-Funktionen können Informationen über Zeichenketten gewinnen oder
neue Zeichenketten aus übergebenen Argumenten erzeugen.

Funktionsaufruf Rückgabewert Beschreibung


strlen('PHP') 3 Länge eines Strings
str_contains( true Enthält String den anderen
'PHP&MySQL', 'MySQL' String?
)
str_starts_with( false Beginnt String mit dem an-
'PHP&MySQL', 'MySQL' deren String?
)
strtoupper('Php') 'PHP' Umwandlung in Großbuch-
staben
strtolower('Php') 'php' Umwandlung in Klein-
buchstaben
trim("\tPHP \n") 'PHP' Entfernt »Weißraum« zu
Beginn und Ende
substr( 'P&M' Schneidet Teil-String für
'PHP&MySQL', 2, 3 gegebene Start-Position
) (erste Position = 0) und
Länge aus
number_format( '5,57' Formatierung einer Zahl
5.569, 2, ',' mit gegebenen Dezimalstel-
) len und Dezimaltrenner
str_replace( 'PHP&MySQL' Suchstring (Argument 1)
'SQL, 'MySQL', mit anderem String (Argu-
'PHP&SQL' ment 2) ersetzen
)
explode(',', 'a,b,c') ['a', 'b', 'c'] String an Trennzeichen in
Array-Elemente aufteilen

113
Kapitel 5 Funktionen

Funktionsaufruf Rückgabewert Beschreibung


implode(',', [1, 2, 3]) '1,2,3' Array-Elemente mit Trenn-
zeichen zu String verbinden
sprintf('%s€', 15) '15€' Platzhalter (%s) ersetzen.
Die Variante printf() gibt
das Ergebnis direkt aus.
json_decode( ['a'=>1, 'b'=>2] Wandelt JSON-String in
'{"a": 1, "b": 2}' PHP-Datenstruktur
)
json_encode( '{"a": 1, "b": 2}' Wandelt PHP-Datenstruk-
['a' => 1, 'b' => 2] tur in JSON-String
)

Tab. 5.2: Beispiele für String-Funktionen

5.1.5 Beispiel: E-Mail validieren


Ein Programm soll eine E-Mail-Adresse vom Benutzer zur Registrierung bei
einem Newsletter abfragen. Die E-Mail-Adresse muss dabei einige Kriterien
erfüllen, um als gültig akzeptiert zu werden.
So läuft das fertige Programm
> php register-email.php
E-Mail: maxbecker
E-Mail muss ein @-Zeichen enthalten.
> php register-email.php
E-Mail: max@example.com
E-Mails von example.com nicht erlaubt.
> php register-email.php
E-Mail: max@becker.de
E-Mail "max(at)becker.de" wurde registriert.

Quelltext
$email = readline('E-Mail: '); 1
$email = strtolower(trim($email)); 2
if (strlen($email) > 50) { 3
exit('E-Mail >50 Zeichen nicht zulässig');
}

114
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

if (!str_contains($email, '@')) { 4
exit('E-Mail muss ein @-Zeichen enthalten.');
}
$emailParts = explode('@', $email); 5
if ($emailParts[1] === 'example.com') { 6
exit('E-Mails von example.com nicht erlaubt.');
}
printf( 7
'E-Mail "%s" wurde registriert.',
str_replace('@', '(at)', $email) 8
);

Listing 5.5: register-email.php

Erklärungen
ƒ 1 Abfrage der E-Mail-Adresse und Zuweisung an $email.
ƒ 2 Normalisierung der Eingabe: Entfernung versehentlich eingegebener
Leerzeichen am Anfang und Ende; einheitliche Umwandlung in Klein-
buchstaben, da die Schreibweise in E-Mail-Adressen keine Rolle spielt.
ƒ 3 Längenprüfung: Falls die Adresse länger als 50 Zeichen ist, beendet
exit() das Programm mit einem Hinweis.
ƒ 4 Prüfung, ob die Adresse ein @-Zeichen enthält. Ablehnung, falls kein
@-Zeichen vorhanden ist.
ƒ 5 Aufsplitten der E-Mail-Adresse an der Stelle des @-Zeichens in ein Array
mit zwei Elementen (Text vor und nach dem @-Zeichen).
ƒ 6 Prüfung, ob die Adresse die Beispiel-Domain »example.com« verwen-
det. Ablehnung, falls ja.
ƒ 7 Rückmeldung zur erfolgreichen Registrierung.
ƒ 8 Ersetzung des @-Zeichens durch (at).

Aufgabe 2

Die Funktion filter_var() bietet eine native Möglichkeit, E-Mail-Adressen


auf korrekte Syntax zu überprüfen. Können Sie herausfinden, wie das geht?

5.1.6 Mit Arrays arbeiten: Array-Funktionen


Array-Funktionen gewinnen Informationen über Arrays bzw. dessen Elemen-
te oder erzeugen neue Arrays aus den übergebenen Argumenten.

115
Kapitel 5 Funktionen

Funktionsaufruf Rückgabewert Beschreibung


count([1, 2, 3]) 3 Elemente zählen
in_array('b', ['a', 'c']) false Existenz von Wert in Array
prüfen
array_key_exists( true Existenz von Schlüssel in
'b', Array prüfen
['a'=>'A', 'b'=>'B']
)
array_search( 1 Schlüssel von Wert im Ar-
'b', ['a', 'b', 'c'] ray suchen
)
array_rand(['a', 'b', 'c']) z.B.: c Zufallselement
range(4, 7) [4, 5, 6, 7] Array aus Bereich erzeu-
gen
array_reverse([1, 2, 3]) [3, 2, 1] Elemente umkehren
array_sum([1, 2, 3]) 6 Elemente aufsummieren
array_merge( [1, 2, 'a', 'b'] Arrays zu neuem Array
[1, 2], ['a', 'b'] vereinen
)

Tab. 5.3: Beispiele für Array-Funktionen

Manche Array-Funktionen erzeugen keinen neuen Wert, sondern manipulie-


ren direkt den Inhalt eines übergebenen Arrays, z.B.:
ƒ sort() sortiert die Arrayelemente alphabetisch.
ƒ shuffle() mischt die Arrayelemente zufällig.
$files = ['03.jpg', '01.jpg', '02.jpg'];
sort($files); // Sortieren
var_dump($files); // => ['01.jpg', '02.jpg', '03.jpg']
shuffle($files); // Zufällig vermischen
var_dump($files); // z.B. => ['02.jpg', '01.jpg', '03.jpg']

Listing 5.6: sort-and-shuffle.php

5.1.7 Beispiel: Passwort generieren


Ein Passwortgenerator soll ein zufälliges, starkes Passwort gewünschter Länge
generieren.

116
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

So läuft das fertige Programm


> php generate-password.php
Passwortlänge: 12
Passwort: Sj*iS52K9s3r

Quelltext
$length = (int) readline('Passwortlänge: '); 1
if ($length < 5) {
exit('Passwortlänge muss >= 5 sein.'); 2
}
$characterPool = array_merge( 3
range(0, 9), 4
range('A', 'Z'),
range('a', 'z'),
['*', '#', '?', '+', '_', '-'] 5
);
shuffle($characterPool); 6
$password = ''; 7
while ($length-- > 0) { 8
$randomKey = array_rand($characterPool); 9
$password .= $characterPool[$randomKey]; 1  0
}
echo "Passwort: $password";

Listing 5.7: password-generator.php

Erklärungen
ƒ 1 Gewünschte Passwortlänge vom Benutzer abfragen und als Ganzzahl an
$length zuweisen.
ƒ 2 Falls die Passwortlänge unter 5 Zeichen liegt, beendet exit() das Pro-
gramm mit einem Hinweis.
ƒ 3 In der Array-Variablen $characterPool wird ein Pool möglicher Pass-
wortzeichen aufgebaut. array_merge() vereint den Pool aus einzelnen Teil-
mengen.
ƒ 4 Neben Arrays mit Zahlenbereichen kann range() auch Buchstabenbe-
reiche erzeugen. So entstehen Arrays mit den Teilmengen 0 bis 9, A bis Z
und a bis z.
ƒ 5 Eine Teilmenge mit Sonderzeichen wird beigemischt.

117
Kapitel 5 Funktionen

ƒ 6 Der Pool wird zufällig durchgemischt.


ƒ 7 Die Variable $password soll das generierte Passwort aufnehmen und wird
zunächst als leerer String initialisiert.
ƒ 8 Die gewünschte Passwortlänge bestimmt die Anzahl der Durchläufe ei-
ner while-Schleife.
ƒ 9 In jedem Schleifendurchlauf wählt array_rand() einen zufälligen Array-
Schlüssel aus dem Zeichenpool.
ƒ 10 Das Zufallszeichen wird an $password angehängt.

5.1.8 Die Unixzeit und Datumsfunktionen


Datum und Zeit spielen in vielen Anwendungen eine wichtige Rolle.
Einige praktische Beispiele:
ƒ Zeitpunkt der Erstellung eines Blog-Artikels festhalten.
ƒ Alter einer Person anhand des Geburtsdatums bestimmen.
ƒ Das Datum des dritten Montags im Monat für eine weiterkehrende Ver-
anstaltung berechnen.

Unixzeit
Für die Programmierung mit Datum und Zeit ist die Unixzeit ein grundle-
gendes Konzept. Die Unixzeit wurde für das Unix-Betriebssystem entwickelt
und zählt die vergangenen Sekunden seit dem 1. Januar 1970 um 00:00 Uhr
UTC (Weltzeit). Die Unixzeit am 25. Mai 2022 um 14:30 lautet zum Beispiel:
1653489000. Eine fixe Unixzeit heißt Zeitstempel (engl. Timestamp).
Ein Aufruf der PHP-Funktion time() liefert den aktuellen Unix-Zeitstempel
nach der Uhr des Betriebssystems:
echo time(); // z.B. => 1653489000
sleep(3); // 3 Sekunden warten
echo time(); // => 1653489003

Listing 5.8: time.php

Datum formatieren
Einem Menschen fällt es schwer, einen Unix-Zeitstempel direkt zu interpre-
tieren. Die date()-Funktion »übersetzt« einen Zeitstempel in ein lesbares
Format und kann auch Dinge wie den Wochentag, den Tag im Monat u.v.m.
daraus bestimmen. Als erstes Argument werden dazu Formatzeichen für das

118
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

gewünschte Datumsformat übergeben, als zweites Argument der zu formatie-


rende Zeitstempel. Der Vorgabewert für den Zeitstempel ist die aktuelle Unix-
Zeit, folgende Aufrufe sind daher äquivalent:
date('d.m.Y H:i:s'), time()); // Aktuelles Datum
date('d.m.Y H:i:s'); // Aktuelles Datum

Tabelle 5.4 gibt einen Überblick einiger Formatzeichen.

Formatzeichen Beschreibung
d Tag des Monats mit führender Null
m Monat des Jahres mit führender Null
Y Jahreszahl, vierstellig
H Stunde im 24h-Format mit führender Null
i Minuten mit führender Null
s Sekunden mit führender Null
y Jahreszahl, zweistellig
n Monat des Jahres, ohne führender Null
w Wochentag als Zahl von 0 (Sonntag, US-Format!) bis Samstag (6)

Tab. 5.4: Einige Formatzeichen für die Funktion date()

Rückgaben der Funktion date() können auch für die eigene Programmlogik
genutzt werden. Das Formatzeichen »w« liefert z.B. den Wochentag als Num-
mer zwischen 0 (Sonntag; nach US-Standard der erste Tag der Woche) und 6
(Samstag). Rufen Sie die Funktion an einem Montag auf, wäre das Ergebnis
die Zahl 1:
echo date('w'); // z.B. Aufruf montags => 1

Die Nummer des Wochentags kann weiter genutzt werden, z.B. um den ak-
tuellen Wochentag auf Deutsch auszugeben. Die Wochentags-Nummer wird
dynamisch als Schlüssel eingesetzt, um den passenden Wert aus einem Array
mit den sortierten Namen der deutschen Wochentage zu finden:
$weekdays = ['Sonntag', 'Montag', ... 'Samstag'];
echo $weekdays[date('w')]; // z.B. Aufruf montags => Montag

Listing 5.9: german-weekday.php

119
Kapitel 5 Funktionen

Durch explizite Angabe eines Zeitstempels kann ein beliebiges Datum forma-
tiert werden:
$ts = time(); // Zeitstempel zur Laufzeit
date('d.m.y H:i:s', $ts); // aktuelle Zeit
date('d.m.y H:i:s', $ts + 60); // +1 Minute
date('d.m.y H:i:s', $ts + (60*60)); // +1 Stunde
date('d.m.y H:i:s', $ts + (60*60*24)); // +1 Tag

Listing 5.10: date-for-timestamp.php

Am 25.05.22 14:30:00 ergeben sich folgende Daten:


25.5.2022 14:30:00 = time()
25.5.2022 14:31:00 = time() + 60 Sekunden
25.5.2022 15:30:00 = time() + 3600 Sekunden
26.5.2022 14:30:00 = time() + 86400 Sekunden

Zeitstempel erzeugen
Die Funktion time() liefert immer den Unix-Zeitstempel für den aktuellen
Zeitpunkt. Mit der Funktion mktime() (make time) können Sie den Zeitstem-
pel für einen beliebigen Zeitpunkt bestimmen. Dazu übergibt man die Be-
standteile des gewünschten Datums in der Reihenfolge: Stunde, Minute, Se-
kunde, Monat, Tag, Jahr:
$ts = mktime(14, 30, 0, 5, 25, 2022); // $ts=1653489000
echo date('d.m.y H:i', $ts); // => 25.05.22 14:30

Listing 5.11: mktime.php

mktime() hat die praktische Eigenschaft, bei Angaben über die gültigen Berei-
che hinweg automatisch umzurechnen:
// 25. Mai + 14 Tage = "39. Mai" = 8. Juni
$ts = mktime(14, 30, 0, 5, 25 + 14, 2022);
echo date('d.m.y H:i', $ts); // 08.06.22 14:30
// 14:30 Uhr + 12 Stunden = "26:30 Uhr" = 2:30 Uhr
$ts = mktime(14 + 12, 30, 0, 5, 25, 2022);
echo date('d.m.y H:i', $ts); // 26.05.22 02:30
// 1. März 2022 – 1 Tag = letzter Februartag 2022
$ts = mktime(0, 0, 0, 3, 0, 2022);
echo date('d.m.y', $ts); // 28.02.22
// 1. März 2020 – 1 Tag = letzter Februartag 2020

120
Native Funktionen aus der PHP-Bibliothek verwenden 5.1

$ts = mktime(0, 0, 0, 3, 0, 2020);


echo date('d.m.y', $ts); // 29.02.20

Listing 5.12: mktime-outside-scope.php

Die praktische Funktion strtotime() (string-to-time) bestimmt einen Unix-


Zeitstempel aus absoluten oder relativen Zeitangaben. Fehlende Teilangaben
ergänzt die Funktion automatisch. Beachten Sie, dass die Interpretation der
Angaben nach dem amerikanischen System erfolgt! Beispiele mit absoluten
Zeitangaben:
$ts = strtotime('2022-05-25 14:30:00');
echo date('d.m.y H:i', $ts); // 25.05.22 14:30
$ts = strtotime('May 25th 2022');
echo date('d.m.y H:i', $ts); // 25.05.22 00:00
$ts = strtotime('May 25th 2022 2:30pm');
echo date('d.m.y H:i', $ts); // 25.05.22 14:30

Listing 5.13: strtotime.php

Beispiele mit relativen Zeitangaben zur Laufzeit am 25. Mai 2022 um 14:30
Uhr:
$ts = strtotime('now');
echo date('d.m.y H:i', $ts); // 25.05.22 14:30
$ts = strtotime('now +3hours');
echo date('d.m.y H:i', $ts); // 25.05.22 17:30
$ts = strtotime('Third Sunday of June next year');
echo date('d.m.y H:i', $ts); // 20.06.22 00:00
$ts = strtotime('Yesterday noon');
echo date('d.m.y H:i', $ts); // 24.05.22 12:00

Die zahlreichen Optionen zu date()und strtotime() beschreibt die


PHP-Dokumentation ausführlich.

Zeitzone
Die Unixzeit entspricht immer der Weltzeit UTC (+00:00). Die mitteleuro-
päische Winterzeit ist eine Stunde (+01:00), die Sommerzeit zwei Stunden
(+02:00) voraus.
Ausgaben der Funktion date() richten sich nach der Zeitzone aus der php.
ini-Einstellung date.timezone (Vorgabewert UTC). PHP rechnet einen Unix-

121
Kapitel 5 Funktionen

Zeitstempel automatisch in die eingestellte Zeitzone um und berücksichtigt


Sommer- und Winterzeit. Der Bezeichner für die deutsche Zeitzone lautet
Europe/Berlin. Um alle Datumfunktionen standardmäßig auf diese Zeitzone
umstellen, passen Sie die php.ini an:
date.timezone=Europe/Berlin

Zur Laufzeit eines Skripts kann die Zeitzone auch mit der Funktion date_
default_timezone_set() eingestellt werden. Die Zeitzone gilt bis zu einem
weiteren Aufruf der Funktion oder dem Ende des Skripts für alle Datums-
funktionen. Das Pendant date_default_timezone_get() liest die aktuell ein-
gestellte Zeitzone aus.
$timezone = date_default_timezone_get();
echo $timezone.': '.date('H:i'); // z.B. => UTC: 14:30
date_default_timezone_set('Europe/Berlin');
$timezone = date_default_timezone_get();
echo $timezone.': '.date('H:i'); // => Europe/Berlin: 16:30

Listing 5.14: timezone-at-runtime.php

5.2 Eigene Funktionen definieren


Beim Programmieren wiederholen sich häufig die gleichen Teilaufgaben. Statt
die Anweisungen zur Lösung einer Teilaufgabe an mehreren Stellen zu wie-
derholen, können sie einmalig in einer eigenen Funktion zusammengefasst
und bei Bedarf wie eine native Funktion aufgerufen werden. Eine Änderung
am zentralen Code der Funktion wirkt sich in allen Aufrufen im Programm
aus; ein mehrfach genutzter Code muss somit praktischerweise nur an einer
Stelle gepflegt werden. Eigene Funktionen helfen auch dabei, ein Programm in
kleine, übersichtliche Teile zu strukturieren.
Angenommen, eine Webseite zur Buchung von Veranstaltungen möchte das
Datum jeder Veranstaltung überall in ihrem Angebot (Suche, Detailansicht,
Rechnung usw.) einheitlich mit Angabe eines deutschen Wochentags forma-
tieren, z.B. Mittwoch, 25.05.2022. Die Datumsangaben sind einheitlich im
Format YYYY-MM-DD abgespeichert, wie es etwa in einer Datenbank üblich ist.
Weiter oben haben Sie die Lösung dieser Aufgabe mit dem Formatzeichen »w«
kennengelernt:
$timestamp = strtotime('2022-05-25');
$weekdays = ['Sonntag', 'Montag', ..., 'Samstag'];

122
Eigene Funktionen definieren 5.2

printf( // printf() = direkte Ausgabe


'%s, %s',
$wekdays[date('w', $timestamp)],
date('d.m.y', $timestamp)
);

Statt den Code an den verschiedenen Stellen zu wiederholen, kann er in eine


eigene Funktion ausgelagert werden:
function formatDate($date) {
$weekdays = ['Sonntag', ..., 'Samstag'];
$timestamp = strtotime($date);
return sprintf( // sprintf() = Rückgabe
'%s, %s',
$weekdays[date('w', $timestamp)],
date('d.m.y', $timestamp)
);
}

Listing 5.15: format-date-function.php

Bemerken Sie den Unterschied bei der Formatierung eines Strings mit
Platzhaltern (%s)? Im Programmkontext wird printf() verwendet, im
Kontext der Funktion aber sprintf(). printf() gibt den String wie
echo nach Ersetzung der Platzhalter direkt aus, während sprintf()
ihn als Rückgabewert liefert.
Zur Formatierung eines Datums ist nur die Übergabe des Datums an die
Funktion formatDate() erforderlich:
echo formatDate('2022-05-25'); // => Mittwoch, 25.05.22
echo formatDate('2022-06-08'); // => Samstag, 06.08.22

Allgemein lautet die Struktur zur Definition einer Funktion:


function Funktionsname($param1, $param2, ...) {
Anweisung(en);
return Rückgabewert;
}

Wie Variablen beginnen Funktionsnamen mit einem Buchstaben oder Unter-


strich; danach können Buchstaben, Ziffern oder Unterstriche folgen. Für die
festgelegten Parameter müssen beim Aufruf der Funktion entsprechende Ar-

123
Kapitel 5 Funktionen

gumente übergeben werden – sie stehen dem Anweisungsblock der Funk­tion


als Variablen zur Verfügung. Eine Funktion kann man sich als »Programm
im Programm« vorstellen, das mit eigenen Eingabedaten (den Parametern)
arbeitet.
Der Codeblock in geschweiften Klammen enthält die Anweisungen der Funk-
tion. Das Schlüsselwort return beendet die Ausführung der Funktion und gibt
einen Wert an den Aufrufer zurück – den Rückgabewert:
function sum($value1, $value2) {
return $value1 + $value2;
}
echo sum(4, 9); // => 13

Listing 5.16: sum-function.php

Soll die Funktion keinen Wert zurückgeben, kann return ohne Rückgabewert
aufgerufen oder weggelassen werden:
function hello($name) {
if (trim($name) === '') { // nur Leerzeichen?
return;
}
echo "Hallo $name!";
}
hello(' '); // Keine Ausgabe
hello('Karla'); // => Hallo Karla!

Listing 5.17: Funktion ohne Rückgabewert – hello-function.php

Funktionen können Vorgabewerte für Parameter festlegen. Der Parameter


verwendet den Vorgabewert, wenn beim Aufruf kein Argument für ihn über-
geben wurde:
function orderPizza($name, $amount = 1) {
return "$amount x $name";
}
echo orderPizza('Margherita'); // => 1x Margherita
echo orderPizza('Hawaii', 2); // => 2x Hawaii

Listing 5.18: order-pizza.php

Der Aufruf einer Funktion ist bereits vor der Definition möglich, da PHP
Skripte bereits vor der Ausführung komplett einliest:

124
Eigene Funktionen definieren 5.2

echo hello('Sven'); // => Hallo Sven!


function hello ($name) {
return "Hallo $name!";
}

5.2.1 Hinweise auf Datentypen: Type Hints


Ohne besondere Kennzeichnung können einer Funktion Argumente eines be-
liebigen Datentyps übergeben werden. Das ergibt oft keinen Sinn, z.B. bei der
Summierung zweier Zahlen:
function sum($value1, $value2) {
return $value1 + $value2;
}
sum('www', 3); // String als erster Operand

PHP quittiert die arithmetische Operation mit einem Fehler:


Fatal error: Uncaught TypeError: Unsupported
operand types: string + int

Mit Hinweisen auf die Datentypen – so genannten Type Hints – kann eine
Funktion explizit bestimmte Datentypen einfordern und auch den Datentyp
des Rückgabewerts klarstellen:
function sum(int $value1, int $value2): int {
return $value1 + $value2;
}
sum('www', 3);

Listing 5.19: sum-function-with-type-hints.php

Die Type Hints machen deutlich, dass beide Parameter und der Rückgabewert
vom Datentyp Integer (int) sein müssen. Der Anweisungsblock der Funktion
ist gegen falsche Datentypen geschützt, der Aufrufer kann sich sicher sein,
eine Ganzzahl als Rückgabewert zu erhalten. Ein falscher Aufruf kommuni-
ziert den Fehler deutlicher:
Fatal error: Uncaught TypeError: sum(): Argument #1 ($value1) must
be of type int, string given

125
Kapitel 5 Funktionen

Mit Type Hints können Code-Editoren falsche Aufrufe bereits beim Schrei-
ben des Codes melden. Um Fehlerquellen zu reduzieren und das Debugging
zu vereinfachen, sollten wann immer möglich Type Hints verwendet werden.

Aufgabe 3

Definieren Sie die Funktion formatDate() vom Beginn des Abschnitts 5.2
mit Type Hints und machen Sie einige Testaufrufe.

5.2.2 Geltungsbereich von Variablen


Eine Variable befindet sich immer im Kontext eines bestimmten Geltungsbe-
reichs (engl. scope), in dem sie »sichtbar« ist. Variablen außerhalb einer Funk-
tion befinden sich im globalen Geltungsbereich (engl. global scope). Der Anwei-
sungsblock einer Funktion definiert einen eigenen, lokalen Geltungsbereich
(engl. local scope). Variablen aus den beiden verschiedenen Bereichen können
sich gegenseitig nicht »sehen«. Im folgenden Skript kann Code im Geltungs-
bereich der Funktion hello() nicht auf die Variable $name aus dem globalen
Geltungsbereich zugreifen:
// Hier: Globaler Bereich (global scope)
$name = 'Karl';
function hello() {
// Hier: Lokaler Bereich der Funktion (local scope)
echo $name;
}
hello(); // Warning: Undefined variable $name

Listing 5.20: global-and-local-scope.php

Umgekehrt gilt das Gleiche: In folgendem Skript ist die Variable $price aus
dem Geltungsbereich der Funktion setPrice() nicht im globalen Geltungs-
bereich verfügbar:
function setPrice() {
$price = 99;
}
setPrice();
echo $price; // Warning: Undefined variable $price

Zusammengefasst: Variablen in einer Funktion sind nur innerhalb, Variablen


außerhalb nur außerhalb der Funktion sichtbar.

126
Übungen 5.3

5.3 Übungen
Übung 1
Schreiben Sie ein Skript, das die Bestimmung eines Schaltjahrs (siehe Kapi­-
tel 3) in einer Funktion isLeapYear() definiert.
ƒ Verwenden Sie Type Hints: Das Eingabejahr muss eine Ganzzahl sein, der
Rückgabewert ein Wahrheitswert.
ƒ Listen Sie mit Hilfe der Funktion und einer Schleife alle Schaltjahre zwi-
schen 1900 und dem aktuellen Jahr auf.
Übung 2
Schreiben Sie ein Skript, das das aktuelle Datum im Format Mittwoch, 25. Mai
2022 ausgibt.
Tipp: Nutzen Sie die date()-Funktion mit den Formatzeichen »w« (numeri-
scher Wochentag 0-6) bzw. »n« (numerischer Monat 1-12) zur Übersetzung in
deutsche Wochentags- und Monatsnamen.

Übung 3
Weltuhr: Schreiben Sie ein Skript, das die aktuelle Uhrzeit in allen Zeitzonen
der Welt anzeigt.
Tipps:
ƒ Die Funktion timezone_identifiers_list() liefert die Namen aller Zeitzo-
nen (America/New_York, Europe/Berlin usw.) in einem Array.
ƒ Die Funktion date_default_timezone_set() stellt eine Zeitzone anhand
­ihres Namens ein. Sie gilt für alle folgenden date()-Aufrufe.

127
Webseiten entwickeln
und veröffentlichen
In den vorigen Kapiteln haben Sie die Grundlagen von PHP anhand von
Kommandozeilen-Skripten kennengelernt. Dieses Kapitel wendet sich dem
wichtigsten Einsatzgebiet von PHP zu: der Generierung dynamischer Websei-
ten mit Hilfe eines Webservers.
Das Kapitel erklärt, was beim Abruf einer Webseite im Hintergrund geschieht
und stellt den Einsatz des in PHP eingebauten Webservers für die Entwicklung
von Webseiten auf dem eigenen Computer vor. Sie erlernen die Grundlagen
der HTTP-Kommunikation und sehen an einem Beispiel, wie ein Webspace
zur Veröffentlichung einer Webseite im Internet eingerichtet wird.

6.1 Was geschieht beim Abruf einer Webseite?


Zum Betrachten einer Webseite geben Sie im Browser eine Internetadresse
(URL), z.B. https://wikipedia.de, in die Adresszeile ein und drücken die
Eingabetaste. Die Webseite erscheint im Anzeigebereich (engl. view port) des
Browsers. Was ist dabei im Hintergrund geschehen?
Die Beispiele verwenden den Firefox-Browser. Andere Browser unterstüt-
zen ebenfalls die beschriebenen Funktionen, die Bezeichnungen können
jedoch abweichen.
6.1.1 Adressen für Computer: IP-Adressen
Die angeforderte Webseite liegt auf einem anderen Computer irgendwo auf
der Welt. Wenn Sie eine Webseite über den Browser öffnen, formuliert Ihr
Computer eine Anfrage (den Request) an diesen Computer, stellt eine Verbin-
dung her und nimmt die Antwort (die Response) mit dem Inhalt der Webseite
entgegen. Zur Kommunikation untereinander benötigt jeder Computer im
Internet eine weltweit eindeutige Adresse – die IP-Adresse. Das ist vergleich-

129
Kapitel 6 Webseiten entwickeln und veröffentlichen

bar mit einer Telefonnummer, die weltweit genau einem Telefon zugeordnet
ist. Ohne die Telefonnummer und Zuordnung zum Gerät kann das Telefon
nicht angerufen werden. Ein an das Internet angeschlossener Computer mit
zugeordneter IP-Adresse kann von anderen Computern unter dieser Adres-
se »angerufen« werden und ist online. IP-Adressen bestehen aus vier Zah-
len zwischen 0 und 255, die mit einem Punkt getrennt notiert werden, z.B.
134.119.24.29.

Das Format der gezeigten IP-Adressen entspricht der noch vorherr-


schenden Version 4 des Internet Protocols (IP). Aufgrund von Adres-
sen-Knappheit vollzieht sich langsam aber stetig der Übergang zur
Version 6 mit einem viel größeren Adressraum. Eine IPv6-Adresse
sieht etwas komplizierter aus, z.B.: 2001:0db8:85a3:08d3::0370:7344.
6.1.2 Adressbuch für Computer:
das Domain Name System (DNS)
Im Vergleich zu Computern können sich Menschen für gewöhnlich kompli-
zierte Nummern nicht gut merken. Dank sprechender Namen für Webseiten
wie wikipedia.de, google.de usw. ist dies auch nicht nötig. Die Namen von
Webseiten heißen Domains.
Ein Domainname dient als Alias für eine IP-Adresse. Wie Sie in einem Tele-
fonbuch anhand eines einfach zu merkenden Namens die passende Telefon-
nummer nachschlagen können, gibt es Register, um die IP-Adresse zu einem
Domainnamen nachzuschlagen:
wikipedia.de 134.119.24.29
google.de 142.250.185.163
mitp.de 178.250.12.15
...

Diese Register befinden sich auf speziellen Computern im Internet, den


­Nameservern. Deren IP-Adressen sind auf Ihrem Computer bzw. Internet-
Zugangspunkt fest eingestellt. Starten Sie den Aufruf einer Webseite, bittet
Ihr Computer einen der Nameserver um die Auflösung der Domain in die
passende IP-Adresse. Da sich die Zuordnungen zwischen Domain und IP
nicht häufig ändern, merkt sich Ihr Computer das Ergebnis eine Weile, um
bei einem weiteren Besuch nicht erneut nachfragen zu müssen. Mit Hilfe der
IP-Adresse startet die eigentliche Anfrage an den richtigen Computer. Das
System der Namensauflösung von Domainnamen in IP-Adressen heißt Do-
main Name System (DNS).

130
Was geschieht beim Abruf einer Webseite? 6.1

Ändert sich die IP-Adresse zu einer Domain, etwa weil eine Webseite auf ei-
nen anderen Computer umzieht, ändert sich nichts am Namen der Domain.
Lediglich die Zuordnung im DNS-Register muss aktualisiert werden.

6.1.3 Computer als Dienstleister im Internet: Host, Server


und Client
Computer im Internet können verschiedene Dienste anbieten. Solche Compu-
ter werden als Hosts (engl. für Gastgeber) bezeichnet. Als Synonym für einen
Host-Computer wird der Begriff Server (engl. für Diener) verwendet. Genau
genommen ist der Server eine auf dem Host installierte Software für einen
bestimmten Dienst. Der Server wartet auf Anfragen von anderen Computern,
den Clients (engl. für Kunden), um eine bestimmte Dienstleistung zu erfüllen.
Andere Dienstleistungen als das Ausliefern einer Webseite sind z.B. der Ver-
sand einer E-Mail oder die Abfrage einer Datenbank. Je nach genutztem
Dienst spricht man dann vom gesamten Host-Computer als Web-, E-Mail-
oder Datenbank-Server.

6.1.4 Dienste auf einem Computer unterscheiden: Ports


Ein Computer kann unter derselben IP-Adresse mehrere Dienste beziehungs-
weise Server gleichzeitig anbieten. Der Zugriff auf verschiedene Dienste des-
selben Host-Computers wird durch so genannte Ports (engl. für Schnittstelle,
Anschluss) unterschieden. Eine Verbindung zu einem Computer kommt im-
mer auf einem bestimmten Port an und lässt sich dadurch dem gewünschten
Dienst zuordnen. Ports werden durch Port-Nummern unterschieden. Viele
Port-Nummern sind für einen bestimmten Dienst standardisiert, z.B. Port 25
für einen E-Mail-Server, Port 3306 für einen MySQL-Datenbank-Server, Port
80 für eine unverschlüsselte bzw. Port 443 für verschlüsselte HTTP-Kommu-
nikation mit einem Webserver. Solange ein Dienst auf einem Port läuft, kann
kein anderer Dienst diesen Port verwenden.
Die Port-Nummer wird mit einem Doppelpunkt hinter der IP-Adresse no-
tiert. Angenommen, der Computer mit der IP 142.250.185.163 bietet einen
Webserver und einen MySQL-Datenbankserver an. Anfragen an den Webser-
ver richten sich dann an 134.119.24.29:80, Anfragen an den MySQL-Daten-
bankserver an 134.119.24.29:3306.
Die Art der Kommunikation zwischen zwei Computern ist ähnlich wie die
Syntax einer Programmiersprache in einem Regelwerk festgelegt, dem soge-
nannten Protokoll. Die Kommunikation mit dem Webserver erfolgt nach den
Regeln des HTTP-Protokolls bzw. seinem verschlüsselten (und damit siche-

131
Kapitel 6 Webseiten entwickeln und veröffentlichen

ren) Pendant HTTPS. Ein HTTP(S) -Webserver bietet seinen Dienst auf dem
fest definierten Port 80 bzw. 443 an. Der Aufruf von https://wikipedia.de
entspricht daher im Hintergrund einem Aufruf an den HTTPS-Webserver-
Dienst unter (z.B.) 134.119.24.29:443. Für den Abruf öffentlicher Webseiten
in einem Browser ist die Angabe des Ports nicht erforderlich. Der Port ergibt
sich aus dem Kontext der Internetadresse (HTTP = 80, HTTPS = 443).

Aufgabe 1

Eine Port-Nummer können Sie auch einem Domainnamen anhängen. Ver-


suchen Sie wikipedia.de auf den Ports 80, (HTTP), 443 (HTTPS) und 3306
(MySQL) im Browser zu erreichen. Was geschieht?

6.2 Webserver auf dem eigenen Computer


betreiben
Um eine eigene Webseite unter einer eigenen Domain im Internet zu betrei-
ben, müssten Sie theoretisch:
ƒ einen Host-Computer im Internet mit einer festen IP-Adresse und stabiler
Internetverbindung betreiben.
ƒ einen Domainnamen in das DNS-Register eintragen und die IP-Adresse
des Host-Computers zuordnen.
ƒ eine Webserver-Software auf dem Host-Computer installieren und konfi-
gurieren.
Doch es gibt einfachere Lösungen:
ƒ Zum Entwickeln und Testen können Sie Ihren eigenen Computer als Server
und Client gleichzeitig einsetzen.
ƒ Den Betrieb einer echten, öffentlichen Webseite bieten sogenannte Web­
hoster als Dienstleistung für geringe Kosten.
Dieser Abschnitt zeigt die Simulation einer Webseite auf dem eigenen, lokalen
Computer. Lokal entwickelte Webseiten können Sie anschließend bei einem
Webhoster hochladen und veröffentlichen (siehe Abschnitt 6.5).

6.2.1 Domainname für den eigenen Rechner: localhost


Um Dienste auf dem eigenen Computer (dem »lokalen Host«) mit einem Do-
mainnamen anzusprechen, gibt es den speziellen Domainnamen localhost.
Mit dieser Domain kann Ihr Computer Client und Server zugleich sein. In

132
Webserver auf dem eigenen Computer betreiben 6.2

der Client-Rolle können Sie Anfragen an localhost stellen. Ihr Computer hat
für diesen speziellen Domainnamen eine feste Auflösung in die reservierte
IP-Adresse 127.0.0.1 gespeichert. Diese IP-Adresse ist die Adresse desselben
Computers, der die Anfrage gestellt hat (engl. loopback: Sender und Empfän-
ger sind identisch). Auf diese Weise kann ein Computer Anfragen an sich
selbst stellen. Durch den Start einer lokalen Server-Software können Sie in die
Server-Rolle schlüpfen und solche Anfragen beantworten.
Versuchen Sie durch Eingabe der Internetadresse http://localhost in Ihrem
Browser einen Webdienst auf Ihrem lokalen Computer anzusprechen. Eine
HTTP-Anfrage im Browser ohne weitere Port-Angabe verwendet automa-
tisch den Port 80. Es wird eine Fehlermeldung erscheinen, dass die Webseite
nicht verfügbar ist. Der Grund ist einfach: Auf Port 80 Ihres Computers läuft
noch kein Dienst zur Bedienung der Anfrage. Der nächste Abschnitt zeigt die
Bereitstellung eines passenden Diensts.

6.2.2 Lokalen Webserver starten


Praktischerweise bringt PHP einen eingebauten Webserver für die lokale Ent-
wicklung mit. Das heißt, Sie haben bereits eine Webserver-Software an Bord,
sie muss lediglich gestartet und eingestellt werden.
Sie starten den PHP-Webserver auf der Kommandozeile mit dem php-Befehl
und der Option -S. Als Argument ist zwingend ein Domainname (oder eine
IP-Adresse) und ein beliebiger freier Port anzugeben, unter dem der Webser-
ver seinen Dienst anbieten soll. Aus Sicherheitsgründen ist die Verwendung
von Port-Nummern bis 1024 Administratoren vorbehalten, daher kann der
Standard-Port 80 nicht direkt genutzt werden. Per Konvention wird zum Ent-
wickeln gern der Port 8000 verwendet.
Legen Sie ein leeres Verzeichnis, z.B. ~/www (Erinnerung: ~ steht als Abkürzung
für das Benutzerverzeichnis), an und wechseln Sie auf der Kommandozeile
in das Verzeichnis. Starten Sie den Webserver für die Domain localhost auf
Port 8000:
> php -S localhost:8000

Abb. 6.1: Start eines lokalen Webservers auf Port 8000

133
Kapitel 6 Webseiten entwickeln und veröffentlichen

Auf der Kommandozeile erscheint eine Log-Nachricht über den erfolgreichen


Start. Der Webserver wartet nun auf Client-Anfragen. Zu jeder bedienten An-
frage im Browser wird eine weitere Log-Nachricht auf der Kommandozeile
erscheinen.

Die Tastenkombination (Strg)+(C) stoppt den Webserver.

Stellen Sie dem lokalen Webserver durch Eingabe der Adresse http://
localhost:8000 im Browser die erste Anfrage. Beobachten Sie die Log-Nach-
richten auf der Kommandozeile und die Anzeige im Browser. Der Webserver
reagiert und spielt eine Not Found Fehlermeldung aus. Logisch – bislang sind
noch keine Dateien verfügbar.

In Kapitel 11 werden Sie zusätzlich zum lokalen Webserver einen lo-


kalen MySQL-Datenbankserver auf Port 3306 starten.

6.2.3 Das Dokument-Wurzelverzeichnis: Document-Root


Die Dienstleistung eines Webservers ist die Auslieferung von Dateien. Clients
sollen dabei nur Zugriff auf gewünschte Dateien erhalten, nicht auf beliebige
Dateien des Host-Computers. Der Webserver bestimmt dazu ein Wurzelver-
zeichnis von dem ausgehend Dateien ausgeliefert werden dürfen, die so ge-
nannte Document-Root.
Bestimmt der Webserver das Verzeichnis ~/www als Document-Root, können
Clients alle Dateien unterhalb dieses Verzeichnisses abrufen. Aus Sicht des
Clients ist die Document-Root die Wurzel des Webserver-Dateisystems. Wie
im lokalen Dateisystem werden Dateien durch Pfade mit Schrägstrichen als
Verzeichnistrenner gebildet:

URL Pfad auf Webserver


http://localhost:8000/hello.html ~/www/hello.html
http://localhost:8000/img/kitty.jpg ~/www/img/kitty.jpg
http://localhost:8000/test.php ~/www/test.php

Tab. 6.1: Webserver mit Document-Root ~/www

134
Webserver auf dem eigenen Computer betreiben 6.2

Statische Dateien liefert der Webserver direkt aus. Legen Sie eine HTML-Da-
tei hello.html in die Document-Root ~/www:
<h1>Hallo!</h1>

Listing 6.1: hello.html gibt ein groß gedrucktes »Hallo!« aus

Über http://localhost:8000/hello.html wird die Datei angezeigt, der Brow-


ser interpretiert das HTML mit einer Überschrift ersten Grades. Legen Sie
ein Bild in ein Unterverzeichnis der Document-Root, z.B. img/kitty.jpg. Ein
Abruf im Browser zeigt das Bild. Legen Sie eine Datei test.php in die Docu-
ment-Root:
Hallo <strong><?php echo 'PHP'; ?></strong>!
Heute ist der <?php echo date('d.m.Y H:i:m'); ?>.

Listing 6.2: test.php wird zunächst serverseitig interpretiert

Der in PHP eingebaute Webserver unterstützt selbstverständlich PHP. PHP-


Skripte werden zunächst interpretiert und die erzeugte Ausgabe ausgeliefert.

Abb. 6.2: test.php im Browser

Aufgabe 2

Fällt Ihnen an der Ausgabe des PHP-Skripts etwas auf? Achten Sie auf die
Zeilenumbrüche.

Werfen Sie einen Blick in die Logs des Webservers auf der Kommandozeile.
Jede Anfrage wird dort protokolliert, u.a. mit
ƒ Datum
ƒ HTTP Status-Code: z.B. 200 für erfolgreichen Abruf oder 404 für eine nicht
gefundene Datei
ƒ HTTP-Verb: bisher nur GET
ƒ angefragter URL

135
Kapitel 6 Webseiten entwickeln und veröffentlichen

Abb. 6.3: Webserver-Konsole nach einigen Anfragen

Standardmäßig verwendet der PHP-Webserver das Verzeichnis, in dem er ge-


startet wurde, als Document-Root. Mit der zusätzlichen Option -t können
Sie die Document-Root beim Start explizit angeben und den Webserver von
überall aus starten:
> php -S localhost:8000 -t /C/Users/Philipp/www

Auf einem Produktionssystem ist besondere Vorsicht geboten. Sensible


Dateien, die nicht im Internet zugänglich sein sollen, müssen sich immer
außerhalb der Document-Root befinden!

6.2.4 Automatisch angezeigte Dateien: Index-Dateien


Statt einer konkreten Datei kann eine Internetadresse auch auf ein Verzeich-
nis wie die Wurzel selbst (/) oder ein Unterverzeichnis (z.B. /news/) zeigen.
Die Anfrage nach Verzeichnissen funktioniert auch ohne den abschließenden
Schrägstrich:
ƒ http://localhost:8000 entspricht http://localhost:8000/
ƒ http://localhost:8000/news entspricht http://localhost:8000/news/
Bei der Anfrage nach einem Verzeichnis sucht der PHP-Webserver nach spe-
ziellen Index-Dateien, zunächst nach index.php, dann index.html. Falls eine
dieser Index-Dateien vorhanden ist, liefert der Webserver sie automatisch aus,
ansonsten kommt es zu einer 404 - Not Found Fehlermeldung.

Aufgabe 3

Legen Sie in der Document-Root die Dateien index.php und index.html an.
Auf welchen verschiedenen Wegen können Sie die beiden Dateien abrufen?

136
Webserver auf dem eigenen Computer betreiben 6.2

6.2.5 Entwicklungszyklus mit PHP auf dem Webserver


Nach dem Start des Webservers und Ablage eines PHP-Skripts in die Docu-
ment-Root kann der Entwicklungszyklus einer dynamischen Webseite mit
PHP beginnen:
ƒ Bearbeitung des Skripts im Quelltext-Editor.
ƒ Aufruf des Skripts über den Browser und Beurteilung des Ergebnisses.
ƒ Nach Bedarf erneute Bearbeitung des Skripts, neu Laden der Seite im
Browser, Beurteilung des Ergebnisses usw.
Nach Abschluss der Entwicklung kann eine Veröffentlichung der Skripte auf
einer Produktionsumgebung erfolgen.
Der PHP-Webserver ist nur für Entwicklungszwecke gedacht. In einer
Produktionsumgebung kommt Webserver-Software wie nginx, Apa-
che oder IIS zum Einsatz. Diese Webserver bieten die nötige Stabilität
und Sicherheit sowie unzählige Konfigurationsmöglichkeiten. Auch
diese Webserver können lokal installiert und zur Entwicklung einge-
setzt werden.

6.2.6 Infos über PHP anzeigen: phpinfo()


Kapitel 4 zeigte die Auflistung aller Informationen zur PHP-Installation (u.a.
PHP-Version und php.ini-Einstellungen) auf der Kommandozeile mit dem
Befehl php -i. Die Funktion phpinfo() liefert diese Informationen als HTML
zur übersichtlichen Darstellung im Browser. Legen Sie ein Skript phpinfo.php
mit einem Aufruf der Funktion in die Document-Root:
<?php phpinfo();

Listing 6.3: phpinfo.php

Das Ergebnis im Browser zeigt Abbildung 6.4.


Die Ausgabe der Funktion phpinfo() sollte in einer Produktionsumge-
bung nie öffentlich einsehbar sein. Die Informationen könnten Angrei-
fern hilfreich sein.

137
Kapitel 6 Webseiten entwickeln und veröffentlichen

Abb. 6.4: phpinfo.php im Browser

Aufgabe 4

Finden Sie in der phpinfo()-Übersicht im Browser die aktuellen Einstellun-


gen der Direktiven memory_limit und date.timezone.

6.3 HTML-Grundgerüst
Webseiten bestehen aus HTML. Für den Browser spielt es keine Rolle, ob
es sich um statische HTML-Dateien oder durch PHP dynamisch generierte
Inhalte handelt. Ein PHP-Skript gibt zur Erzeugung einer Webseite, z.B. per
echo-Anweisung, HTML aus. Browser sind sehr tolerant bei der Interpretation
von HTML. Im Vergleich zu einem PHP-Programm kommt es bei Syntaxver-
stößen nicht zu einem Abbruch. Nach eigenem Ermessen gleicht der Browser
Dinge wie vergessene HTML-Tags oder unvollständige Seitenstrukturen aus.
Um die beabsichtigte Darstellung in allen Browsern zu erreichen, sollte man
sich aber nicht auf die Fehlertoleranz verlassen und sauberes HTML schrei-
ben. Gewisse Strukturen sind auch nötig, um HTML in Verbindung mit CSS
und JavaScript nutzen zu können. Eine vollständige HTML-Webseite hat fol-
gende Grundstruktur:
<!DOCTYPE html>
<html>
<head>
<title>Titel der Webseite</title>
</head>
<body>
<!-- Das ist ein unsichtbarer Kommentar -->

138
HTML-Grundgerüst 6.3

<p>Inhalt der Webseite</p>


</body>
</html>

Listing 6.4: Grundstruktur einer HTML-Datei (index.html)

Das body-Element enthält den sichtbaren Teil der Webseite.


Der HTML-Quelltext einer Webseite ist über Extras | Brow-
ser Werkzeuge | Seitenquelltext anzeigen oder den Shortcut
(Strg)+(U) einsehbar.

Mit PHP lassen sich dynamische Elemente einbetten:


<!DOCTYPE html>
<html>
<head>
<title>Willkommen bei PHP</title>
</head>
<body>
<p>Datum: <?php echo date('d.m.Y'); ?></p>
<p>Zufallszahl: <?php echo random_int(0, 99); ?></p>
</body>
</html>

Listing 6.5: Vollständige Webseite mit PHP (index.php)

Als index.php in der Document-Root abgespeichert zeigt der Aufruf von


http://localhost:8000 die Darstellung aus Abbildung 6.5.

Abb. 6.5: index.php im Browser

Beispiele für einige HTML-Elemente:

<p>...</p>
Paragraph: Ein Absatz.

139
Kapitel 6 Webseiten entwickeln und veröffentlichen

<a href="http://localhost">Linkttext</a>
Anchor: Klickbarer Link mit dem sichtbaren Linktext zur URL
http://localhost aus dem href-Attribut (Hypertext Reference).
<ul>
<li>Listenpunkt 1</li>
<li>Listenpunkt 2</li>
</ul>
Unordered list: Ungeordnete Liste.
<strong>Betonter Inhalt</strong>
Betont den Inhalt; sorgt üblicherweise für Fettdruck.
Tab. 6.2: Beispiele für HTML-Elemente und deren Funktion

Hinweis

Weitere Codebeispiele zeigen aus Platzgründen nur gekürzten HTML-


Code.

6.4 Anfragen und Antworten mit dem HTTP-


Protokoll
Zur Kommunikation müssen Browser und Webserver die gleiche Sprache
sprechen. Diese Sprache ist reiner, direkt lesbarer Text und im HTTP-Protokoll
festgelegt. Die meisten Browser bringen Entwickler-Werkzeuge zur Beobach-
tung der HTTP-Kommunikation mit.

6.4.1 Browser-Konsole nutzen


Die Entwickler-Werkzeuge öffnen Sie über das Browser-Menü Web-Ent-
wickler | Werkezeuge für Web-Entwickler.
Im unteren Drittel des Browsers öffnet sich ein Bereich mit Werkzeugen zur
Webseiten-Analyse. Der Tab Netzwerkanalyse gibt Einsicht in die HTTP-
Kommunikation mit dem Webserver. Jede Zeile zeichnet einen Zyklus aus
Anfrage (Request) und Antwort (Response) auf. Eine Aufzeichnung findet
nur bei geöffneten Entwickler-Werkzeugen statt, gegebenenfalls müssen Sie
eine Webseite neu laden. Eine Aufzeichnung gibt Auskunft zu welchem Host-
Computer (z.B. localhost:8000) die Anfrage geschickt und welche Datei (z.B.

140
Anfragen und Antworten mit dem HTTP-Protokoll 6.4

/phpinfo.php) angefordert wurde. Der Status der Antwort wird per Code an-
gezeigt, die wichtigsten Werte lauten:
ƒ 200: »OK« – die Datei wurde gefunden und ausgeliefert.
ƒ 404: »Not Found« – die Datei existiert nicht.

Abb. 6.6: Entwickler-Werkzeuge im Firefox-Browser öffnen

6.4.2 HTTP-Anfragen (Requests)


Beim Abruf von http://localhost:8000/phpinfo.php sendet der Browser fol-
gende HTTP-Anfrage im Klartext (hier verkürzt dargestellt):
GET /phpinfo.php HTTP/1.1
Host: localhost:8000
User-Agent: Macintosh Firefox/88.0 ...
Accept-Language: de,en ...
...

Die erste Zeile signalisiert die HTTP-Methode zum Abholen (GET) einer
gewünschten Datei (/phpinfo.php). Die weiteren Zeilen sind so genannte
HTTP-Kopfzeilen oder geläufiger in English: HTTP-Header bzw. kurz
Header. Jeder Header ist ein Schlüssel-Wert-Paar, getrennt durch Doppel-
punkt:
Name: Wert

141
Kapitel 6 Webseiten entwickeln und veröffentlichen

Der Header Host nennt Ziel-Domain und Port:


Host: localhost:8000

Header übermitteln wichtige Anfrage-Parameter an den Server, die er zur


Beantwortung der Anfrage nutzen kann. Der Header User-Agent (engl. für
»Stellvertreter des Benutzers«, gemeint ist der Computer/Browser) gibt
z.B. Auskunft über das Betriebssystem und den Browser des Benutzers, der
­Header Accept-Language über die bevorzugten Sprachen des Benutzers usw.
Durch Anklicken einer Anfrage im Tab Netzwerkanalyse der Entwickler-
Werkzeuge im Browser sind die Header im Detail einsehbar.

Abb. 6.7: HTTP-Header in der Browser-Konsole

6.4.3 Header im superglobalen Array $_SERVER


Die Anfrage-Header stellt PHP im superglobalen Array $_SERVER zur Verfü-
gung. Es gehört zu einer Gruppe von vordefinierten Arrays – den »Superglo-
bals« – die PHP jedem Skript automatisch zur Verfügung stellt. Diese Arrays
heißen »superglobal«, da sie im Gegensatz zu normalen Variablen nicht den
Einschränkungen der Geltungsbereiche (siehe Abschnitt 5.2.2) unterliegen.
Das heißt, sie können überall im Programm direkt genutzt werden, auch
­innerhalb von selbst definierten Funktionen. Eine Übersicht des Inhalts von
$_SERVER erhalten Sie mit var_dump():

<pre><?php var_dump($_SERVER); ?></pre>

Listing 6.6: superglobal-SERVER.php

142
Anfragen und Antworten mit dem HTTP-Protokoll 6.4

Das HTML-Element pre sorgt im Browser für eine übersichtliche Darstellung


der var_dump()-Ausgabe wie auf der Kommandozeile, da Leerzeichen und
Zeilenumbrüche exakt wiedergegeben werden. Neben vielen weiteren Infor-
mationen über die Anfrage befinden sich die HTTP-Anfrage-Header in den
Elementen mit dem Schlüssel-Präfix HTTP_. Beachten Sie, dass die Header-Na-
men in den Schlüsseln des $_SERVER-Arrays alle großgeschrieben und mit Un-
terstrichen statt Bindestrichen unterteilt sind. Den Header User-Agent finden
Sie folglich unter dem Schlüssel $_SERVER['HTTP_USER_AGENT'].
Auf Basis der Anfrage-Header kann ein Skript dynamisch reagieren. Für ei-
nen benutzerfreundlichen Software-Download könnte die passende Datei für
das Betriebssystem des Benutzers angeboten werden. Informationen über Be-
triebssystem und Browser des Benutzers enthält der Header User-Agent:
$userAgent = $_SERVER['HTTP_USER_AGENT'];
if (str_contains($userAgent, 'Windows')) {
echo '<a href=".exe">Download für Windows</a>';
} elseif (str_contains($userAgent, 'Macintosh')) {
echo '<a href=".pkg">Download für macOS</a>';
} elseif (str_contains($userAgent, 'Linux')) {
echo '<a href=".deb">Download für Linux</a>';
}

Listing 6.7: software-download.php

Hinweis

In den nächsten Kapiteln lernen Sie mit $_GET, $_POST, $_COOKIE und $_SES-
SION weitere superglobale Arrays kennen. Die Namen aller »Superglobals«
werden großgeschrieben und beginnen mit einem Unterstrich.

6.4.4 HTTP-Antworten
Auf die Anfrage nach http://localhost:8000/phpinfo.php sendet der Web-
server eine HTTP-Antwort (verkürzt dargestellt):
HTTP/1.1 200 OK
Date: Wed, 27 May 2022 10:13:12 GMT
Content-Type: text/html
... Weitere Header ...

<!DOCTYPE html>

143
Kapitel 6 Webseiten entwickeln und veröffentlichen

<html>
<head>
<title>PHP 8.0.3 - phpinfo()</title>
</head>
<body>...</body>
</html>

Die erste Zeile teilt neben der HTTP-Version den Status der Antwort mit, im
Beispiel 200 OK für eine gefundene Datei. Die restliche Antwort teilt sich im-
mer in zwei durch eine Leerzeile getrennte Bereiche:
ƒ Die Antwort-Header (Kopfzeilen): Wie in der Anfrage geben Schlüssel-
Wert-Paare wichtige Zusatzinformationen (Metadaten) über den eigentli-
chen Inhalt der Antwort. PHP erzeugt erforderliche Header automatisch,
z.B. das Datum (Header Date) der Antwort oder den Hinweis, von welcher
Art der folgende Inhalt ist (Content-Type). Mit der Funktion header() kön-
nen Sie eigene Header erzeugen. Die gesendeten Header sind nur über die
Entwickler-Konsole im Browser sichtbar.
ƒ Der Inhalt: Nach der Leerzeile folgt der im Browser-Fenster sichtbare In-
halt einer Webseite als sogenannte »Nutzlast« (payload oder body). Die
Payload ist alles, was in der Skriptdatei durch PHP ausgegeben wurde (z.B.
per echo oder var_dump()), bzw. außerhalb von PHP-Tags steht (etwa große
Teile des HTML-Codes).
HTTP-Header können das Verhalten des Browsers beeinflussen. Der Content-
Type-Header text/html veranlasst den Browser etwa, den Inhalt als HTML zu
interpretieren.
Mit der Funktion header() schreiben Sie bei Bedarf eigene Header in die Ant-
wort. Durch Setzen des Headers Content-Type können Sie z.B. die Art des
gesendeten Inhalts selbst bestimmen:
header('Content-Type: text/plain'); // Header
echo '<h1>Hallo text/plain</h1>'; // Payload

Listing 6.8: HTTP-Header und Payload in hello-plain-text.php

Der Content-Type-Header text/plain veranlasst den Browser, den Inhalt statt


wie zuvor als HTML wie eine reine Textdatei anzuzeigen, d.h. kein HTML zu
interpretieren:

144
Anfragen und Antworten mit dem HTTP-Protokoll 6.4

Abb. 6.8: Browser-Anzeige richtet sich nach Content-Type

Neben HTML (text/html) und reinem Text (text/plain) gibt es für alle Arten
von Inhalt eine offizielle Bezeichnung, den so genannten MIME-Type1. Wei-
tere Beispiele sind application/pdf für PDF-Dateien, image/jpeg für JPEG-
Bilddateien oder application/zip für ZIP-Archive. Alle MIME-Types können
im Content-Type-Header genutzt werden, um Clients die Art der gesendeten
Payload mitzuteilen. Ein Browser kann so z.B. entsprechend reagieren (HTML
interpretieren, PDF darstellen, Bild anzeigen, ZIP-Archiv zum Download an-
bieten usw.). Im Verlauf des Buchs werden Sie sehen, wie PHP neben Text und
HTML auch Bilder und PDFs erzeugen kann.

Aufgabe 5

Wieso interpretiert der Browser Ausgaben aus PHP-Skripten standardmä-


ßig als HTML?
Tipp: Prüfen Sie die php.ini-Direktive default_mimetype.

6.4.5 HTTP als zustandsloses Protokoll


Jede HTTP-Anfrage an einen Webserver ist völlig unabhängig von vorigen
Anfragen, auch wenn sie vom gleichen Benutzer stammen. Klickt ein Benut-
zer eine Schaltfläche zum Abstimmen in einer Umfrage, ist es mit HTTP nicht
möglich festzustellen, ob derselbe Benutzer bereits vorher abgestimmt hat,
es »vergisst« die vorigen Anfragen. Diese Eigenschaft des HTTP-Protokolls
nennt man zustandslos (stateless).

1
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/
MIME_types

145
Kapitel 6 Webseiten entwickeln und veröffentlichen

Wie sind jedoch Funktionalitäten möglich, die Zusammenhänge zwischen


Anfragen herstellen? Wie kann ein PHP-Skript wissen, ob sich ein Benutzer
in einer vorausgehenden Anfrage eingeloggt oder etwas in einen Warenkorb
gelegt hat? Dazu kommen sogenannte Benutzersitzungen (Sessions) zum Ein-
satz (Kapitel 9).

6.5 Webspace mieten und Webseite


veröffentlichen
Die unkomplizierteste Möglichkeit, eine PHP-Webseite im Internet zu veröf-
fentlichen, ist die Anmietung von Webspace bei einem Webhoster. Webhoster
betreiben Server im Internet. Sie kümmern sich um Hardware, Internetan-
bindung und die sichere Konfiguration von Betriebssystem und weiterer Soft-
ware. Der Webspace ist ein eigener Teilbereich auf einem solchen Internetser-
ver. Da sich mehrere Kunden einen Server teilen (Shared Hosting), sind die
Kosten gering. Individuelle Einstellungen werden über eine Weboberfläche
vorgenommen. Dateien gelangen per Upload in den Webspace, z.B. mit einem
FTP-Programm. Neben Speicherplatz und Webserver (mit PHP-Unterstüt-
zung) sind meist auch Domainnamen, Datenbanken, E-Mail-Postfächer und
die Einrichtung von Cronjobs inklusive.

6.5.1 Anbieter finden


Es gibt zahlreiche Webhosting-Anbieter. Einige bekannte Namen sind Strato,
1&1 Ionos, HostEurope, Hetzner oder alfahosting. Führen Sie bei Bedarf eine
Recherche nach einem passenden Anbieter durch.
Die Mindestanforderungen für eigene Webseiten sollten sein: einige Gigabyte
Speicherplatz, mindestens 1 Domain inklusive, PHP-Unterstützung in einer
aktuellen Version, mindestens eine MySQL-Datenbank, FTP-Zugang, SSL-
Verschlüsselung, E-Mail-Postfächer, Administrationsoberfläche.

6.5.2 Webspace in der Praxis


Dieser Abschnitt stellt einen Webspace am Beispiel des Anbieters Hetzner vor.
Webspace-Pakete können Sie dort über www.hetzner.com/de/webhosting ver-
gleichen und bestellen (siehe Abbildung 6.9).
Zur Bestellung legen Sie einen Account in der Verwaltungsoberfläche an.
Während des Bestellvorgangs wählen Sie einen verfügbaren Domainnamen
und hinterlegen Ihre persönlichen Daten.

146
Webspace mieten und Webseite veröffentlichen 6.5

Abb. 6.9: Webspace-Pakete vergleichen und bestellen

In der Verwaltungsoberfläche haben Sie anschließend Zugriff auf etliche Ein-


stellungen (PHP, Webserver, E-Mail, FTP, Datenbanken etc.) und können Zu-
gangsdaten für Datenbanken, FTP oder E-Mail-Postfächer verwalten. Beach-
ten Sie, dass die Einstellungsmöglichkeiten (z.B. für den erlaubten Speicher-
verbrauch eines PHP-Skripts) in einem Webspace eingeschränkt sind, um das
Gesamtsystem (und damit andere Kunden) zu schützen.

Abb. 6.10: Webspace verwalten

147
Kapitel 6 Webseiten entwickeln und veröffentlichen

Der einfachste Weg, bei Hetzner eigene Skripte in den Webspace hochzula-
den, ist »WebFTP«, eine spezielle Weboberfläche.

Abb. 6.11: WebFTP – FTP über den Browser

Sofern Sie eine Datei in die Document-Root hochgeladen haben, ist sie über
die Domain-Adresse öffentlich im Internet erreichbar. Viele Webhoster bieten
auch Zugriff auf eine Verzeichnisebene über der Document-Root, um Dateien
vor öffentlichem Zugriff geschützt abzulegen.
Für den täglichen Gebrauch empfiehlt sich die Verwendung eines speziellen
FTP-Programms, z.B. FileZilla (https://filezilla-project.org) für Win-
dows, macOS und Linux.

6.6 Übungen
Übung 1
Schreiben Sie ein Skript, das die HTTP-Anfrage-Header aus dem supergloba-
len $_SERVER-Array auflistet.

Übung 2
Folgendes Skript erzeugt mit Hilfe von Funktionen aus der »gd«-Erweiterung
(siehe Abschnitt 4.2.4) ein PNG-Bild (MIME-Type: image/png) mit einem ro-
ten Quadrat:
$image = imagecreate(100, 100); // Bild erzeugen
imagecolorallocate($image, 255, 0, 0); // rot färben
imagepng($image); // Bilddaten ausgeben

Der Browser zeigt jedoch nur »Buchstabensalat«. Wie bringen Sie den Brow-
ser zu einer korrekten Interpretation, um das Bild sichtbar zu machen?

148
Dynamische Webseiten
und Formulare
Die Interaktion mit einer Webseite findet über HTML-Elemente im Browser
statt. Die serverseitige Technologie ist dem Betrachter verborgen. Ein server-
seitiges Skript kann nicht den Anfrage-Antwort-Zyklus des HTTP-Protokolls
unterbrechen, um wie auf der Kommandozeile interaktiv Eingaben vom Be-
trachter zu erfragen. Stattdessen transportiert eine HTTP-Anfrage Benutzer­
eingaben durch Klicken eines Links oder Abschicken eines Formulars zum
Webserver:
ƒ Die GET-Methode schickt Benutzerdaten beim Klicken eines Links als Teil
der Internetadresse einer Webseite (URL) im sogenannten Query-String
zum Webserver. PHP stellt sie im superglobalen $_GET-Array zur Verfü-
gung.
ƒ Die POST-Methode schickt Benutzerdaten beim Absenden eines Formulars
als »Nutzlast« (payload) im unsichtbaren Rumpfteil (body) der Anfrage an
den Webserver. PHP stellt sie im superglobalen $_POST-Array zur Verfü-
gung.
PHP-Skripte können auf die Benutzereingaben dynamisch reagieren.

7.1 $_GET: Daten aus dem Query-String der URL


Ein fiktives Kunstprojekt möchte im Internet ein Kunstwerk zeigen – das Bild
eines Quadrats in der Bilddatei square.png:

Abb. 7.1: Ein kunstvolles Quadrat in der Datei square.png

149
Kapitel 7 Dynamische Webseiten und Formulare

Um das Projekt spannender zu gestalten, soll die Möglichkeit bestehen, das


Bild mehrfach anzuzeigen, wobei Besucher die Anzahl der Wiederholungen
selbst bestimmen können. Bei einer statischen Webseite ohne PHP kann ein
Besucher per Klick auf verschiedene Links zwischen der Anzeige von ein, zwei
oder drei Quadraten entscheiden:
<p>Quadrate! - Wieviele möchten Sie sehen?</p>
<a href="/squares-1.html">1 Quadrat</a><br/>
<a href="/squares-2.html">2 Quadrate</a><br/>
<a href="/squares-3.html">3 Quadrate</a>

Listing 7.1: Statische Einstiegsseite index.html

Abb. 7.2: Startseite index.html der statischen Version

Jede Zielseite muss einzeln als statische HTML-Seite angelegt werden – jeweils
für die Anzeige von ein, zwei und drei Quadraten:
<!-- /squares-1.html (Datei mit einem Quadrat) -->
<img src="/square.png" alt="Quadrat 1"/>

<!-- /squares-2.html (Datei mit zwei Quadraten) -->


<img src="/square.png" alt="Quadrat 1"/>
<img src="/square.png" alt="Quadrat 2"/>

<!-- /squares-3.html (Datei mit drei Quadraten) -->


<img src="/square.png" alt="Quadrat 1"/>
<img src="/square.png" alt="Quadrat 2"/>
<img src="/square.png" alt="Quadrat 3"/>

Listing 7.2: Statische HTML-Seiten mit je 1, 2 oder 3 Quadraten

Ein Klick auf den Link zur Anzeige von drei Quadraten löst eine GET-Anfrage
auf die Datei squares-3.html aus (Abbildung 7.3).

150
$_GET: Daten aus dem Query-String der URL 7.1

Abb. 7.3: Anzeige dreier Quadrate mit statischem HTML

Aufgabe 1

Testen Sie die statische Version des Kunstprojekts auf Ihrem Computer.
Starten Sie dazu den eingebauten PHP-Webserver auf localhost:8000 und
setzen Sie die Document-Root auf das Unterverzeichnis art-project/v1-
static aus den Code-Downloads zu diesem Kapitel (www.mitp.de/0395).

Eine angedachte Ausweitung des Projekts auf mehr als drei Quadrate ist mit
Aufwand verbunden: Jedes zusätzliche Quadrat erfordert die Ergänzung ei-
nes Links auf der Startseite und eine weitere HTML-Datei mit der passenden
Anzahl an Bild-Elementen. Flexibler und pflegeleichter ist eine dynamische
Lösung mit PHP, wie die nächsten Abschnitte zeigen.

7.1.1 Der Query-String in Hyperlinks


Die Aufgabe zur Anzeige einer bestimmten Anzahl an Quadraten kann statt
mit einzelnen HTML-Dateien (squares-1.html, squares-2.html usw.) in ei-
nem einzigen PHP-Skript squares.php gelöst werden:
$numberOfSquares = 3; // Anzahl der Quadrate
for($n = 1; $n <= $numberOfSquares; $n++) {
echo '<img src="/square.png"/>';
}

Listing 7.3: Variable Quadrate-Anzahl in squares.php

151
Kapitel 7 Dynamische Webseiten und Formulare

Die Quadrate-Anzahl ist mit dem Wert 3 in der Variablen $numberOfSquares


noch fest vorgegeben (»hart codiert«). Mit einer kleinen Änderung kann der
Wert der Variable jedoch über die Internetadresse (URL) bestimmt werden:
$numberOfSquares = $_GET['number'];
for($n = 1; $n <= $numberOfSquares; $n++) {
echo '<img src="/square.png"/>';
}

Ein Aufruf des Skripts squares.php mit dem Anhängsel ?number=12 in der
URL zeigt ohne Anpassung des HTML gleich ein Dutzend Quadrate:
http://localhost:8000/squares.php?number=12

Das Skript wird weiterhin mit der GET-Methode aufgerufen.

Abb. 7.4: GET-Parameter bestimmt die Quadrate-Anzahl

Hinweis

Zum Testen der zweiten, dynamischen Version des Projekts starten Sie den
lokalen Webserver mit dem Verzeichnis art-project/v2-query-string aus
den Code-Downloads als Document-Root.

Mit dem Fragezeichen in der URL (?) beginnt der so genannte Query-String,
der Eingabedaten an serverseitige Skripte übermitteln kann. Der Query-String
enthält Schlüssel-Wert-Paare nach dem allgemeinen Schema:
script.php?key1=wert1&key2=wert2&key3=wert3

152
$_GET: Daten aus dem Query-String der URL 7.1

Ein Schlüssel-Wert-Paar aus dem Query-String heißt URL-Parameter oder


auch GET-Parameter (nach der HTTP-Anfrage-Methode GET). Mehrere
URL-Parameter trennt das Et-Zeichen »&« (kaufmännisches Und; engl. Am-
persand). Die enthaltenen Daten stellt PHP dem Skript script.php im super-
globalen $_GET-Array zur Verfügung:
echo $_GET['key1']; // => wert1
echo $_GET['key2']; // => wert2
echo $_GET['key3']; // => wert3

Im fiktiven Kunstprojekt stellt ein Aufruf der URL http://localhost:8000/


squares.php?number=12 dem Skript squares.php den URL-Parameter num-
ber aus dem Query-String unter $_GET['number'] zur Verfügung. Dieser be-
stimmt die Anzahl der Quadrate:
$numberOfSquares = $_GET['number']; // = 12

Die statische Startseite index.html wird ebenfalls in ein dynamisches PHP-


Skript index.php überführt. Es muss nicht mehr jeder Link einzeln geschrie-
ben werden:
<p>Quadrate - Wieviele möchten Sie sehen?</p>
<?php for($n = 1; $n <= 12; $n++): ?>
<a href="/squares.php?number=<?= $n ?>">
<?= $n ?> Quadrat<?= $n > 1 ? 'e' : '' ?>
</a><br/>
<?php endfor; ?>

Listing 7.4: Dynamische Einstiegsseite index.php

Die Abbruchbedingung der Zählschleife bestimmt die Anzahl der Links. Mit
$n<=12 generiert das Skript z.B. gleich ein Dutzend Links mit den passenden
Query-Strings:
<a href="/squares.php?number=1">1 Quadrat</a>
<a href="/squares.php?number=2">2 Quadrate</a>
...
<a href="/squares.php?number=12">12 Quadrate</a>

Die dynamische Variante des Projekts hat nun die gleiche Funktionalität wie
die statische, benötigt jedoch weniger Dateien und Code. Um die Anzahl der
Links und damit die mögliche Anzahl an Quadraten zu variieren, ist nur ein
Wert im Schleifenkopf des Skripts index.php anzupassen.

153
Kapitel 7 Dynamische Webseiten und Formulare

7.1.2 Niemals Benutzereingaben vertrauen:


Eingabevalidierung
Ein URL-Parameter führt keine Information über seinen Datentyp mit sich.
Werte im $_GET-Array liefert PHP immer als Strings. Der Query-String ...
script.php?string=PHP&int=3&float=3.14&boolean=true

... ergibt das $_GET-Array:


array(4) {
["string"]=> string(3) "PHP"
["int"]=> string(1) "3"
["float"]=> string(4) "3.14"
["boolean"]=> string(4) "true"
}

Im Rahmen des fiktiven Kunstprojekts vertraut das Skript squares.php zur


Darstellung der Quadrate darauf, dass der URL-Parameter number eine sinn-
volle Zahl enthält, damit die Abbruchbedingung der Schleife wie erwartet
funktioniert:
$numberOfSquares = $_GET['number'];
for($n = 1; $n <= $numberOfSquares; $n++) {
echo '<img src="/square.png"/>';
}

Die Abbruchbedingung der for-Schleife verwendet den URL-Parameter


number ungeprüft als String – die Zählvariable $n hingegen ist immer eine
Ganzzahl (Integer). Beim Vergleich zwischen Integer und einem numerischen
String führt PHP aufgrund seiner »Type-Juggling«-Eigenschaften (Abschnitt
2.5.1) eine automatische Konvertierung des Strings in eine Ganzzahl durch
und sorgt dafür, dass das Programm wie erwartet funktioniert. Bei number=3
lautet der Vergleich 1<='3' und ist daher äquivalent zu 1<=3.
Doch jeder Benutzer könnte den Query-String manipulieren und unerwarte-
te Situationen für die Abbruchbedingung im Schleifenkopf herbeiführen; der
GET-Parameter number könnte:
ƒ gar nicht gesetzt sein.
ƒ keine Zahl sein, d.h. nicht numerisch: number=abc oder number= (leerer
String)
ƒ negativ oder sehr hoch sein: number=-5 bzw. number=999999999999999.

154
$_GET: Daten aus dem Query-String der URL 7.1

Ein Programm darf Eingabewerten niemals blind vertrauen. Eine Überprü-


fung (Validierung) muss sicherstellen, dass Eingabewerte den Erwartungen
des Programms entsprechen, um sich vor unliebsamen Überraschungen zu
schützen. Schlägt die Validierung fehl, kann das Programm dem Benutzer
Rückmeldung über den ungeeigneten Eingabewert geben oder mit einem
sinnvollen Vorgabewert weiterarbeiten.
Die folgenden Abschnitte zeigen die Auswirkungen unerwarteter Eingabe-
werte für den URL-Parameter number im fiktiven Kunstprojekt. Es werden
sinnvolle Reaktionen auf unerwünschte Eingabewerte eingeführt. Im Zusam-
menhang mit Formularen sehen Sie später im Kapitel auch Rückmeldungen
an den Benutzer bei ungeeigneten Eingaben.

URL-Parameter nicht gesetzt


Enthält der Query-String gar keinen URL-Parameter number, kommt es in
squares.php beim Zugriff auf den nicht vorhandenen Schlüssel im $_GET-Ar-
ray zu einem Fehler der Stufe Warning (siehe Abschnitt 4.3):
$numberOfSquares = $_GET['number']; // = null
// Warning: Undefined array key "number"
for($n = 1; $n <= $numberOfSquares; $n++) { ... }

Nach einer Warnung läuft das Skript jedoch weiter und PHP setzt die Va-
riable $numberOfSquares auf null. Die Abbruchbedingung der Schleife lautet
bei der ersten Iteration 1<=null. Der unerwartete Vergleich zweier unglei-
cher Datentypen ruft das »Type Juggling« von PHP auf den Plan (Abschnitt
2.5.1), d.h. PHP passt die Datentypen für den Vergleich nach seinem internen
(recht undurchsichtigen) Regelwerk automatisch an. In diesem Fall kommt es
zu einem Vergleich des Wahrheitsgehalts beider Operanden: Eine Zahl un-
gleich Null entspricht wahr, null entspricht falsch. Der resultierende Vergleich
true<=false ergibt immer false. Die Schleife wird daher nie ausgeführt und
der Benutzer sieht gar kein Quadrat bzw. je nach Fehlersichtbarkeit (Abschnitt
4.4) die Warnung. Eine vorige Prüfung hilft zum einen die Warnung zu ver-
meiden und zum anderen die Variable $numberOfSquares sinnvoll auf 0 zu
setzen:
$numberOfSquares = isset($_GET['number'])
? $_GET['number']
: 0;

155
Kapitel 7 Dynamische Webseiten und Formulare

Der ternäre Operator liest sich: »Falls $_GET['number'] gesetzt ist (engl. is set),
initialisiere $numberOfSquares mit dem Wert von $_GET['number'], ansonsten
mit 0«. Ist der URL-Parameter nicht gesetzt, kann das Skript mit $numberOf­
Squares=0 weiterarbeiten (siehe unten); Kopfzerbrechen über das mögliche
Verhalten ist bei dem resultierenden Vergleich 1<=0 im Gegensatz zu 1<=null
ausgeschlossen.

URL-Parameter ist keine Zahl (nicht numerisch)


Auch wenn der Wert des URL-Parameters number nicht numerisch ist (z.B.
number=abc oder ein leerer String mit number=), ist das Verhalten von PHP
nicht mehr offensichtlich. Beim Vergleich in der Abbruchbedingung der
Schleife zwischen einer Ganzzahl und einem nicht-numerischen String kon-
vertiert PHP nun nämlich die Zahl in einen String (nicht umgekehrt wie zu-
vor) und führt einen lexikalischen Vergleich durch, d.h. nach den Regeln der
Lexikon-Sortierung. Da im Lexikon Zahlen vor Buchstaben einsortiert wer-
den, ergibt die Bedingung z.B. für den String abc immer true, da:
var_dump(1 <= 'abc'); // '1' vor 'a' => bool(true)
var_dump(2 <= 'abc'); // '2' vor 'b' => bool(true) usw.

Die for-Schleife wird zur Endlosschleife (Abschnitt 3.3.9) und hört nicht auf,
immer mehr Quadrate anzuzeigen.
Falls Sie die dynamische Typisierung in den beschriebenen Situationen als
verwirrend empfinden, sind Sie sicher nicht allein. Solche Situationen sollten
besser vermieden werden. Im Kunstprojekt muss dazu der Datentyp für den
Eingabewert number zu Beginn klargestellt werden, anstatt dies später PHP
zu überlassen. Da das Programm ohnehin nur mit einer Ganzzahl als Einga-
be sinnvoll arbeitet, ist dies durch ein explizites Type-Casting in den Integer-­
Datentyp mit dem Casting-Operator (int) (Abschnitt 2.5.2) möglich:
$numberOfSquares = isset($_GET['number'])
? (int) $_GET['number'] // Nicht-Zahlen = 0
: 0;

Nach diesem Schritt ist sichergestellt, dass die Variable $numberOfSquares im-
mer vom Typ Integer ist und anschließend nur Vergleiche zwischen Zahlen
stattfinden. Nicht-Zahlen wie abc oder der leere String werden beim Casting
in die Zahl 0 umgewandelt.

156
$_GET: Daten aus dem Query-String der URL 7.1

URL-Parameter ist keine sinnvolle Zahl


Bisher ist sichergestellt, dass $numberOfSquares mit einer Ganzzahl initialisiert
wird, nicht jedoch, ob sie in einem sinnvollen Bereich liegt. Sehr hohe Zahlen
(number=99999999999999) könnten zu einer Überlastung durch die Darstel-
lung zu vieler Bilddateien führen. Dieser Fall lässt sich durch eine Begrenzung
verhindern:
if ($numberOfSquares > 100) {
$numberOfSquares = 100; // nicht mehr als 100
}

Auf negative Zahlen und die Null lässt sich passend reagieren:
if ($numberOfSquares < 1) {
echo 'Keine Quadrate.';
}

Hinweis

Die dritte Version des Projekts mit Eingabevalidierung starten Sie mit dem
Verzeichnis art-project/v3-validation als Document-Root.

Zusammenfassung
Programme dürfen Eingabewerte niemals ungeprüft verwenden. Die Einga-
bevalidierung von squares.php für den URL-Parameter number sorgt für eine
sichere Ausführung des Skripts ohne Überraschungen:
$numberOfSquares = isset($_GET['number'])
? (int) $_GET['number']
: 0; // Variable enthält sicher eine Ganzzahl!
if ($numberOfSquares > 100) { // nicht zu groß
$numberOfSquares = 100;
}
if ($numberOfSquares < 1) { // nicht zu klein
echo 'Keine Quadrate.';
}
for($n = 1; $n <= $numberOfSquares; $n++) {

157
Kapitel 7 Dynamische Webseiten und Formulare

echo '<img src="/square.png"/>;


}

Listing 7.5: squares.php

Die Startseite index.php wird auf den unterstützten Bereich der Eingabevali-
dierung mit bis zu 100 Quadraten angepasst:
<p>Quadrate - Wieviele möchten Sie sehen?</p>
<?php for($n = 1; $n <= 100; $n++): ?>
<a href="/squares.php?number=<?= $n ?>">
<?= $n ?> Quadrat<?= $n > 1 ? 'e' : '' ?>
</a><br/>
<?php endfor; ?>

7.2 Formulardaten im Query-String der URL


übermitteln
Das fiktive Kunstprojekt des vorigen Abschnitts besteht aus zwei Skripten:
ƒ index.php generiert feste Links zu squares.php?number=Anzahl, um 1 bis
100 Quadrate anzuzeigen.
ƒ squares.php zeigt zwischen 1 und 100 Quadrate an; die Anzahl bestimmt
der URL-Parameter number.
Um das Projekt interaktiver und übersichtlicher zu gestalten, soll ein Formu-
lar die vielen festen Links in index.php ersetzen. Der Betrachter kann die ge-
wünschte Anzahl von Quadraten als Zahl eingeben:
<form action="/squares.php">
<label>Wieviele Quadrate?</label><br/>
<input name="number"/><br/>
<button type="submit">Anzeigen</button>
</form>

Listing 7.6: index.php mit Formular anstelle von Links

Das HTML-Element form erzeugt einen Formularbereich, alle Elemente in-


nerhalb gehören zum Formular:
ƒ Das label-Element erzeugt eine Beschriftung für das folgende Eingabefeld.
ƒ Das input-Element erzeugt ein einzeiliges Text-Eingabefeld mit dem Na-
men number.

158
Formulardaten im Query-String der URL übermitteln 7.2

ƒ Das button-Element vom Typ submit erzeugt eine Schaltfläche zum Absen-
den des Formulars.

Abb. 7.5: Starseite index.php mit Formular statt Links

Das Absenden des Formulars löst eine neue HTTP-Anfrage mit der GET-Me-
thode an die URL aus dem action-Attribut des form-Elements aus: squares.
php. Ohne Angabe einer Domain komplettiert der Browser die URL automa-
tisch mit derselben Domain, von der das Formular geladen wurde. Die Ziel-
URL lautet daher http://localhost:8000/squares.php. Außerdem wandelt
der Browser die Daten aller Formularfelder in einen Query-String um und
hängt diesen an die Ziel-URL. Jedes Eingabefeld erzeugt einen URL-Parame-
ter mit dem Namen des Felds als Schlüssel und dem eingegebenen Inhalt als
Wert. Mit Eingabe des Werts 12 in das input-Feld namens number lautet die
vollständige Ziel-URL beim Absenden des Formulars:
http://localhost:8000/squares.php?number=12

Das Skript squares.php bedarf keiner Anpassung. Die Eingabe der Quadrate-
Anzahl wurde von den Links in das Formular verlagert.

Hinweis

Die vierte Version des Projekts mit Eingabevalidierung starten Sie mit dem
Verzeichnis art-project/v4-form als Document-Root.

7.2.1 Formular und Auswertung in einem Skript


zusammenfassen
Das fiktive Kunstprojekt besteht weiterhin aus zwei Skripten:
ƒ index.php zeigt ein Formular zur Eingabe der Anzahl der Quadrate und
sendet die Eingabe als URL-Parameter number an squares.php.
ƒ squares.php kann zwischen 1 und 100 Quadrate anzeigen, die Anzahl be-
stimmt der URL-Parameter number.

159
Kapitel 7 Dynamische Webseiten und Formulare

Zur Vereinfachung und für direktes Feedback über unzulässige Formularein-


gaben kann das Projekt in der Datei index.php zusammengefasst werden. Zu-
nächst das Formular:
<form action="">
<label>Wieviele Quadrate?</label><br/>
<input name="number"/><br/>
<button type="submit">Anzeigen</button>
</form>

Listing 7.7: Formular in der Datei index.php

Das Absenden eines Formulars mit leerem action-Attribut sendet das For-
mular an dieselbe URL zurück, von der es geladen wurde. Die Eingabevalidie-
rung kann daher (in abgewandelter Form) im selben Skript über dem Formu-
lar platziert werden, die Anzeige der Quadrate darunter:
<?php
$numberOfSquares = 0; 1
$error = false; 2
if (isset($_GET['number'])) { 3
$number = (int) $_GET['number']; 4
if ($number > 0 && $number <= 100) { 5
$numberOfSquares = $number;
} else {
$error = true; 6
}
}
?>
<form action="">...</form>
<?php
for($n = 1; $n <= $numberOfSquares; $n++) { 7
echo '<img src="/square.png"/>';
}

Listing 7.8: Formular-Auswertung in der Datei index.php

ƒ 1 $numberOfSquares speichert die Anzahl der Quadrate. Der Vorgabewert


lautet 0 und wird bei Übermittlung eines gültigen Werts durch das Formu-
lar überschrieben.

160
Formulardaten im Query-String der URL übermitteln 7.2

ƒ 2 $error speichert, ob ein Eingabefehler vorliegt (wahr oder falsch). Der


Vorgabewert lautet false und wird im Falle eines Eingabefehlers mit true
überschrieben.
ƒ 3 Der Anweisungsblock zur Eingabevalidierung wird aktiv, falls der URL-
Parameter number vorhanden ist, d.h. das Formular abgeschickt wurde.
Beim ersten Aufruf wird der Block übersprungen, $numberOfSquares und
$error behalten ihre Vorgabewerte.
ƒ 4 Wurde das Formular abgeschickt, startet die Eingabevalidierung. Der
number-Parameter wird in eine Ganzzahl gecastet und der temporären Va-
riablen $number zugewiesen.
ƒ 5 Liegt $number im zulässigen Bereich zwischen 1 und 100, ist alles in Ord-
nung und $number darf den Vorgabewert in $numberOfSquares überschrei-
ben.
ƒ 6 Liegt $number nicht im zulässigen Bereich, wird $error auf true gesetzt,
um sich den Eingabefehler zu merken.
ƒ 7 Unterhalb des Formulars wird entsprechend $numberOfSquares die nö-
tige Quadrate-Anzahl angezeigt. Ohne Absenden des Formulars bleibt es
beim Vorgabewert 0 und kein Quadrat ist sichtbar.
Eine Variable, die wie $error zur Markierung eines bestimmten Zu-
stands dient, heißt auch Flag. Im Beispiel kann $error durch die Zu-
stände true bzw. false markieren, ob es einen Eingabefehler gab oder
nicht. Es handelt sich um ein boolsches Flag für zwei verschiedene
Zustände.
Innerhalb des Formulars entscheidet das $error-Flag über die Anzeige einer
Fehlermeldung.
<form action="">
<label>Wieviele Quadrate?</label><br/>
<?php if ($error): ?>
<em style="color:red">Nur Zahlen zwischen 1 und 100!</em><br/>
<?php endif; ?>
<input name="number"/><br/>
<button type="submit">Anzeigen</button>
</form>

Eine häufige Maßnahme zur Verbesserung der Benutzerfreundlichkeit ist die


Wiederanzeige eingegebener Werte in einem Formular bei einem Eingabefeh-
ler. Ein Nutzer kann dann, ausgehend von seinen bisherigen Eingaben, Kor-
rekturen vornehmen und muss nicht komplett von vorne beginnen. Der sicht-

161
Kapitel 7 Dynamische Webseiten und Formulare

bare Eingabewert eines input-Felds befindet sich in seinem value-Attribut.


Beim erstmaligen Laden des Formulars soll kein Wert gesetzt sein:
<input name="number" value=""/>

Bei der erneuten Anzeige des Formulars im Falle eines Eingabefehlers soll das
Attribut hingegen vorbelegt sein, z.B.:
<input name="number" value="Fünf"/>

Ob das Formular abgeschickt und damit auf Grund eines Eingabefehlers er-
neut angezeigt wird, lässt sich am gesetzten URL-Parameter number erkennen.
Nur in diesem Fall soll das Feld mit der bisherigen Eingabe vorbelegt werden:
<input name="number" value="<?php if (isset($_GET['number'])) {
echo $_GET['number']; } ?>"/>

Abb. 7.6: Wiederanzeige der Eingabe nach Eingabefehler

Hinweis

Die fünfte Version des Projekts mit nur einem Skript starten Sie mit dem
Verzeichnis art-project/v5-single-file als Document-Root.

7.2.2 Niemals Eingabedaten vertrauen: Ausgabe-Maskierung


Im vorigen Abschnitt hat sich bei der Wiederanzeige des Eingabewerts im
number-Feld ein Sicherheitsproblem eingeschlichen. Der URL-Parameter num-
ber wird direkt in den HTML-Code ausgegeben:

<input name="number" value="<?php if (isset($_GET['number'])) {


echo $_GET['number']; } ?>"/>

162
Formulardaten im Query-String der URL übermitteln 7.2

Neben der erwarteten Eingabe einer Zahl kann ein böswilliger Benutzer auf
geschickte Weise HTML und damit auch JavaScript zur clientseitigen Pro-
grammierung in die Webseite einschleusen. Betrachten Sie folgende Formu-
lareingabe:
3" onclick="alert('Sie wurden gehackt!')"

Abb. 7.7: Eingabe mit bösen Absichten

Mit der 3 ist zunächst wie gewünscht eine Zahl enthalten. Im Anschluss been-
det jedoch das doppelte Anführungszeichen das value-Attribut und es kann
beliebiger HTML-Code in die Seite eingebracht werden. Nach Abschicken des
Formulars offenbart ein Blick in den Seitenquelltext (Extras|Browserwerkz
euge|Seitequelltext) das Ergebnis:
<input name="number" value="3" onclick="alert('Sie wurden ge-
hackt!')"/>

Das onclick-Attribut führt das enthaltene JavaScript beim Anklicken des Ein-
gabefelds aus. Die JavaScript-Funktion alert() zeigt daraufhin ein Hinweis-
fenster mit dem Text Sie wurden gehackt! an. Wird der Link zur Ergebnisseite
des Formulars an einen unbedarften Benutzer weitergegeben, erlebt dieser
beim Klick in das Eingabefeld eine Überraschung.

Abb. 7.8: XSS-Attacke

Sobald das Einschmuggeln von HTML und JavaScript in eine Webseite mög-
lich ist, können Benutzer Opfer eines solchen Cross-Site-Scripting (XSS) An-
griffs werden. Dabei wird bösartiger Code in einen vertrauenswürdigen Kon-

163
Kapitel 7 Dynamische Webseiten und Formulare

text eingebracht, um etwa Benutzerkonten zu übernehmen, Daten auszuspä-


hen oder den Inhalt einer Webseite zu verändern.
Die Antwort zur Vermeidung von XSS-Sicherheitslücken heißt Ausgabe-Mas-
kierung oder Englisch Output-Escaping. Um die fälschliche Interpretation von
Eingabedaten als HTML auszuschließen, müssen alle Zeichen, die in HTML
eine besondere Bedeutung haben, »maskiert« werden. Durch Maskierung ver-
lieren die Zeichen ihre besondere Bedeutung.
Die Zeichen mit besonderer Bedeutung in HTML lauten: <, >, &, und ". Die
PHP-Funktion htmlspecialchars() konvertiert diese Sonderzeichen in un-
schädliche HTML-Entitäten:
echo htmlspecialchars('Sonderzeichen: < > & "');
// => Sonderzeichen: &lt; &gt; &amp; &quot;

Listing 7.9: HTML-Sonderzeichen umwandeln (htmlspecialchars.php)

Mit HTML-Entitäten können die HTML-Sonderzeichen von normalem Text


unterschieden werden, z.B. wenn der Vergleich 7 < 10 in HTML dargestellt
werden soll:
<strong>7 < 10</strong>

Das Kleiner-Zeichen soll hier kein HTML-Tag öffnen, sondern als solches an-
gezeigt werden. Das gelingt mit der HTML-Entität &lt;:
<strong>7 &lt; 10</strong>

Sonderzeichen Entität Beschreibung


< &lt; less than / kleiner als
> &gt; greater than / größer als
& &amp; ampersand / Et-Zeichen
" &quot; quotation / Anführungszeichen
Tab. 7.1: HTML-Entitäten zur Darstellung von HTML-Sonderzeichen

Eingabedaten dürfen niemals direkt in die Skript-Ausgabe zur Generie-


rung einer Webseite übernommen werden. Es muss immer eine Maskie-
rung der HTML-Sonderzeichen erfolgen, um Manipulationen des Web-
seiten-Inhalts auszuschließen.
Das Formularfeld im Kunstprojekt wird durch den Einsatz von htmlspecial-
chars() vor einem Cross-Site-Scripting-Angriff geschützt:

164
Einsatzgebiete der GET-Methode 7.3

<input name="number" value="<?php if (isset($_GET['number'])) {


echo htmlspecialchars($_GET['number']); } ?>"/>

Die böswillige Beispiel-Eingabe von oben kann keinen Schaden mehr anrich-
ten, der HTML-Quellcode mit maskierten Anführungszeichen lautet:
<input name="number" value="3&quot; onclick=&quot;alert('Sie wur-
den gehackt!')&quot;"/>

7.3 Einsatzgebiete der GET-Methode


Bei der GET-Methode des HTTP-Protokolls sind Parameter bzw. Formular-
eingaben, die an den Server gesendet werden sollen, offen im Query-String
der URL eingebettet. Vollständige URLs sind unter anderem in der Adresszei-
le des Browsers, in den Logs des Webservers, in Ergebnissen einer Suchma-
schine oder beim Versenden per E-Mail sichtbar.

Vorteile
Die GET-Methode eignet sich hauptsächlich, um Inhalte einer Webseite mit
Hilfe von Parametern an individuelle Bedürfnisse anzupassen, z.B. durch
Sucheingaben, Filter oder Sortierungen. Aufrufe mit der GET-Methode haben
normalerweise keine Seiteneffekte, d.h. sie erzeugen oder ändern keine Daten,
sondern rufen sie lediglich ab. So ändert eine Suche mit einer Suchmaschine
nicht den Datenbestand, sondern filtert mittels URL-Parametern gewünschte
Ergebnisse aus dem Datenbestand heraus.
Eine URL inklusive Query-String kann als Lesezeichen gespeichert oder wei-
tergegeben werden. Mit den Vor- und Zurück-Schaltflächen des Browsers
kann man durch den Verlauf von GET-Anfragen navigieren. Der Aufruf einer
URL mit Parametern führt später für gewöhnlich zur gleichen Ansicht.

Aufgabe 2

Suchen Sie mit Google nach dem Begriff PHP. Bearbeiten Sie die URL der
Ergebnisseite, so dass im Query-String nur noch der GET-Parameter mit
dem Suchbegriff enthalten ist. Passen Sie den Suchbegriff in der URL an,
um ohne Formulareingabe direkt auf die Ergebnisseite für den Suchbegriff
MySQL zu gelangen.

165
Kapitel 7 Dynamische Webseiten und Formulare

Nachteile
Da URL-Parameter offen einsehbar sind, ist die GET-Methode nicht zur Über-
tragung sensibler Daten geeignet. Eine URL wie login.php?password=s3cr3t
offenbart Zugangsdaten leicht an Unbefugte.
Eine URL ist in der Länge limitiert; eine Übertragung von größeren Daten-
mengen, z.B. langer Texte oder Dateiuploads, ist daher nicht möglich. Das
Limit kann je nach Browser variieren, etwa 2000 Zeichen sollten nicht über-
schritten werden.
Bei der Erzeugung oder Änderung von Inhalten aus Formulardaten (Blog-
Einträge, Kommentare, Bild-Uploads usw.) handelt es sich um einmalige
Aktionen. Der nochmalige Aufruf dieser Aktionen durch Vor- und Zurück-
Navigieren oder einen Aufruf aus den Lesezeichen ist nicht gewollt, weshalb
auch in diesen Fällen die GET-Methode nicht geeignet ist.
Eine Datenübertragung ohne URL-Parameter und Größenbeschränkung er-
laubt die POST-Methode, die Sie im nächsten Abschnitt kennenlernen.

7.4 $_POST: Formulardaten unsichtbar


übermitteln
Die GET-Methode dient in erster Linie dem Abrufen (engl. to get) von Daten.
Im Gegensatz dazu steht das Senden (engl. to post) von Daten an den Web-
server, z.B. um Daten auf dem Server zu speichern oder zu ändern. Hierfür
kommt die POST-Methode des HTTP-Protokolls zum Einsatz. Ein vertrauter
Anwendungsfall ist die Registrierung bei einem Newsletter:
<form action="" method="post">
<input name="email"/><br/>
<button type="submit">Anmelden</button>
</form>

Listing 7.10: Anmeldung bei einem Newsletter (subscribe.php)

Statt Daten abzufragen, werden Daten einmalig an den Webserver übertra-


gen, um sich in eine Liste mit E-Mail-Adressen einzutragen. Die Anfrage kann
anschließend »vergessen« werden; eine URL /subscribe.php?email=me@exam­
ple.com zur Wiederholung der Aktion ist daher nicht sinnvoll.
Standardmäßig versendet ein Formular seine Daten per GET-Methode. Zum
ªen auf die POST-Methode ist das method-Attribut des form-Elements auf den

166
$_POST: Formulardaten unsichtbar übermitteln 7.4

Wert post zu setzen. Statt in der URL transportiert HTTP die Formulardaten
dann verborgen im Rumpf der Anfrage als »Nutzlast« (Payload) in Richtung
Server. Der Inhalt folgt mit einer Leerzeile getrennt auf die Anfrage-Header.
Allgemein lautet das Schema einer POST-Anfrage:
POST /script.php HTTP/1.1
Host: localhost:8000
User-Agent: ...
Content-Type: application/x-www-form-urlencoded
...

formularfeld1=eingabe1&formularfeld2=eingabe2

Der Inhalt ist wie ein Query-String kodiert – die Formulardaten sind eben-
falls Schlüssel-Wert-Paare und mit Et-Zeichen (&) voneinander getrennt. Die
Browserkonsole gibt nach dem Absenden eines Formulars Aufschluss über
die HTTP-Anfrage:

Abb. 7.9: POST-Anfrage mit Formulardaten

In PHP erfolgt der Zugriff auf POST-Daten über das superglobale $_POST-
Array. Bislang reagiert das Skript zur Newsletter-Registrierung noch nicht
auf das abgesendete Formular. Eine if-Anweisung kann feststellen, ob POST-
Daten übermittelt wurden:
<?php if (isset($_POST['email'])): ?>
<!-- Todo: Abspeichern der E-Mail-Addresse -->
Danke, <?= htmlspecialchars($_POST['email']) ?>
wurde registriert.
<?php else: ?>

167
Kapitel 7 Dynamische Webseiten und Formulare

<form action="" method="post">


<input name="email"/><br/>
<button type="submit">Anmelden</button>
</form>
<?php endif; ?>

Aufgabe 3

So wie eine HTTP-Antwort Inhalt zum Client transportiert, transportiert


die HTTP-Anfrage eines POST-Formulars Daten zum Server. Wie lautet
der »Content-Type« (siehe auch Abschnitt 6.4.4) einer solchen POST-An-
frage?
Tipp: Beobachten Sie die Header in der »Netzwerkwerkanalyse« der Brow-
ser-Konsole.

Neu laden von POST-Anfragen


Der Versuch, eine POST-Anfrage im Browser neu zu laden, führt im Vergleich
zu einer GET-Anfrage zu einer Browser-Warnung:

Abb. 7.10: Warnung beim neu laden einer POST-Anfrage

Der Benutzer wird darauf hingewiesen, dass das neu Laden dem wiederholten
Absenden des Formulars entspricht und entsprechende Folgen haben könn­-
te – bei einem Bestellformular etwa die nochmalige Aufgabe der gleichen Be-
stellung.
Ein eleganter Weg zur Vermeidung dieser möglicherweise verwirrenden Mel-
dung ist es, den Benutzer nach erfolgreicher Verarbeitung des Formulars di-
rekt auf eine neue Seite weiterzuleiten (engl. redirect). Das gelingt durch Set-
zen eines Location-Antwort-Headers mit der Funktion header():

168
Vergleich zwischen GET und POST 7.5

<?php
if (isset($_POST['email'])) {
/* Todo: Abspeichern der E-Mail-Addresse */
header('Location: /thanks.php');
exit();
}
?>
<form action="" method="post">...</form>

Listing 7.11: Weiterleitung nach Formularverarbeitung (subscribe-redirect.php)

Der Location-Header weist den Browser beim Empfang der Antwort an, so-
fort auf die angegebene URL /thanks.php weiterzuleiten. Der Aufruf von
exit() stellt das Ende der Skriptausführung sicher, da die Verarbeitung des
Formulars abgeschlossen ist und der Benutzer auf das neue Skript weitergelei-
tet werden wird. Nach Erhalt der Antwort lädt der Browser aufgrund der per
Location-Header angewiesenen Weiterleitung direkt die neue Seite thanks.
php (per GET-Methode). Auf dieser Seite würde ein Neu-Laden das Skript
thanks.php wiederholt abrufen statt das Formular der nunmehr vorletzten
Seite erneut zu übermitteln.

Hinweis

Starten Sie zum Test und Vergleich der Registrierungs-Beispiele (mit und
ohne Redirect) den Webserver mit dem Unterverzeichnis subscription/
aus den Code-Downloads zum Kapitel als Document-Root.

7.5 Vergleich zwischen GET und POST


Wichtige Merkmale der GET- und POST-Methode im Vergleich:

Merkmal GET POST


Links als Auslöser Ja Nein
Formular als Auslöser Ja Ja
Datensichtbarkeit URL-Parameter öffentlich Formulardaten un-
sichtbar sichtbar im Rumpf
der Anfrage
Webserver Logs Speicherung der URL-Parameter Keine Speicherung
der Formulardaten

169
Kapitel 7 Dynamische Webseiten und Formulare

Merkmal GET POST


Browser-Navigation: Erneutes Senden der URL- Warnung vor erneu-
Neu laden, Zurück, Vor Parameter tem Senden der For-
mulardaten
Lesezeichen Speicherung mit URL-Parame- Speicherung ohne
tern Formulardaten
Datenmenge ca. 2000 Zeichen Unbegrenzt
Dateiuploads Nein Ja
Datenzugriff in PHP $_GET $_POST

Tab. 7.2: Vergleich der HTTP-Methoden GET und POST

7.6 Übungen
Übung 1
Erstellen Sie ein Formular zur Eingabe von Vor- und Nachname. Begrüßen Sie
den Besucher nach Abschicken des Formulars per GET-Methode an dasselbe
Skript mit dem eingegebenen Namen.

Übung 2
Folgendes Skript erzeugt ein PNG-Bild mit dem schwarzen Text TEST auf hel-
lem Grund (erfordert aktivierte gd-Erweiterung, siehe Abschnitt 4.2.4):
header('Content-Type: image/png');
$image = imagecreate(500, 75); // Bildgröße 500x75px
imagecolorallocate($image, 200, 200, 200); // Hintergrund
$textcolor = imagecolorallocate($image, 0, 0, 0);
imagestring($image, 30, 25, 25, 'TEST', $textcolor);
imagepng($image); // Bilddaten ausgeben

Verwenden Sie den Bild-Code, um das Skript aus Übung 1 so zu überarbei-


ten, dass der eingegebene Name nach Absenden des Formulars auf dem Bild
erscheint.

170
Übungen 7.6

Übung 3
Erweitern Sie die finale Version des fiktiven Kunstprojekts aus Abschnitt 7.2
(Verzeichnis art-project/v5-single-file in den Code-Downloads zum Ka-
pitel) mit einem select-Element um die Auswahl einer Form: Quadrat oder
Kreis.
<select name="shape">
<option value="square">Quadrat</option>
<option value="circle" selected>Kreis</option>
</select>

Hinweis: Ergänzen Sie ein Bild circle.png von einem Kreis und machen Sie
den Bildpfad des img-Tags vom Eingabewert des neuen Formularfelds ab­
hängig.

Abb. 7.11: Kunstprojekt mit Form-Auswahl

Übung 4
Ändern Sie das Skript aus Übung 1 von der GET- auf die POST-Methode.

171
Mit Dateien arbeiten
Jedes Skript bestand bislang aus einer einzelnen Datei. Größere Skripte wer-
den dabei unübersichtlich und Code aus einem Skript kann nicht in einem
anderen wiederverwendet werden. Zur besseren Strukturierung und Wieder-
verwendung lässt sich Quelltext auf mehrere Dateien aufteilen.
Andere Dateien können auch als Datenquelle bzw. -speicher dienen. Ein
Skript liest dabei Daten aus einer Datei ein oder speichert sie dort. So kön-
nen z.B. Benutzereingaben in einer Datei abgelegt und in einem späteren Pro-
grammlauf wieder eingelesen werden – eine einfache Art, Daten dauerhaft zu
speichern.
In diesem Kapitel lernen Sie den Umgang mit Dateien zur Strukturierung von
Quelltexten sowie zum Lesen und Speichern von Daten kennen.

8.1 Quelltext in mehreren Dateien strukturieren


Stellen Sie sich einen kleinen Webauftritt vor, der aus einigen Webseiten
(bzw. PHP-Skripten) besteht: Startseite (index.php), Bildergalerie (pictures.
php), Gästebuch (guestbook.php) und Impressum (imprint.php). Für ein ein-
heitliches Erscheinungsbild auf jeder Seite müssen alle Dateien das gleiche
HTML-Grundgerüst enthalten. Der Kopf- und Fußbereich (HTML-Element
header bzw. footer) mit den Links zu den anderen Seiten sollen immer gleich
aussehen. Nur der Seitentitel und Inhalt im Hauptbereich (HTML-Element
main) unterscheiden sich individuell von Seite zu Seite. Die Startseite index.
php könnte wie folgt aufgebaut sein, wobei die individuellen Bereiche hervor-
gehoben sind:
<html>
<head>
<title>Startseite</title>
</head>

173
Kapitel 8 Mit Dateien arbeiten

<body>
<header>
<img src="/logo.png"/>
<nav>
<a href="/index.php">Startseite</a> -
<a href="/pictures.php">Bilder</a> -
<a href="/guestbook.php">Gästebuch</a>
</nav>
<h1>Startseite</h1>
</header>
<main>
<h2>Willkommen auf meiner Homepage!</h2> ...
</main>
<footer>
<a href="/imprint.php">Impressum</a>
</footer>
</body>
</html>

Jede einzelne Webseite entspricht einem Skript. Auf dem Host-Computer lie-
gen alle Skripte z.B. im Projektverzeichnis ~/website:
~/website
guestbook.php
index.php
imprint.php
pictures.php

Die Webseiten sind erreichbar, sobald der Webserver mit dem Verzeichnis
~/website als Document-Root startet:

> php -S localhost:8000 -t ~/website

Die Skripte haben viele Bereiche gemeinsam. Das heißt, jede Datei wiederholt
viel gleichen HTML-Code, unterscheidet sich eigentlich jedoch nur im Titel
und im Inhalt des main-Elements. Hier das (gekürzte) Skript pictures.php mit
den hervorgehobenen, individuellen Bereichen, an denen es sich von index.
php unterscheidet:

<html>
<head>

174
Quelltext in mehreren Dateien strukturieren 8.1

<title>Bilder</title>
</head>
<body>
<header>
<img src="logo.png"/>
<nav>...</nav>
<h1>Bilder</h1>
</header>
<main>
<h2>Meine schönsten Bilder</h2> ...
</main>
<footer>...</footer>
</body>
</html>

Änderungen an den gemeinsamen Bereichen, z.B. die Ergänzung eines Menü-


punkts, erfordern eine Anpassung aller Skripte. Der nächste Abschnitt stellt
eine nachhaltigere Lösung vor.

Hinweis

Die beschriebene erste Version der Website finden Sie in den Code-Down-
loads zum Kapitel (www.mitp.de/0395) unter website/version-1, die in den
nächsten Abschnitten verbesserten Versionen entsprechend unter versi-
on-2 und version-3.

8.1.1 Quelltext-Dateien einbinden und ausführen


Eine nachhaltigere Strukturierung der Quelltexte wird mit der Aufteilung des
Codes von Kopf-, Haupt- und Fußbereich in separate Dateien erreicht. Der
Kopfbereich ist in die Datei _header.php ausgelagert:
<html>
<head>
<title>Startseite</title>
</head>
<body>
<header>
<img src="/logo.png"/>

175
Kapitel 8 Mit Dateien arbeiten

<nav>...</nav>
<h1>Startseite</h1>
</header>
<main>

Listing 8.1: Ausgelagerte Kopfbereich in der Datei _header.php

Analog der Fußbereich in die Datei _footer.php:


</main>
<footer>...</footer>
</body>
</html>

Listing 8.2: Ausgelagerter Fußbereich in der Datei _footer.php

Die Dateien _header.php und _footer.php sind nicht als eigenständige Skripte
gedacht, sondern werden von den Hauptskripten (index.php, pictures.php
usw.) mit der require-Anweisung eingebunden.
Der Unterstrich als Präfix in den Dateinamen _header.php und _foot­
er.php ist eine Konvention. Sie drückt aus, dass diese Dateien keine
eigenständigen Skripte sind, sondern von anderen Skripten eingebun-
den werden.
In den Hauptskripten ersetzen die Einbindungen mit require die gemeinsa-
men Kopf- und Fußbereiche, wie hier in der Startseite index.php.
<?php require __DIR__.'/_header.php'; ?>
<h2>Willkommen auf meiner Homepage</h2> ...
<?php require __DIR__.'/_footer.php'; ?>

Listing 8.3: Startseite index.php mit eingebundenem Kopf- und Fußbereich

Erinnerung: Die magische Konstante __DIR__ enthält den absoluten


Pfad zum Verzeichnis des ausgeführten Skripts (Abschnitt 2.7.4).
Ausgehend von diesem Verzeichnis kann in der require-Anweisung
der Pfad zur Einbindung einer anderen Datei vervollständigt werden.
Für das Endergebnis macht es keinen Unterschied, ob der Quelltext einge-
bunden oder direkt im Hauptskript selbst notiert wurde. Die übrigen Seiten
werden auf gleiche Weise umgebaut, z.B. pictures.php:
<?php require __DIR__.'/_header.php'; ?>
<h2>Meine schönsten Bilder</h2>

176
Quelltext in mehreren Dateien strukturieren 8.1

<img src="..."/> <img src="..."/> ...


<?php require __DIR__.'/_footer.php'; ?>

Listing 8.4: Bildergalerie pictures.php mit eingebundenem Kopf- und Fußbereich

Änderungen an den allgemeinen Bereichen des Webauftritts sind nun viel


einfacher geworden. In der neuen Struktur wirkt sich eine Anpassung in
_header.php oder _footer.php sofort auf alle Seiten aus, z.B. die Ergänzung
eines neuen Menüpunkts.

8.1.2 Geltungsbereich von Variablen bei


eingebundenen Dateien
Der Kopfbereich in _header.php, der nun von allen Skripten verwendet wird,
enthält noch den statischen Seitentitel der Startseite:
<html>
<head>
<title>Startseite</title>
</head>
<body>
<header>
<img src="/logo.png"/>
<nav>…</nav>
<h1>Startseite</h1>
</header>
<main>

Der Einsatz einer Variablen ($title) sorgt für einen dynamischen Seitentitel,
so dass jede Hauptseite einen eigenen Titel verwenden kann:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<header>
<img src="logo.png"/>
<nav>…</nav>
<h1><?= $title ?></h1>
</header>
<main>

177
Kapitel 8 Mit Dateien arbeiten

Im eingebundenen Skript kann der PHP-Modus mit PHP-Tags ebenso ein-


und ausgeschaltet werden wie im Hauptskript. Variablen befinden sich im
gleichen Geltungsbereich wie die Zeile, an der das Skript eingebunden wird.
Die Variable $title kann somit im Hauptskript initialisiert werden und ist im
eingebundenen Quellcode der Datei _header.php »sichtbar«.
<?php
$title = 'Startseite';
require __DIR__ . '/_header.php';
?>
<h2>Willkommen auf meiner Homepage</h2> ...
<?php require __DIR__ . '/_footer.php'; ?>

Listing 8.5: $title bestimmt den Seitentitel in index.php

Andere Seiten verfahren gleichermaßen, z.B. pictures.php, und erhalten da-


durch ihren individuellen Seitentitel:
<?php
$title = 'Bildergalerie';
require __DIR__ . '/_header.php';
?>
<h2>Meine schönsten Bilder</h2>
<img src="..."/> <img src="..."/> ...
<?php require __DIR__ . '/_footer.php'; ?>

Listing 8.6: $title bestimmt den Seitentitel in pictures.php

Umgekehrt könnte auch das Hauptskript Variablen »sehen«, die das


eingebundene Skript neu definiert.

8.1.3 Dateien außerhalb der Document-Root einbinden


Mit der bisherigen Verzeichnisstruktur der Website sind alle Dateien aus der
Document-Root ~/website über den Browser zugänglich, z.B:

Datei URL
~/website/index.php localhost:8000/
~/website/pictures.php localhost:8000/pictures.php
~/website/guestbook.php localhost:8000/guestbook.php

178
Quelltext in mehreren Dateien strukturieren 8.1

Datei URL
~/website/_header.php localhost:8000/_header.php
~/website/_footer.php localhost:8000/_footer.php

Tab. 8.1: Alle Skripte aus ~/website sind erreichbar

Die mit der Neustrukturierung entstandenen Skripte _header.php und _foot­


er.php müssen allerdings nicht direkt öffentlich zugänglich sein, eigenstän-
dige Aufrufe ergeben keinen Sinn. Die Unterscheidung von öffentlichen und
nicht-öffentlichen Dateien gelingt mit einer Unterteilung des Projektverzeich-
nisses ~/website:
~/website
_footer.php
_header.php
/public
guestbook.php
imprint.php
index.php
pictures.php

Alle öffentlichen Dateien werden in das Unterverzeichnis public/ verschoben


und der Webserver mit der Document-Root ~/website/public neu gestartet:
> php -S localhost:8000 -t ~/website/public

Die require-Anweisungen in den öffentlichen Hauptskripten müssen die Pfa-


de anpassen, um die Dateien aus dem übergeordneten, nicht-öffentlichen Ver-
zeichnis einzubinden:
<?php
$title = 'Startseite';
require __DIR__.'/../_header.php';
?>
<h2>Willkommen auf meiner Homepage</h2> ...
<?php require __DIR__.'/../_footer.php'; ?>

Anschließend funktioniert die Website wie zuvor, die Dateien _header.php


und _footer.php sind jedoch nicht mehr öffentlich erreichbar.

179
Kapitel 8 Mit Dateien arbeiten

8.2 Dateien schreiben und lesen


Abschnitt 7.2 zeigte ein Formular zur Newsletter-Registrierung:
<?php
if (isset($_POST['email'])) {
/* To-do: Abspeichern der E-Mail-Addresse */
header('Location: /thanks.php');
exit();
}
?>
<form action="" method="post">
<input name="email"/><br/>
<button type="submit">Anmelden</button>
</form>

Eingegebene E-Mail-Adressen gehen bislang »verloren«, da sie nirgends abge-


speichert werden. Dies soll sich in den folgenden Abschnitten ändern.

Hinweis

Die Beispielskripte zu diesem Abschnitt finden Sie im Unterverzeichnis


newsletter/ der Code-Downloads (www.mitp.de/0395) zu diesem Kapitel.

8.2.1 Dateien schreiben: file_put_contents()


Die einfachste Form zur dauerhaften Datenspeicherung ist eine Textdatei. Die
PHP-Funktion file_put_contents() schreibt Textdaten in eine Datei:
file_put_contents('/Pfad/zur/Datei', 'Textdaten');

Die Newsletter-Registrierung kann die E-Mail-Adresse z.B. in der Datei


emails.txt sichern:

if (isset($_POST['email'])) {
file_put_contents(__DIR__.'/emails.txt', $_POST['email']);
header('Location: /thanks.php');
exit();
}

Listing 8.7: E-Mail-Adressen in Datei speichern (subscribe.php)

180
Dateien schreiben und lesen 8.2

Standardmäßig überschreibt file_put_contents() bei jedem Aufruf den ge-


samten Inhalt der Datei; es könnte also immer nur eine einzige E-Mail-Ad-
resse gespeichert werden. Um neue Daten an den bestehenden Inhalt anzu-
hängen (engl. to append), übergibt man die von PHP vordefinierte Konstante
FILE_APPEND als drittes Argument.

file_put_contents(
__DIR__ . '/emails.txt',
$_POST['email'] . "\n", // mit Zeilenumbruch!
FILE_APPEND
);

Mit jeder Registrierung erweitert sich die Datei nun um eine Zeile:
lukas@me.com
lina@gmx.net
lea@gmail.com ...

Listing 8.8: emails.txt mit einigen Registrierungen

Aufgabe 1

Aus Datenschutzgründen sollte die Datei emails.txt nicht öffentlich ab-


rufbar sein. Wo lässt sie sich sicher ablegen?

Aufgabe 2

Welchen Wert enthält die vordefinierte Konstante FILE_APPEND?

8.2.2 Dateien lesen: file_get_contents()


Der Administrator der Website möchte die aktuelle Liste der Newsletter-
Abonnenten einsehen. Die Funktion file_get_contents() liest eine Datei als
String ein:
$contents = file_get_contents('/Pfad/zur/Datei');

Folgendes Skript liest die Datei emails.txt ein und listet die gesammelten
E-Mail-Adressen zeilenweise auf:
$contents = file_get_contents(__DIR__.'/emails.txt'); 1
$emails = explode("\n", $contents); 2

181
Kapitel 8 Mit Dateien arbeiten

foreach ($emails as $email) {


echo $email . '<br/>';
}

Listing 8.9: Auflistung registrierter E-Mail-Adressen in admin.php

ƒ 1 file_get_contents()liest den kompletten Inhalt der Datei ein.


ƒ 2 explode() splittet den eingelesenen Datei-Inhalt an den Zeilenumbrü-
chen (Escape-Sequenz \n, siehe Abschnitt 2.2.2) in ein Array auf.

8.2.3 Dateien als Array einlesen: file()


Das Einlesen einer Datei in ein Array wie im vorigen Abschnitt, bei dem jedes
Element einer Zeile entspricht, ist eine häufige Aufgabe. Mit file() bietet PHP
dafür eine »Komfortfunktion«. So lässt sich die Aufgabe aus dem letzten Ab-
schnitt eleganter lösen:
$emails = file('emails.txt'); // $emails ist direkt ein Array
foreach ($emails as $email) {
echo $email . '<br/>';
}

Listing 8.10: Auflistung registrierter E-Mail-Adressen in admin-file.php

8.3 Dateien über das Internet laden


Das Lesen von Dateien ist nicht auf lokale Dateien des Host-Computers be-
schränkt, auch der Datenabruf von einer URL ist möglich. Folgendes Skript
lädt den HTML-Quellcode einer Wikipedia-Seite herunter und gibt ihn aus:
echo file_get_contents('https://de.wikipedia.org/wiki/PHP');

Listing 8.11: Wikipedia-Seite mit PHP abrufen (get-wiki-page.php)

Das Laden fremder Inhalte ermöglicht das sogenannte »Web Scraping« (engl.
etwa: »das Web schürfen«). Mit entsprechenden String-Funktionen können
automatisiert Informationen aus anderen Webseiten extrahiert und weiter-
verarbeitet werden, z.B. aus Nachrichtenseiten, Wetterberichten, Fahrplänen,
Sporttickern usw.
Die Weiterverarbeitung fremder Inhalte ist rechtlich bedenklich. Für viele In-
formationen gibt es jedoch offizielle Alternativen in Form von Web-Schnitt-
stellen. Der Anbieter eines bestimmten Diensts (z.B. für den Wetterbericht)
erlaubt es durch eine solche Schnittstelle anderen Programmen, sich über das

182
Dateien über das Internet laden 8.3

Internet an den Dienst anzudocken. Im Programmier-Jargon heißt eine Web-


Schnittstelle auch Web-API (API steht für Application Programming Inter-
face). Die Kommunikation mit der API erfolgt über gewöhnliche Internetad-
ressen (URLs). Der Datenaustausch findet in strukturierten Text-Formaten
wie dem populären JSON statt. JSON ist für die Weiterverarbeitung durch
Software gedacht, gleichzeitig aber dennoch gut für Menschen lesbar.
OpenWeather (www.openweathermap.org) bietet beispielsweise eine freie Web-
API für Wetterdaten an. Nach der kostenlosen Registrierung erhalten Sie ei-
nen individuellen Schlüssel (»API-Key«), der als Passwort zum Abruf aktuel-
ler Wetterdaten für eine beliebige Stadt von einer URL dient. Die gewünschte
Stadt und weitere Optionen (Sprache, Maßeinheiten, API-Key) werden als
URL-Parameter übergeben, z.B. für Berlin:
https://api.openweathermap.org/data/2.5/weather?q=Berlin&lang=de&
units=metric&appid=XXXXX

Ein Aufruf der gezeigten OpenWeather-URL im Browser zeigt die JSON-Ant-


wort (hier gekürzt):
{
"main": {
"temp": 20.76,
"feels_like": 19.86,
"pressure": 1002,
"humidity": 37
},
"wind": {
"speed": 4.47,
"deg": 141
}
...
}

Listing 8.12: JSON-Wetterdaten, abgefragt von OpenWeather

Die JSON-Daten sind reiner Text und unabhängig von PHP. Jede Program-
miersprache kann JSON-Daten einlesen und in eigene interne Datenstruk-
turen übersetzen. Sie können erkennen, dass die zurückgelieferte Struktur
einem assoziativen Array (d.h. mit String-Schlüsseln) in PHP sehr ähnlich
ist. Es gibt Schlüssel-Wert-Paare und die Verschachtelung mit geschweiften
Klammern bildet zwei Dimensionen.

183
Kapitel 8 Mit Dateien arbeiten

Als ob die Daten in einer lokalen Datei vorlägen, kann PHP die Wetterdaten
mit file_get_contents() von der URL abrufen:
$weatherUrl = 'https://api.openweathermap.org/data/2.5/weather?q=B
erlin&lang=de&units=metric&appid=XXXXX';
$data = file_get_contents($weatherUrl);

Listing 8.13: Wetterdaten abrufen in openweather.php

Die Funktion json_decode() übersetzt die JSON-Textdaten anschließend in


ein echtes assoziatives PHP-Array:
$data = file_get_contents($weatherUrl);
$weather = json_decode($data, true); 1
printf(
'Temperatur in Berlin: %s°',
round($weather['main']['temp']) 2
); // z.B. => Temperatur in Berlin: 21°

ƒ 1 Das erste Argument für json_decode() ist der reine JSON-String aus der
Variablen $data. Das zweite Argument (true) bestimmt, dass das Ergebnis
der Übersetzung ein assoziatives Array sein soll. Das Ergebnis-Array wird
der Variablen $weather zugewiesen.
ƒ 2 Vergleichen Sie den Array-Zugriff mit den reinen JSON-Daten. Sie er-
kennen ein Element main und darunter die Temperatur im Element temp.
Die genaue Struktur und Benennung der JSON-Daten bestimmt der An-
bieter der Web-API (hier: OpenWeather) und muss bei Unklarheiten in der
Dokumentation nachgeschlagen werden.

8.4 Datei-Uploads: $_FILES


Mit einem Formular können Website-Besucher eigene Dateien vom lokalen
Computer auf den Webserver hochladen, etwa zum Teilen von Bildern. In
HTML erzeugt das input-Element vom Typ file eine Schaltfläche zum Öffnen
eines Dialogs zur Dateiauswahl:
<form method="post" enctype="multipart/form-data">
Bild: <input type="file" name="picture"/>
Beschreibung: <input name="description"/>
<button type="submit">Absenden</submit>
</form>

Listing 8.14: Formular mit Feld zum Dateiupload (file-upload.php)

184
Datei-Uploads: $_FILES 8.4

Ohne action-Attributs sendet der Browser das Formular an dieselbe


URL, die das Formular bereitgestellt hat.

Abb. 8.1: Auswahldialog zum Dateiupload

Es ist zu beachten:
ƒ Datei-Uploads sind nur mit der POST-Methode möglich.
ƒ Das Formular muss im Attribut enctype den speziellen Kodierungstyp
(engl. encoding type) multipart/form-data angeben. Ohne diese Angabe
werden alle Formulardaten außer den Dateien übertragen.

8.4.1 $_POST und $_FILES


Empfängt ein PHP-Skript Formulardaten mit Dateiuploads, stehen die Daten
gewöhnlicher Formularfelder wie bereits bekannt im $_POST-Array zur Verfü-
gung. Daten zu den Dateiuploads stellt PHP im superglobalen $_FILES-Array
bereit. Pro Upload-Feld befindet sich darin ein Array-Element mit Metadaten
über die jeweilige hochgeladene Datei. Metadaten sind Informationen über
die Datei (Name, Typ, Größe, Speicherort), aber nicht der Dateiinhalt selbst.
print_r() sorgt für eine übersichtliche Darstellung eines Arrays:

<pre><?php print_r($_POST); print_r($_FILES) ?></pre>


<form>...</form>

Das Textfeld description befindet sich im $_POST-Array, das Dateiupload-


Feld picture im $_FILES-Array.

185
Kapitel 8 Mit Dateien arbeiten

Abb. 8.2: $_POST und $_FILES nach Dateiupload

8.4.2 Uploads verarbeiten


Das zweidimensionale $_FILES-Array enthält je Dateiupload ein Array-Ele-
ment mit folgenden Metadaten:

Schlüssel Beschreibung Beispiel


name Original-Dateiname circle.png
type Art der Datei (MIME-Type) image/png
size Dateigröße in Bytes 7699 (~ 7,5KB)
tmp_name Temporärer Dateipfad /tmp/phpAAB2nV
error Fehlercode des Uploads 0

Tab. 8.2: Metadaten je Dateiupload im $_FILES-Array

PHP generiert für jede hochgeladene Datei einen zufälligen Dateinamen (z.B.
phpAAB2nV) und legt die Datei mit diesem Namen für die Laufzeit des Skripts
in einem Verzeichnis für temporäre Dateien ab, z.B. /tmp unter Linux oder
~\AppData\Local\Temp unter Windows. Am Ende der Skript-Laufzeit wird die
Datei automatisch wieder gelöscht. Zur dauerhaften Speicherung muss das
Skript daher zur Laufzeit die Datei mit der Funktion move_uploaded_file() an
einen anderen Ort verschieben:
move_uploaded_file(Quellpfad, Zielpfad);

186
Datei-Uploads: $_FILES 8.4

Folgender Code speichert die hochgeladene Datei aus dem Dateifeld picture
unter dem Original-Dateinamen in das Verzeichnis uploads/ relativ zum
Skript-Verzeichnis:
if (isset($_FILES['picture'])) {
move_uploaded_file(
$_FILES['picture']['tmp_name'],
__DIR__.'/uploads/'.$_FILES['picture']['name']
);
}

Listing 8.15: Hochgeladene Datei dauerhaft speichern (file-upload.php)

Das Verzeichnis uploads/ muss existieren und der Webserver benötigt darin
Schreibrechte (siehe Abschnitt 8.5).
Der Original-Dateiname stammt vom Client. Ein späterer Upload einer ande-
ren Datei mit gleichem Namen würde eine bestehende Datei überschreiben.
Die serverseitige Vergabe eines eindeutigen Namens vermeidet eine mögliche
Namenskollision:
$extension = pathinfo( 1
$_FILES['picture']['name'],
PATHINFO_EXTENSION // Dateiendung extrahieren
);
$filename = sprintf('%s.%s', uniqid(), $extension); 2
move_uploaded_file(
$_FILES['picture']['tmp_name'],
__DIR__.'/uploads/'.$filename // z.b. 60af75f8dc3c8.png
);

ƒ 1 pathinfo() zerlegt Dateipfade bzw. -namen in die Bestandteile. Die


Option PATHINFO_EXTENSION extrahiert nur die Dateiendung, z.B. png aus
­ ircle.png.
c
ƒ 2 uniqid() generiert einen zufälligen, zeitbasierten String, z.B. 60af75f-
8dc3c8.

8.4.3 Upload-Fehler
Ein Dateiupload kann fehlschlagen, z.B. wenn die maximal zugelassene Da-
teigröße (Abschnitt 8.4.4) überschritten ist. Upload-Fehler meldet PHP als
ganzzahligen Fehlercode ungleich Null im Feld error des Datei-Arrays aus
$_FILES. Für das Upload-Feld mit dem Namen picture findet sich der Feh-

187
Kapitel 8 Mit Dateien arbeiten

lercode folglich in $_FILES['picture']['error']. Vor der Verarbeitung einer


hochgeladenen Datei sollte der Code geprüft werden. Jeder mögliche Fehler-
code kann zur besseren Lesbarkeit als vordefinierte Konstante ausgedrückt
werden (siehe www.php.net/features.file-upload.errors).
$error = $_FILES['picture']['error'];
if ($error === UPLOAD_ERR_OK) { // 0 = Kein Fehler
move_uploaded_file(...); // alles OK, Datei speichern usw.
} elseif ($error === UPLOAD_ERR_NO_FILE) {
echo 'Keine Datei ausgewählt';
} elseif ($error === UPLOAD_ERR_INI_SIZE) {
exit('Fehler beim Upload: Datei ist zu groß.');
} else { // ggf. weitere Fehler unterscheiden
exit('Fehler beim Upload: Code ' . $error);
}

Alle Fehlercodes beschreibt die PHP-Dokumentation unter www.php.net/


features.file-upload.errors.

8.4.4 php.ini-Einstellungen
Einige php.ini-Einstellungen beeinflussen Datei-Uploads, u.a.:

php.ini-Einstellung Standard Beschreibung

file_uploads On Uploads erlauben (On/Off)


upload_max_filesize 2M (= 2MB) Maximale Dateigröße je Upload
post_max_size 8M (= 8MB) Maximale Größe von POST-Daten

Die Einstellung post_max_size bestimmt die maximal erlaubte Größe aller


Formulardaten (Textfelder und Dateiuploads). Die Einstellung muss daher
alle Dateiuploads eines Formulars plus einen Puffer für die Inhalte der übri-
gen Textfelder berücksichtigen.

Aufgabe 3

Ein Formular soll den Upload von bis zu drei Fotos zu maximal je 15MB
plus ein Textfeld für eine Beschreibung erlauben. Welche Einstellungen für
upload_max_filesize und post_max_size erscheinen sinnvoll?

188
Datei-Uploads: $_FILES 8.4

8.4.5 Datei-Art prüfen (MIME-Type)


Wie allen Benutzereingaben darf auch Dateiuploads nicht blind vertraut wer-
den. Insbesondere der Dateityp muss den Erwartungen des Skripts entspre-
chen, um folgende Szenarien auf einer Website zu vermeiden:
ƒ Missbrauch als Filesharing-Plattform.
ƒ Upload gefährlicher Dateien, z.B. Skripte.
ƒ Unerwarteter Programmablauf, z.B. bei Upload eines Videos in eine Foto-
galerie.
Dateitypen werden mit dem sogenannten MIME-Type (siehe auch Abschnitt
6.4.4) klassifiziert. Die Tabelle gibt einige Beispiele:

MIME-Type Datei-Erweiterung Beschreibung


image/jpeg .jpg JPEG-Bilddatei
image/png .png PNG-Bilddatei
video/mp4 .mp4 MP4-Videodatei
application/zip .zip ZIP-Archivdatei
application/pdf .pdf PDF-Datei
Tab. 8.3: Beispiele für MIME-Types

Eine Liste gängiger MIME-Types finden Sie z.B. in den Mozilla Web Docs1.
Das Datei-Array in $_FILES enthält eine Angabe zum MIME-Type für jedes
Upload-Feld:
$_FILES['picture']['type']; // z.B. image/jpeg

Dieser Wert wird jedoch vom Browser übermittelt und ist daher wie alle Cli-
ent-Daten nicht fälschungssicher. Serverseitig kann der MIME-Type mit der
Funktion mime_content_type() aus dem Dateiinhalt selbst bestimmt werden:
$mimeType = mime_content_type($_FILES['picture']['tmp_name']);
if ($mimeType !== 'image/jpeg') {
exit('Nur JPEG Dateien erlaubt.');
}
move_uploaded_file(...);

1
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/
MIME_types/Common_types

189
Kapitel 8 Mit Dateien arbeiten

Die Funktion mime_content_type() ist Teil der PHP-Erweiterung fileinfo.


Aktivieren Sie die Erweiterung ggf. wie in Abschnitt 4.2.4 beschrieben.

Aufgabe 4

Wie lautet der MIME-Type einer MP3-Datei? Nutzen Sie die Funktion
mime_content_type() mit dem Pfad zu einer mp3-Datei auf Ihrem Compu-
ter, um es herauszufinden.

8.5 Datei-Zugriffsrechte
Bei der Arbeit mit Dateien und Verzeichnissen kommt jeder Programmie-
rer früher oder später mit dem Thema Datei-Zugriffsrechte (engl. permissions)
in Berührung. Etwa wenn das Schreiben einer Datei wegen fehlender Rechte
misslingt:
Warning: file_put_contents(data.txt): Failed to open stream: Per-
mission denied

8.5.1 Warum Zugriffsrechte?


Moderne Betriebssysteme sind Mehrbenutzersysteme: Mehrere Benutzer kön-
nen auf dem gleichen Computer in isolierten Arbeitsumgebungen arbeiten.
Jeder Benutzer kann in seiner Umgebung Dateien anlegen, verändern oder
löschen. Benutzer dürfen jedoch keinen unkontrollierten Zugriff auf Dateien
anderer Benutzer haben. Auch Programme wie ein Web- oder Datenbank-
server werden unter einem bestimmten Benutzer ausgeführt. Zugriffsrechte
regeln, ob ein Benutzer oder Programm auf eine Datei zugreifen darf oder
nicht. Das Betriebssystem schützt auch eigene Systemdateien mit Zugriffs-
rechten vor versehentlicher Änderung.
Windows und unixartige Betriebssysteme (u.a. Linux und macOS) haben un-
terschiedliche Ansätze für Zugriffsrechte. Produktive PHP-Anwendungen im
Internet laufen überwiegend auf unixartigen Betriebssystemen. Daher kom-
men auch PHP-Programmierer, die auf Windows entwickeln, mit den unixar-
tigen Zugriffsrechten in Kontakt.

190
Datei-Zugriffsrechte 8.5

8.5.2 Klassische Unix-Zugriffsrechte


Jede Datei bzw. jedes Verzeichnis hat genau einen Inhaber:
ƒ Sind Sie an Ihrem Computer angemeldet und legen eine Datei an, so ist Ihr
Benutzer-Account Inhaber der Datei.
ƒ Laden Sie eine Datei per FTP in einen Webspace, so ist der FTP-Benutzer
auf dem Webspace Inhaber der Datei.
ƒ Lädt ein Website-Besucher über eine PHP-Skript eine Datei hoch, so ist der
Benutzer, unter dem die Webserver-Software läuft, Inhaber der Datei.
Neben dem Inhaber ist jede Datei genau einer Benutzer-Gruppe zugeordnet.
Eine Gruppe fasst mehrere Benutzer mit gleichen Eigenschaften oder Interes-
sen zusammen, z.B. die Gruppe aller FTP-Benutzer. Benutzer können gleich-
zeitig verschiedenen Gruppen angehören.
Zugriffsrechte werden separat für den Inhaber, die Gruppe und »alle anderen«
vergeben:

Bereich Abkürzung Bedeutung


Benutzer (User) u Inhaber der Datei
Gruppe (Group) g Gruppe der Datei
Andere (Other) o Alle anderen
Tab. 8.4: Benutzerklassen unixartiger Betriebssysteme

Für jeden der drei Bereiche (Benutzer, Gruppe, Andere) gibt es drei Zugriffs-
rechte zum Lesen, Schreiben und Ausführen. Die Rechte haben für eine Datei
oder ein Verzeichnis unterschiedliche Bedeutungen.

Zugriffsrecht Abk. Datei Verzeichnis


Lesen (Read) r Lesen Dateinamen auflisten
Schreiben (Write) w Verändern Dateien verändern
Ausführen (Execute) x Ausführen Verzeichnis betreten
Tab. 8.5: Grundlegende Rechte unixartiger Betriebssysteme

8.5.3 Zugriffrechte ändern


Bei der lokalen Entwicklung unter Windows oder macOS sollten Ihnen kaum
Berechtigungsprobleme begegnen, alle Programme laufen unter demselben
Benutzer. In einem Webspace nutzen Sie zur Prüfung und Änderung am ein-

191
Kapitel 8 Mit Dateien arbeiten

fachsten ein FTP-Programm wie FileZilla (www.filezilla-project.org). Ein


Rechtsklick auf eine Datei oder ein Verzeichnis öffnet einen grafischen Dialog
zur Einstellung der Dateiberechtigungen.

Abb. 8.3: Dateiberechtigungen im FTP-Programm ändern

Das häufigste Problem beim Webhoster tritt im Rahmen von Dateiuploads


über eine Webseite auf. Der Webserver-Benutzer, unter dem das PHP-Skript
ausgeführt wird, versucht eine hochgeladene Datei in ein Verzeichnis zu ver-
schieben, das mit einem FTP-Benutzer angelegt wurde. Umgekehrt kann das
Löschen einer Datei durch einen FTP-Benutzer fehlschlagen, wenn sie durch
ein PHP-Skript erzeugt wurde und daher dem Webserver-Benutzer gehört.
Die Lösung des Problems hängt von den Einstellungen des Webhosters ab.
Einige Webhoster sorgen durch spezielle Konfiguration für gegenseitigen
Schreibzugriff zwischen Dateien des FTP- und Webserver-Benutzers und Sie
bemerken keine Probleme. Recherchieren Sie ggf. die empfohlenen Berechti-
gungs-Einstellungen in der Dokumentation des Webhosters.

8.6 Übungen
Übung 1
Auch die include-Anweisung kann Dateien einbinden. Sie unterscheidet sich
in ihrer Reaktion von require, falls eine eingebundene Datei nicht existiert.
Probieren Sie mit folgendem Skript beide Anweisungen aus und finden Sie
den Unterschied.

192
Übungen 8.6

require ODER include '/Datei/die/nicht/existiert';


echo 'Hallo PHP';

Übung 2
Ein beliebtes, einfaches Dateiformat zur Datenspeicherung ist das CSV-For-
mat (»comma-separated values«). Folgende CSV-Datei users.csv speichert
Personendaten mit Name, Alter und Hobbies:
Name,Alter,Hobbies
Max Becker,29,"Schwimmen, Laufen"
Lisa Fischer,33,"Lesen, Wandern"
Moritz Bender,41,"Computer, Games, Kochen"

Listing 8.16: users.csv mit kommaseparierten Daten

Die erste Zeile enthält die Spalten-Überschriften. Jede weitere Zeile entspricht
einem Datensatz. Jeder Datensatz besteht aus durch Kommas getrennten Fel-
dern für Name, Alter und Hobbies.
Schreiben Sie ein Skript, das die Datei einliest und die Daten in einer HTML-
Tabelle ausgibt:

Tipps:
ƒ Lesen Sie die Zeilen der CSV-Datei mit file() in ein Array.
ƒ Zerlegen Sie jede Zeile mit str_getcsv() in die Felder, PHP übernimmt die
Interpretation des CSV-Formats je Zeile.

Übung 3
Registrieren Sie einen Account auf OpenWeather (www.openweathermap.org).
Erstellen Sie ein Formular, das die Abfrage der Temperatur und der gefühlten
Temperatur für eine eingegebene Stadt ermöglicht.

193
Kapitel 8 Mit Dateien arbeiten

Übung 4
Erstellen Sie ein Formular zum Upload von bis zu drei JPEG-Fotos. Speichern
Sie die Bilder serverseitig ab. Prüfen Sie Uploads auf Fehlercodes und den
korrekten MIME-Type.

194
Cookies und Sessions
Aufgrund der Zustandslosigkeit des HTTP-Protokolls (Abschnitt 6.4.5) haben
Webseiten kein eigenes Gedächtnis. Jeder Seitenaufruf ist isoliert von vorigen
Aufrufen. Ein Besucher, der sich von Seite zu Seite bewegt, wird immer als
neuer Besucher wahrgenommen.
Cookies und Sessions ermöglichen die Wiedererkennung von Besuchern über
mehrere Seiten hinweg und damit die Personalisierung von Webseiten.
ƒ Mit einem Cookie legt eine Webseite ein kleines Datenpaket clientseitig im
Browser des Besuchers ab. Der Browser schickt das Datenpaket bei späte-
ren Besuchen an den Webserver zurück.
ƒ Mit einer Session (engl. für Sitzung) speichert eine Webseite Daten server-
seitig unter einem eindeutigen Schlüssel. Der Schlüssel wird als Cookie im
Browser des Besuchers hinterlegt und bei erneuten Besuchen an den Web-
server zurückgeschickt.
Cookies und Sessions bilden die Grundlage für Funktionalitäten wie Logins,
Warenkörbe, die Verwaltung persönlicher Daten und vieles mehr.

9.1 Cookies
Ein Cookie (engl. für »Keks«) ist ein kleines Datenpaket in Textform, das cli-
entseitig im Browser eines Website-Besuchers gespeichert wird. Im Kern han-
delt es sich um ein Schlüssel-Wert-Paar, z.B. language=de zur Speicherung der
bevorzugten Sprache eines Besuchers. Der Name des Cookies lautet in diesem
Fall language, sein Wert de.

9.1.1 Wie entsteht ein Cookie?


Zur Erinnerung: Auf eine Anfrage nach einer Webseite sendet der Webser-
ver eine HTTP-Antwort aus reinem Text (siehe Abschnitt 6.4.4), hier verkürzt
dargestellt:

195
Kapitel 9 Cookies und Sessions

HTTP/1.1 200 OK
Date: Wed, 27 May 2022 06:38:19 GMT
Content-Type: text/html

<html>...</html>

Über der Leerzeile befinden sich die für gewöhnliche Besucher der Websei-
te unsichtbaren HTTP-Header mit Metainformationen über die Antwort,
die das Verhalten des Browsers beeinflussen können. Eigene Header können
mit der Funktion header() erzeugt werden. Unter der Leerzeile folgt der im
Browserfenster sichtbare Inhalt, d.h. alle Ausgaben eines PHP-Skripts (echo,
var_dump() etc.) bzw. alles, was außerhalb von PHP-Tags steht.
Zurück zur Entstehung eines Cookies: In der Antwort kann der Webserver
den Browser mit dem HTTP-Header Set-Cookie zur Speicherung eines Coo-
kies auffordern:
HTTP/1.1 200 OK
Date: Wed, 27 May 2022 06:38:19 GMT
Content-Type: text/html
Set-Cookie: language=de

<html>...</html>

Die Erzeugung eines Cookies wird also durch einen HTTP-Header in der Ant-
wort ausgelöst und kann daher mit der Funktion header() veranlasst werden:
header('Set-Cookie: language=de');
echo 'Cookie "language" gesetzt!';

Listing 9.1: set-cookie-using-header-function.php

Abb. 9.1: Webserver setzt Cookie mit Set-Cookie-Header

196
Cookies 9.1

Nach Abruf des Skripts im Browser ist das Cookie in der Browser-Konsole
unter Web-Speicher|Cookies und der Domain http://localhost:8000
sichtbar:

Abb. 9.2: Gesetztes Cookie in der Browser-Konsole

PHP vereinfacht das Setzen eines Cookies mit der »Komfortfunktion«


setcookie(Name, Wert). Folgendes Skript hat die gleiche Wirkung wie zuvor
der explizite Aufruf der header()-Funktion:
setcookie('language', 'de');

Listing 9.2: set-cookie-using-setcookie-function.php

Sobald in einem Skript die erste Ausgabe erfolgt (z.B. per echo), beginnt
der Webserver eventuell bereits mit dem Senden der Antwort an den
Browser. Ab diesem Zeitpunkt können der Antwort keine Header mehr
hinzugefügt werden. Das Setzen eines Cookies bzw. jeglicher Header soll-
te daher im Code immer vor der ersten Ausgabe stattfinden.

Aufgabe 1

Erzeugen Sie einige Cookies mit verschiedenen Namen.

9.1.2 Cookie-Daten lesen: $_COOKIE


Hat eine Webseite bei einer früheren Anfrage ein Cookie gesetzt, schickt der
Browser das Cookie fortan in jeder folgenden Anfrage an die gleiche Domain
im HTTP-Anfrage-Header Cookie wieder an den Webserver zurück:
Cookie: language=de

PHP stellt alle empfangenen Cookies im superglobalen Array $_COOKIE zur


Verfügung. Die Funktion print_r() gibt eine Übersicht über alle empfange-
nen Cookies:

197
Kapitel 9 Cookies und Sessions

print_r($_COOKIE); // => Array ([language] => de)

Mit dem passenden Cookie-Namen als Array-Schlüssel erfolgt der Zugriff auf
den Wert eines bestimmten Cookies:
echo $_COOKIE['language']; // => de

Abb. 9.3: Browser schickt Cookie mit Cookie-Header

Beachten Sie, dass ein neu gesetztes Cookie erst ab der nächsten Anfrage des
Clients im $_COOKIE-Array sichtbar ist:
setcookie('language', 'de'); 1
if (isset($_COOKIE['language'])) { 2
echo $_COOKIE['language'];
} else {
echo 'Noch kein Cookie gesetzt';
}

Listing 9.3: Cookie auslesen in check-cookie.php

ƒ 1 Setzen des Cookies language.


ƒ 2 Beim ersten Skriptaufruf ist das Cookie noch kein Teil der Anfrage. Der
Zugriff ist erst bei Folgeaufrufen möglich, wenn der Browser beginnt, das
Cookie zurückzusenden.

Jeder Browser pflegt seine eigenen Cookies, d.h. Cookies sind auf den
Browser beschränkt, in dem sie gesetzt wurden.

Aufgabe 2

Lässt sich der Wert eines Cookies später überschreiben?

198
Cookies 9.1

9.1.3 Beispiel: Sprachwechsler


Besucher einer Website sollen zwischen Deutsch und Englisch als Anzeige-
sprache wechseln können. Die Auswahl soll während des Besuchs verschiede-
ner Seiten erhalten bleiben.

Die fertige Webseite

Abb. 9.4: Website mit Sprachwechsler

Die Website hat eine Navigation mit den zwei Unterseiten Startseite/Home-
page und Gästebuch/Guestbook sowie Links zur Einstellung der Anzeige-
sprache Deutsch oder Englisch. Nach Auswahl der Sprache kann beliebig zwi-
schen den Unterseiten gewechselt werden, die Auswahl bleibt erhalten. Die
Sprachauswahl ist als Cookie auf dem Rechner des Besuchers hinterlegt.

Hinweis

Zum Test des Sprachwechslers starten Sie den Webserver mit dem Verzeich-
nis language-switcher/ aus den Code-Downloads als Document-Root.

Quelltext
Die beiden Unterseiten index.php und guestbook.php binden beide zunächst
die Datei _header.php ein. Abhängig von der Variablen $lang ist die Ausgabe-
sprache Englisch oder Deutsch.
<?php require __DIR__ . '/_header.php'; ?>
<h1><?= $lang === 'en' ? 'Welcome' : 'Willkommen'; ?></h1>

Listing 9.4: Startseite/Homepage index.php

<?php require __DIR__ . '/_header.php'; ?>


<h1><?= $lang === 'en' ? 'Guestbook' : 'Gästebuch'; ?></h1>

Listing 9.5: Gästebuch/Guestbook guestbook.php

Das Skript _header.php setzt die Variable $lang in Abhängigkeit des Cookies
namens lang:

199
Kapitel 9 Cookies und Sessions

<?php
$lang = $_COOKIE['lang'] ?? 'de'; 1
if (isset($_GET['lang'])) { 2
setcookie('lang', $_GET['lang']); 3
header('Location: ' . $_SERVER['PHP_SELF']); exit(); 4
}
?>
<p><a href="?lang=de">DE</a>|<a href="?lang=en">EN</a></p> 5
<ul> 6
<li><a href="index.php">
<?= $lang === 'en' ? 'Homepage' : 'Startseite' ?>
</a></li>
<li><a href="guestbook.php">
<?= $lang === 'en' ? 'Guestbook' : 'Gästebuch' ?>
</a></li>
</ul>

Listing 9.6: Mehrsprachige Navigation in _header.php

Erklärungen
ƒ 1 Mit Hilfe des Null Coalescing Operators (?? – siehe unten) wird die Vari-
able $lang mit dem Wert aus dem Cookie lang initialisiert, falls es existiert,
ansonsten mit de.
ƒ 2 Falls ein URL-Parameter lang empfangen wurde …
ƒ 3 … wird mit dessen Wert das Cookie lang gesetzt …
ƒ 4 … und zum selben Skript weitergleitet (= neu laden der Seite). Bei der
erneuten Ausführung wird der Sprachwechsel durch 1 aktiv.
ƒ 5 Menü zum Sprachwechsel: Jeder Link setzt den passenden URL-Parame-
ter, der den Sprachwechsel auslöst.
ƒ 6 Website-Menü mit Links zu zwei Unterseiten.
$_SERVER['PHP_SELF'] enthält den aktuellen Skriptnamen und kann verwen-
det werden, um dynamisch Links auf dasselbe Skript zu erzeugen, ohne den
Dateinamen explizit zu nennen.
Der Null coalescing operator (??) ist eine Kurzform für eine häufige, bestimmte
Verwendung des ternären Operators (siehe auch Abschnitt 3.1.5):
$lang = $_COOKIE['lang'] ?? 'de';

200
Cookies 9.1

Der Operator liest sich in Gedanken: »Weise $lang den Wert von $_
COOKIE['lang'] zu, wenn das Element existiert, ansonsten den String de«. Die
gleichwertige Langform lautet:
$lang = isset($_COOKIE['lang']) ? $_COOKIE['lang'] : 'de';

9.1.4 Cookies mit Ablaufdatum


Standardmäßig speichert der Browser einen gesetzten Cookie bis zum Been-
den des Browsers. Das Cookie zur Speicherung der Anzeigesprache aus dem
vorigen Abschnitt wird bis zum Schließen des Browsers bei jeder Anfrage
gesendet und anschließend gelöscht. Bei einem späteren Besuch müsste der
Benutzer die Sprachauswahl erneut treffen.
Durch die explizite Angabe eines Ablaufdatums (engl. expiration) speichert
und sendet der Browser das Cookie bei Folgeaufrufen auf jeden Fall bis zu
diesem Ablaufdatum, auch wenn der Browser zwischendurch geschlossen
wird. Das gewünschte Ablaufdatum wird der Funktion setcookie() als drittes
Argument in Form eines Unix-Zeitstempels übergeben, z.B. ein Jahr in der
Zukunft:
setcookie('lang', 'de', strtotime('+1 year'));

Listing 9.7: set-cookie-with-expiration.php

Abb. 9.5: Cookie mit explizitem Ablaufdatum

Standardmäßig haben Cookies implizit den Unix-Zeitstempel 0 als Ablaufda-


tum. Dieses spezielle Ablaufdatum führt zum eingangs beschriebenen Verhal-
ten: Das Cookie wird mit Schließen des Browsers gelöscht.
setcookie('lang', 'de'); // äquivalent zu:
setcookie('lang', 'de', 0);

Listing 9.8: Cookie mit explizitem Ablaufdatum 0

Der »Privat«- bzw. »Inkognito«-Modus eines Browsers bietet eine


isolierte Umgebung. In einer solchen Umgebung löscht der Browser
beim Schließen alle clientseitig gespeicherten Daten (d.h. auch Coo-
kies) unabhängig von einer Ablaufzeit. Im Firefox wählen Sie dazu
Datei|Neues Privates Fenster.
201
Kapitel 9 Cookies und Sessions

9.1.5 Cookies löschen


Bisher gab es zwei Gelegenheiten zur Löschung eines Cookies:
ƒ Sitzungscookie: nach Schließen des Browsers.
ƒ Ablaufdatum: nach Überschreiten des Ablaufdatums.
Durch erneutes Setzen eines Cookies mit einem Ablaufdatum in der Vergan-
genheit kann ein Cookie auch explizit gelöscht werden:
setcookie('lang', '', time()-3600); // z.B. vor 1h

Ab der nächsten Anfrage sendet der Browser das Cookie nicht mehr.

9.1.6 Cookies im Browser verwalten


Cookies können in der Browser-Konsole verändert, gelöscht und neu angelegt
werden. Sie sind daher frei manipulierbar und mit derselben Vorsicht zu be-
handeln wie andere Benutzereingaben.

Abb. 9.6: Cookies in der Browser-Konsole bearbeiten

9.2 Sessions: Benutzersitzungen


Cookies überwinden die Statuslosigkeit des HTTP-Protokolls und ermög-
lichen es, Daten eines Besuchers über mehrere Seiten hinweg clientseitig zu
speichern. Durch die Speicherung im Browser entziehen sich die Daten je-
doch der Kontrolle des Servers und können manipuliert werden.
PHP-Sessions (engl. für »Sitzungen«) bieten die Möglichkeit, Daten zu einem
Besucher über Seitenaufrufe hinweg serverseitig zu speichern und daher auch
sensible Daten zu speichern. Der Server kann sich in einer Session beispiels-
weise »merken«, welcher Besucher sich für den Zugriff auf geschützte Daten
zuvor durch einen Login authentifiziert hat.

9.2.1 Eine Session starten


Die Funktion session_start() startet eine Session:
session_start();

202
Sessions: Benutzersitzungen 9.2

Dabei geschieht Folgendes:


ƒ PHP erzeugt eine Session-ID – einen zufälligen, eindeutigen String zur
Identifizierung der Session bzw. des Besuchers, z.B. j7u7htkfv01c3nek9pip.
ƒ PHP setzt einen Cookie mit dem Namen PHPSESSID und der Session-ID als
Wert – den sogenannten Session-Cookie.
ƒ Zur Speicherung der Session-Daten legt PHP auf dem Server eine Datei im
Verzeichnis für temporäre Dateien an. Ihr Name setzt sich aus dem Präfix
sess_ und der Session-ID zusammen, z.B. sess_j7u7htkfv01c3nek9pip.
Die Funktion session_id() liest die ID der Session aus:
session_start();
echo session_id(); // => j7u7htkfv01c3nek9pip

Listing 9.9: Session-ID auslesen in session-start.php

Nach dem Start einer Session können Daten in das superglobale $_SESSION-
Array geschrieben werden:
$_SESSION['lang'] = 'de'; // Spracheinstellung
$_SESSION['cart'] = [ // "Einkaufswagen"
'apples' => 3,
'bananas' => 2,
];

Mit Ende der Skriptausführung speichert PHP die Daten des $_SESSION-Ar-
rays automatisch in die zugehörige Session-Datei auf dem Server. Von dort
liest sie PHP bei einer späteren Anfrage wieder ein.

9.2.2 Eine Session wieder aufnehmen


Im Session-Cookie speichert der Browser nur das Merkmal zu Identifizierung
eines Besuchers – die Session-ID. Die Besucherdaten selbst liegen in der zuge-
hörigen Datei auf dem Server – identifiziert durch die Session-ID im Dateina-
men. Der Browser sendet das Session-Cookie nach dem Start der Session bei
jedem Folgeaufruf. PHP kann die serverseitig gespeicherten Session-Daten
über Seitenaufrufe hinweg demselben Besucher zuordnen.
Die Wiederaufnahme einer Session erfolgt automatisch durch session_
start(). Erkennt PHP einen vorhandenen Session-Cookie in der Anfrage,
wird keine neue Session gestartet, sondern das $_SESSION-Array aus den Da-
ten der serverseitigen Datei wiederhergestellt.

203
Kapitel 9 Cookies und Sessions

session_start();
print_r($_SESSION);

Die Ausgabe:
Array(
[lang] => de
[cart] => Array (
[apples] => 3
[bananas] => 2
)
)

Listing 9.10: Sessiondaten anzeigen in session-read.php

Aufgabe 3

Ungenutzte Session-Dateien räumt PHP automatisch auf. Finden Sie unter


www.php.net/session.configuration heraus, nach welcher Zeit PHP eine
Session als »Müll« (engl. garbage) betrachtet.

9.2.3 Beispiel: Login


Besucher sollen sich auf einer Website mit Benutzernamen und Passwort an-
melden. Angemeldete Besucher sehen auf jeder Seite ihren Benutzernamen
und eine Abmelde-Möglichkeit.

So sieht die fertige Website aus

Abb. 9.7: Ausgeloggter und eingeloggter Benutzer

Quelltext
Die Datei users.php speichert die Benutzer mit individuellen Passwörtern in
einem Array:

204
Sessions: Benutzersitzungen 9.2

return [
'alisa84' => 's3cr3t',
'limo02' => 'pa$$w0rd', // ...
];

Listing 9.11: Benutzer-Passwort-Liste als Array in users.php

Durch die return-Anweisung kann das Array bei Einbindung der Datei einer
Variablen zugewiesen werden:
$users = require __DIR__.'/users.php';
print_r($users);
// Array([alisa84] => s3cr3t [limo02] => pa$$w0rd)

Die Website besteht aus zwei Unterseiten. Beim Wechsel zwischen den Seiten
muss der Anmeldestatus aufrechterhalten werden:
<?php require __DIR__.'/_header.php' ?>
<h1>Willkommen</h1>

Listing 9.12: Startseite index.php

<?php require __DIR__.'/_header.php' ?>


<h1>Gästebuch</h1>

Listing 9.13: Gästebuch-Seite guestbook.php

Die Unterseiten teilen sich den Kopfbereich aus der Datei _header.php. Sie
enthält den An- und Abmeldebereich sowie die Navigation der Website:
<?php
session_start(); 1
$error = null; 2
if (isset($_POST['username'], $_POST['password'])) { 3
$users = require __DIR__.'/users.php'; 4
$username = $_POST['username'];
if (isset($users[$username]) &&
$users[$username] === $_POST['password']) { 5
$_SESSION['username'] = $_POST['username']; 6
header('Location: ' . $_SERVER['PHP_SELF']); exit();
}
$error = 'Ungültige Anmeldedaten'; 7

205
Kapitel 9 Cookies und Sessions

} elseif (isset($_POST['logout'])) { 8
unset($_SESSION['username']); 9
header('Location: ' . $_SERVER['PHP_SELF']); exit();
}
?>
<?php if ($error !== null): ?> 10
<p style="color:red; >"><?= $error ?></p>
<?php endif; ?>
<?php if (isset($_SESSION['username'])): ?> 11
<p>Hallo <strong><?= $_SESSION['username'] ?></strong>,
Sie sind eingeloggt.</p>
<form method="post">
<button type="submit" name="logout">Ausloggen</button>
</form>
<?php else: ?> 12
<form method="post">
<input name="username" placeholder="Benutzername"/><br/>
<input name="password" placeholder="Passwort"/><br/>
<button type="submit">Einloggen</button>
</form>
<?php endif; ?>
<ul> 13
<li><a href="index.php">Startseite</a></li>
<li><a href="guestbook.php">Gästebuch</a></li>
</ul>

Listing 9.14: Login-Logik in _header.php

Erklärungen
ƒ 1 Start bzw. Wiederaufnahme der Session.
ƒ 2 $error zur Speicherung möglicher Fehlermeldungen.
ƒ 3 Wurde das Anmeldeformular abgeschickt?
ƒ 4 Array mit Benutzern und deren Passwörter in die Variable $users laden.
ƒ 5 Wenn eingegebener Benutzername in $users als Schlüssel existiert und
das Passwort übereinstimmt, ist die Anmeldung erfolgreich.
ƒ 6 Bei erfolgreicher Anmeldung wird der Benutzernamen in $_SES­
SION['username'] gespeichert und die Seite durch eine Weiterleitung auf
sich selbst neu geladen. Der Benutzername in der Session dient als sicheres,

206
Sessions: Benutzersitzungen 9.2

serverseitig gespeichertes, seitenübergreifendes Merkmal, ob der Besucher


angemeldet ist. Die Weiterleitung führt zu einem Neuaufbau der Seite ent-
sprechend dem geänderten Anmeldestatus.
ƒ 7 Bei nicht erfolgreicher Anmeldung nimmt $error eine Fehlermeldung
auf.
ƒ 8 Wurde das Formular zum Abmelden abgeschickt? Auch button-Schalt-
flächen werden im $_POST-Array übermittelt.
ƒ 9 Die Abmeldung löscht das Anmeldemerkmal aus der Session und lädt
die Seite neu, damit sie entsprechend dem neuen Anmeldestatus neu auf-
gebaut werden kann.
ƒ 10 Falls ein Eingabefehler in der Variable $error vermerkt wurde, wird die
Fehlermeldung hier angezeigt.
ƒ 11 Falls der Benutzername in $_SESSION['username'] vorhanden ist, ist
der Besucher angemeldet. Eine Information über den Anmeldestatus und
eine Schaltfläche zum Abmelden in einem eigenen Formular sind sichtbar.
ƒ 12 Zeige Login-Formular, falls der Besucher nicht angemeldet ist.
ƒ 13 Website-Navigation. Bei einem Wechsel zwischen den Seiten bleibt der
aktuelle Anmeldestatus immer erhalten.
$_SESSION['username'] ist nicht clientseitig manipulierbar, es kann als siche-
res Merkmal für den Anmeldestatus dienen.

Hinweis

Zum Test des Logins starten Sie den Webserver mit dem Verzeichnis
login/ aus den Code-Downloads als Document-Root.

9.2.4 Beispiel: »Flash«-Nachrichten


Im Login-Beispiel des vorigen Abschnitts erkennt ein Benutzer den Erfolg ei-
nes Logins oder Logouts nur an Veränderungen des Inhalts: Nach dem Login
steht ihm eine Logout-Schaltfläche zur Verfügung – nach dem Logout wieder
das Login-Formular. Es wäre hilfreich, eine deutliche, einmalige Rückmel-
dung über den Erfolg einer Login- bzw. Logout-Aktion zu geben, etwa mit
folgenden Hinweisen:
ƒ »Sie haben sich angemeldet, willkommen!« bzw.
ƒ »Sie haben sich abgemeldet, bis bald!«

207
Kapitel 9 Cookies und Sessions

So sieht die fertige Website aus

Abb. 9.8: »Flash«-Nachricht nach Logout

Die Technik zur einmaligen Anzeige einer Nachricht wird mit einem
Blitz (engl. flash) verglichen, da sie nur einmalig »aufblitzt« und mit
dem nächsten Seitenwechsel wieder verschwindet.
Wie kann ein Skript jedoch von einer Aktion aus einem vorherigen Skript wis-
sen und eine passende Meldung dazu anzeigen?

Quelltext
Die Lösung erweitert den Quellcode des Login-Beispiels aus Abschnitt 9.2.3 an
wenigen Stellen. Zur Umsetzung einer Flash-Nachricht wird die gewünschte
Nachricht nur kurzzeitig zwischen zwei Anfragen in der Session gespeichert:
ƒ Nach einer erfolgreichen Aktion wie dem Login bzw. Logout und vor der
Weiterleitung auf eine neue Seite wird eine passende Nachricht unter dem
Schlüssel flash_message in das $_SESSION-Array geschrieben.
ƒ Im allgemeinen Kopfbereich der Webseite wird bei jeder Anfrage geprüft,
ob eine Flash-Nachricht in der Session vorliegt. Falls ja, wird sie zur An-
zeige gebracht und anschließend gelöscht, um eine einmalige Anzeige der
Nachricht sicherzustellen.
Während eines erfolgreichen Logins bzw. Logouts schreibt das Skript kurz
vor der Weiterleitung zusätzlich die passende Flash-Nachricht unter $_
SESSION['flash_message'] in die Session:

...
if (isset($users[$username]) &&
$users[$username] === $_POST['password']) {
$_SESSION['username'] = $_POST['username'];

208
Sessions: Benutzersitzungen 9.2

$_SESSION['flash_message'] = 'Sie haben sich angemeldet, Willkom-


men!';
header('Location: ' . $_SERVER['PHP_SELF']); // Neu laden
exit();
} else if (isset($_POST['logout'])) {
unset($_SESSION['username']);
$_SESSION['flash_message'] = 'Sie haben sich abgemeldet, bis
bald!';
header('Location: ' . $_SERVER['PHP_SELF']); // Neu laden
exit();
}
...

Listing 9.15: Speicherung der Flash-Nachrichten in _header.php (Ausschnitt)

Nach dem neu Laden der Seite durch die Weiterleitung per Location-Header
baut sich die Seite neu auf, um sich dem neuen Anmeldestatus anpassen zu
können. Der Code im Kopfbereich in der Datei _header.php wird erweitert,
um eine Flash-Nachricht anzuzeigen, falls das $_SESSION-Array unter dem
Schlüssel flash_message eine bereithält. Unmittelbar nach der Anzeige wird
die Flash-Nachricht aus der Session entfernt, um eine nochmalige Anzeige
beim nächsten Laden der Seite zu vermeiden.
...
<?php if (isset($_SESSION['flash_message'])): ?>
<p style="color:green; font-weight: bold;>">
<?= htmlspecialchars($_SESSION['flash_message']) ?>
</p>
<?php unset($_SESSION['flash_message']); ?>
<?php endif; ?>
...

Die Anzeige der Flash-Nachricht ist unabhängig von ihrem Ursprung und
kann daher ohne weitere Anpassung für Rückmeldungen weiterer Anwen-
dungsfälle genutzt werden, sofern diese eine Flash-Nachricht in die Session
schreiben.

209
Kapitel 9 Cookies und Sessions

Hinweis

Zum Test des Logins mit Flash-Nachrichten starten Sie den Webserver mit
dem Verzeichnis login-with-flash-messages/ aus den Code-Downloads als
Document-Root.

Das Abschlussprojekt des Buchs in Kapitel 14 macht regen Gebrauch


von Flash-Nachrichten, um Benutzern Rückmeldung zu jeder mögli-
chen Aktion zu geben.

9.3 Übungen
Übung 1
Erzeugen Sie eine Webseite mit einem »Cookie-Banner«, das Besucher einma-
lig fragt, ob sie Werbe-Cookies zustimmen.

ƒ Speichern Sie die Entscheidung in einem Cookie namens consent (Wert 1


für Ja bzw. 0 für Nein).
ƒ Verwenden Sie im Formular Eingabefelder vom Typ radio.
ƒ Unterdrücken Sie die Anzeige des Banners nach einer Entscheidung bei
Folgebesuchen.
ƒ Zeigen Sie zur Überprüfung den Zustimmungsstatus an.
ƒ Prüfen und testen Sie mit Hilfe der Browser-Konsole.
Übung 2
Erzeugen Sie eine Webseite mit einem Einkaufswagen für Eintrittskarten.
Speichern Sie die bisher gebuchte Ticketanzahl in einer Benutzersession.

210
Einstieg in die
objektorientierte
Programmierung (OOP)
Für die Strukturierung von Computerprogrammen haben sich verschiedene
Grundkonzepte entwickelt, so genannte Programmierparadigmen. Ein Pro-
grammierparadigma ist eine »Denkwelt« zur Lösung von Programmieraufga-
ben. Eine Programmiersprache legt durch die bereitgestellten Sprachelemente
die Verwendung bestimmter Paradigmen nahe. PHP ist eine Hybridsprache
mit zwei etablierten Paradigmen:
ƒ Prozedurale Programmierung: Hauptmerkmale sind eine genau bestimmte
Abfolge an Programmanweisungen und die Strukturierung durch Funktio­
nen (»Prozeduren«). Die bisherigen Beispiele im Buch verwenden diesen
Stil.
ƒ Objektorientierte Programmierung (OOP): Die Objektorientierung bietet
mächtige Werkzeuge, um Programme in sinnvolle Einheiten, die Objekte,
zu zerlegen. Ziel ist eine höhere Flexibilität, Stabilität und Wartbarkeit.
Kenntnisse der objektorientierten Programmierung sind nicht nur zur Gestal-
tung eigener Programme nützlich – PHP selbst stellt viele native Funktiona-
litäten im objektorientierten Stil bereit. In diesem Kapitel lernen Sie Grund-
konzepte der objektorientierten Programmierung kennen: Klassen, Objekte,
Eigenschaften, Methoden, Konstruktoren, Kapselung, Vererbung und Aus-
nahmen.

10.1 Grenzen der prozeduralen Programmierung


Bis in die 1990er Jahre war der prozedurale Programmierstil aus den klassi-
schen Programmiersprachen (C, Pascal, Fortran) vorherrschend. Unter diesen

211
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

Vorzeichen startete PHP als prozedurale Programmiersprache. Früh fanden


sich jedoch Sprachelemente zur Unterstützung der aufkommenden objekt­
orientierten Programmierung, die sich seitdem in den klassischen moder-
nen Sprachen (C++, Java, C#) durchsetzte. Wie andere Skriptsprachen auch
(JavaScript, Ruby, Python) entwickelte sich PHP zur Hybridsprache, die
­sowohl prozedurale als auch objektorientierte Programmierung unterstützt.
Für einfache Programme ist der prozedurale Stil ausreichend. Der objektori-
entierte Ansatz erscheint im Gegensatz anfangs sehr abstrakt. Die Vorteile der
»OOP« erschließen sich erst nach und nach mit komplexeren Anforderungen,
langlebigen Anwendungen und der Erfahrung, wo die prozedurale Program-
mierung an Grenzen stößt. Zum Beispiel:
ƒ »Spaghetti-Code«: Größere Programme entwickeln sich schnell in ver-
worrene Code-Gebilde mit unübersichtlichen Abhängigkeiten.
ƒ Mangelnde Datenkapselung: Keine Ausdrucksmöglichkeiten, um Daten
vor nicht beabsichtigter Verwendung zu schützen.
ƒ Unflexible Fehlerbehandlung: Konstrukte zum Umgang mit Fehlern er-
scheinen als Behelfslösung.
Die Sprachelemente und Denkweise der objektorientierten Programmierung
überwinden Schwierigkeiten der prozeduralen Programmierung. Der Einsatz
objektorientierter Programmierung ist Standard in populären PHP-Open-
Source-Projekten und professionellen PHP-Anwendungen.
Der bloße Einsatz objektorientierter Sprachelemente führt nicht automa-
tisch zu besseren Programmen. Ohne konsequente Anwendung der Prin-
zipien kann kein Programmierstil seinen Versprechen gerecht werden.

10.2 Grundbegriffe der »OOP«


Bei der gedanklichen Annäherung an eine Programmieraufgabe kristallisie-
ren sich bestimmte Akteure und Elemente heraus. Häufig haben diese Dinge
Entsprechungen in der realen Welt. In einem Online-Shop kauft vermutlich
ein »Kunde« ein, der »Produkte« in einen »Einkaufswagen« legt. Es kann sich
aber auch um abstraktere Dinge handeln wie einen »Blog-Artikel« mit »Kom-
mentaren« und »Likes«. In der objektorientierten Betrachtung sind die iden-
tifizierten Bausteine »Objekte«, die miteinander agieren können. Dafür gibt es
vier grundlegende Konzepte:

212
Grundbegriffe der »OOP« 10.2

ƒ Klasse (engl. class)


ƒ Objekt (engl. object)
ƒ Eigenschaft (engl. property)
ƒ Methode (engl. method)
10.2.1 Klasse (Class)
Eine Klasse ist der Bauplan für die Erstellung eines bestimmten Objekttyps.
Das ist vergleichbar mit der Definition eines eigenen Datentyps, der im An-
schluss für Variablen genutzt werden kann. Eine Personal-Software könnte die
Klasse Employee (engl. für »Angestellter«) als Bauplan für Objekte modellie-
ren, um Angestellte zu repräsentieren:

Abb. 10.1: Ein Angestellter – noch ohne Eigenschaften

Die Definition einer Klasse erfolgt mit dem Schlüsselwort class:


class Employee {}

Der Code zur Definition der Klasse hat wie die Definition einer Funktion kei-
ne direkte Auswirkung; sie macht PHP lediglich mit dem Bauplan für Objekte
vom Typ Employee bekannt. Auf Grund des leeren Rumpfs hat die Klasse noch
keine Funktionalität.

10.2.2 Objekt (Object)


Nach dem Bauplan einer Klasse kann ein konkretes Objekt erzeugt werden.
Dieser Vorgang heißt Instanziierung. Das erzeugte Objekt ist eine Instanz der
Klasse.

213
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

Abb. 10.2: Instanziierung mehrerer Employee-Instanzen

Neue Objekte erzeugt der new-Operator. Darauf folgt der Name der Klasse,
von der ein neues Objekt erzeugt werden soll und ein Paar runde Klammern.
Folgendes Skript instanziiert Objekte für zwei Angestellte:
class Employee {}
$employee1 = new Employee(); // ein Angestellter
$employee2 = new Employee(); // ein anderer Angestellter

Listing 10.1: Erzeugung von Employee-Ojekten in employee.php

var_dump() macht sichtbar, dass es sich um zwei verschiedene Objekte vom


Typ Employee handelt:
var_dump($employee1); // object(Employee)#1 (0) {}
var_dump($employee2); // object(Employee)#2 (0) {}

Noch haben die Objekte keine Funktionalitäten. Erst Eigenschaften und Me-
thoden hauchen den Objekten Leben ein.

10.2.3 Eigenschaft (Property)


Objekte werden durch Eigenschaften näher beschrieben. Für das Objekt zur
Repräsentation eines Angestellten erscheinen die Eigenschaften »Vorname«
und »Nachname« naheliegend:

214
Grundbegriffe der »OOP« 10.2

Abb. 10.3: Angestellte mit Eigenschaften

Die Klasse Employee legt in ihrem Rumpf fest, mit welchen Eigenschaften jedes
konkrete Objekt ausgestattet sein soll:
class Employee {
public string $firstName;
public string $lastName;
}

Listing 10.2: Employee-Klasse mit zwei Eigenschaften in employee-with-properties.php

Die Deklaration einer Eigenschaft im Klassenrumpf folgt dem Schema:


Sichtbarkeit Datentyp $variable;

Eigenschaften kann man sich als Variablen im Kontext eines Objekts vor-
stellen. Jedes erzeugte Objekt vom Typ Employee verfügt über seine eigenen
Variablen für Vor- und Nachnamen. Der Datentyp schränkt ihren Wert auf
einen bestimmten Datentyp ein. Mit der Angabe string ist sichergestellt, dass
Vor- und Nachname eines Angestellten sinnvollerweise nur Strings sein kön-
nen. Die Sichtbarkeit bestimmt, von welchen Bereichen des Programms ein
Zugriff auf eine Eigenschaft erlaubt sein soll – dazu mehr in Abschnitt 10.2.7.
Nach der Instanziierung eines Objekts vom Typ Employee können die Eigen-
schaften wie Variablen genutzt werden. Die Zugehörigkeit zum Objekt wird
mit dem Zugriff über den speziellen Objekt-Operator »->« deutlich:

215
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

$emma = new Employee();


$emma->firstName = 'Emma'; // Wert an Eigenschaft zuweisen
$emma->lastName = 'Wagner';
$max = new Employee();
$max->firstName = 'Max';
$max->lastName = 'Becker';
echo $emma->firstName.' '.$emma->lastName; // Emma Wagner
echo $max->firstName.' '.$max->lastName; // Max Becker

Eigenschaften sind das »Gedächtnis« eines Objekts, ihre Werte bestimmen


dessen aktuellen Zustand (engl. state).

Hinweis

Natürlich hat ein »echter« Angestellter weitere Eigenschaften, z.B. Augen-


farbe, Blutgruppe usw. Eine Software modelliert nur, was für das Programm
relevant ist.

Eigenschaften können mit einem Vorgabewert initialisiert werden:


class Employee {
public int $vacationDays = 30; // Urlaubstage
}
$employee = new Employee();
echo $employee->vacationDays; // => 30

Listing 10.3: Eigenschaft mit Vorgabewert in employee-with-property-default.php

10.2.4 Methode (Method)


Methoden legen das Verhalten eines Objekts fest. Sie werden wie gewöhnli-
che Funktionen definiert, allerdings im Rumpf einer Klasse. Sie können nur
im Zusammenhang mit einem Objekt dieser Klasse aufgerufen werden. Die
folgende Klasse Employee definiert eine Methode payBonus(), um einem An-
gestellten, d.h. einem Objekt dieser Klasse, einen fiktiven Bonus zu bezahlen:
class Employee {
public function payBonus(int $amount): void {
echo "Danke für $amount €!";

216
Grundbegriffe der »OOP« 10.2

}
}

Listing 10.4: Definition einer Methode in employee-with-method.php

Nach der Erzeugung eines Objekts der Klasse Employee kann der Aufruf der
Methode mit dem Objekt-Operator »->« erfolgen:
$employee = new Employee();
$employee->payBonus(1000); // => Danke für 1000 €!

Wie Eigenschaften (Abschnitt 10.2.3) legen auch Methoden in ihrer Defini-


tion eine Sichtbarkeit fest, die vor dem Schlüsselwort function notiert wird.
Bisher wurde nur die Sichtbarkeit public verwendet – mehr dazu in Abschnitt
10.2.7.

10.2.5 Die spezielle Variable $this


Innerhalb eines Methodenrumpfs stellt PHP automatisch die spezielle Variab-
le $this zur Verfügung. Diese Sondervariable repräsentiert das Objekt selbst
und ermöglicht den Zugriff auf Eigenschafen und Methoden innerhalb des
Objekts. Folgende Definition der Klasse Employee definiert eine Eigenschaft
und eine Methode. Im Rumpf der Methode wird $this zum Zugriff auf die
Eigenschaft eingesetzt:
class Employee {
public int $vacationDays = 30; // Urlaubstage
public function takeVacation(int $days): void {
$this->vacationDays -= $days; // Urlaubstage anpassen
}
}
$employee = new Employee();
$employee->takeVacation(5);
echo $employee->vacationDays; // => 25

Listing 10.5: Die Sondervariable $this in employee-using-this-variable.php

Im Code referenzieren $employee und $this dasselbe Objekt: $employee von


außerhalb und $this von innerhalb.

217
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

10.2.6 Konstruktor: __construct()


Der Konstruktor ist eine optionale Methode mit dem reservierten Namen
__construct() (zwei Unterstriche!). Ist ein Konstruktor definiert, ruft PHP
ihn während der Instanziierung automatisch auf:
class Employee {
public function __construct() {
echo 'Ich werde instanziiert!';
}
}
new Employee(); // => Ich werde instanziiert!

Der Konstruktor ist nützlich, um ein Objekt vor der weiteren Verwendung
in einen gewünschten Ausgangszustand zu versetzen, z.B. durch Methoden-
Parameter:
class Employee {
public int $vacationDays;
public function __construct(int $vacationDays) {
$this->vacationDays = $vacationDays; // initiale Urlaubstage
}
}

Listing 10.6: Konstruktor-Methode in employee-with-constructor.php

Entsprechend sind bei der Instanziierung Konstruktor-Argumente zu über-


geben:
$employee = new Employee(28);
echo $employee->vacationDays; // => 28

Der Konstruktor ist eine Methode ohne Rückgabewert! Die Signatur des
Konstruktors besitzt daher weder einen Typ-Hinweis auf den Rückgabe-
wert, noch kann der Rumpf einen Wert zurückgeben.

10.2.7 Sichtbarkeit (Visibility)


Die Sichtbarkeit einer Eigenschaft bzw. Methode definiert den Programmkon-
text, aus dem heraus ein Zugriff erlaubt ist. Alle bisherigen Beispiele verwen-
deten die öffentliche (public) Sichtbarkeit. In folgendem Code ist der Zugriff
auf die öffentliche Eigenschaft $vacationDays aus dem globalen Programm-
kontext möglich, d.h. von außerhalb des Objekts:

218
Grundbegriffe der »OOP« 10.2

class Employee {
public int $vacationDays;
}
$employee = new Employee();
$employee->vacationDays = 30; // Zugriff von außen
echo $employee->vacationDays; // => 30

Einer öffentlichen Eigenschaft lassen sich so aber auch ungehindert fragwür-


dige Werte zuweisen:
$employee->vacationDays = -498;
echo $employee->vacationDays; // => -498

Durch Anpassung auf eine private (private) Sichtbarkeit kann ein Objekt sei-
ne Daten vor dem Zugriff von außen schützen:
class Employee {
private int $vacationDays;
}
$employee = new Employee();
$employee->vacationDays = 30; // Zugriff verboten!
// Fatal error: Uncaught Error: Cannot access
// private property Employee::$vacationDays

Das Schreiben und Lesen einer privaten Eigenschaft innerhalb des Objekts
mittels $this ist weiterhin möglich. Methoden können daher die Aufgabe
übernehmen, der Eigenschaft Werte zuzuweisen:
class Employee {
private int $vacationDays;
public function setVacationDays(int $days): void {
$this->vacationDays = $days; // Zugriff möglich!
}
public function vacationDays(): int {
return $this->vacationDays; // Zugriff möglich!
}
}
$employee = new Employee();
$employee->setVacationDays(25); // Aufruf von außen
echo $employee->vacationDays(); // => 25

Listing 10.7: employee-with-private-property.php

219
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

Das Zuweisen eines Werts an die private Eigenschaft mit dem Umweg über
eine Methode ergibt bis hierhin noch keinen Vorteil gegenüber einer Ei-
genschaft, die selbst öffentlich ist. Eine Methode kann jedoch beliebige Pro-
grammlogik enthalten und eröffnet dadurch alle Möglichkeiten zur Überprü-
fung eingehender Daten, die für die Eigenschaft bestimmt sind.
class Employee {
private int $vacationDays;
public function setVacationDays(int $days): void {
if ($days < 20) {
exit('Mindesturlaub unterschritten!');
}
$this->vacationDays = $days;
}
}
$employee = new Employee();
$employee->setVacationDays(0);
// => Mindesturlaub unterschritten!

Neben privaten Eigenschaften können private Methoden helfen, grö-


ßere Codeblöcke innerhalb einer Klasse in handliche Einheiten zu
zerlegen. Beim Blick von außen auf ein Objekt sind nur die öffentli-
chen Methoden sichtbar.

10.2.8 Das Geheimnisprinzip: Daten kapseln


Ziel eines Computer-Programms ist die Erfüllung bestimmter Anforderun-
gen. Ein Programm wird umso stabiler, je besser es sich gegen Verletzungen
der Anforderungen schützt und dadurch unerwartete Situationen sinnvoll
unterbindet.
Lautet die Anforderung einer Software zur Personalverwaltung, dass kein
Mitarbeiter den gesetzlichen Mindesturlaub unterschreiten darf, schützt sich
die Klasse Employee aus dem vorigen Abschnitt bereits gegen das Setzen un-
gültiger Werte. Für Benutzer von Objekten des Typs Employee ist ein korrek-
ter Zustand noch nicht vollständig garantiert. Ein Zugriff auf die Urlaubstage
kann scheitern, sofern sie vorher nicht gesetzt wurden:
$employee = new Employee();
echo $employee->vacationDays();
// Fatal error: Uncaught Error: Typed property

220
Vererbung 10.3

// Employee::$vacationDays must not be accessed


// before initialization

Eine Garantie für einen gültigen Zustand entsteht, indem bereits der Kon­
struktor zur Übergabe der Urlaubstage verpflichtet:
class Employee {
private int $vacationDays;
public function __construct(int $vacationDays) {
if ($vacationDays < 20) {
exit('Mindesturlaub unterschritten!');
}
$this->vacationDays = $vacationDays;
}
public function vacationDays(): int {
return $this->vacationDays;
}
}
$employee = new Employee(27);
echo $employee->vacationDays(); // => 27

Listing 10.8: employee-with-encapsulated-data.php

Die Employee-Klasse erreicht damit ein wichtiges Ziel der objektorientier-


ten Programmierung, die so genannte Datenkapselung – auch als »Geheim-
nisprinzip« bezeichnet.
Im letzten Beispiel ist die Eigenschaft $vacationDays gekapselt: Von außen be-
trachtet bleibt sie verborgen, ein direkter Zugriff ist nicht erlaubt. Die Kommu-
nikation mit einem Objekt der Klasse ist nur über die als öffentlich festgelegte
Methode vacationDays() möglich. Je geringer die Anzahl der öffentlichen Ei-
genschaften und Methoden bzw. je mehr Bestandteile einer Klasse »geheim«
sind, desto flexibler sind interne Anpassungen der Klasse durchzuführen. Die
verborgenen Teile können beliebig geändert werden, solange die öffentliche
»Schnittstelle« (API) unverändert bleibt.

10.3 Vererbung
Ein weiteres grundlegendes Strukturierungselement der objektorientierten
Programmierung ist die Vererbung (engl. inheritance). Die Vererbung erlaubt
es, eine Klasse zu definieren, die auf einer bereits existierenden Klasse auf-

221
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

baut. Die neue Klasse »erbt« die Fähigkeiten der Basisklasse und kann neue
hinzufügen.

Abb. 10.4: Mitarbeiter-Vererbungshierarchie

Die beiden Klassen Manager und Worker erweitern (engl. to extend) die Klasse
Employee:

class Employee {}
class Manager extends Employee {}
class Worker extends Employee {}

Eine Klasse, von der geerbt wird, bezeichnet man als Elternklasse, die
erbende Klasse als Kindklasse.

»Manager« und »Arbeiter« sind spezifischere Ausprägungen eines »Angestell-


ten«. Die Elternklasse Employee definiert Funktionalität, die auf alle Angestell-
ten gleichermaßen zutrifft, z.B. zur Speicherung des Namens:
class Employee {
private string $name;
public function __construct(string $name) {
$this->name = $name;

222
Vererbung 10.3

}
public function name(): string {
return $this->name;
}
}

Die spezifischeren Klassen Manager und Worker erweitern die Elternklasse um


die eigene Funktionalität. Die Manager-Klasse könnte beispielsweise um eine
Methode erweitert werden, um einem Manager Arbeiter als Team-Mitglieder
zuzuweisen:
class Manager extends Employee {
private array $workers = [];
public function addWorkerToTeam(Worker $worker): void {
$this->workers[] = $worker;
}
public function team(): array {
return $this->workers;
}
}

Listing 10.9: employee-with-inheritance.php

Der Type Hint Worker für den Parameter $worker in der Methode addWorker-
ToTeam() stellt sicher, dass nur Objekte der Klasse Worker übergeben werden
dürfen. Einem Manager können dadurch nur Arbeiter als Team-Mitglieder
zugeordnet werden, aber keine anderen Manager. Dadurch können Anforde-
rungen an eine Software sicher abgebildet werden.
$worker1 = new Worker('Arbeiter Max');
$worker2 = new Worker('Arbeiter Jan');
$manager = new Manager('Manager Hans');
$manager->addWorkerToTeam($worker1);
$manager->addWorkerToTeam($worker2);
foreach($manager->team() as $worker) {
echo $worker->name() . "<br/>";
}

Eine Unterscheidung von Objekten anhand ihrer Klassenzugehörigkeit ist mit


dem instanceof-Operator möglich. Im Login-Bereich einer Webanwendung
könnte dadurch über die Anzeige unterschiedlicher Informationen entschie-
den werden:

223
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

if ($employee instanceof Manager) {


// Schichtplan aller Arbeiter im Team zeigen
} elseif ($employee instanceof Worker) {
// Schichtplan des Arbeiters anzeigen
}

10.4 Ausnahmen (Exceptions)


Im bisherigen Verlauf des Buchs konnten Sie zwei Varianten im Umgang mit
einer Fehlersituation beobachten:
ƒ Beenden des Skripts am Ort des Fehlers mit exit().
ƒ In einer Funktion bzw. Methode: Rückgabe eines alternativen Werts wie
false oder null zum Zeichen eines Fehlers. Es bleibt dem Aufrufer über-
lassen, den Fehler zu erkennen und Maßnahmen zu ergreifen.
Der Konstruktor der Employee-Klasse im vorigen Abschnitt beendete das
Skript, um sich vor ungültigen Werten zu schützen:
class Employee {
private int $vacationDays;
public function __construct(int $vacationDays) {
if ($vacationDays < 20) {
exit('Mindesturlaub unterschritten!');
}
$this->vacationDays = $vacationDays;
}
// ...
}

Die Beendigung des Skripts am Ort des Fehlers verhindert die weitere Verar-
beitung eines ungültigen Werts, ist jedoch recht unflexibel. Eine Web-Anwen-
dung könnte z.B. mitten in der Zusammenstellung einer HTML-Seite unter-
brochen werden:
<html>
<head>…</head>
<body>
<?php $employee = new Employee(3); ?>
Urlaubstage: <?= $employee->vacationDays() ?>
</body>
</html>

224
Ausnahmen (Exceptions) 10.4

Das Ergebnis ist unvollständiger HTML-Code statt einer kontrollierbaren


und vollständigen Fehlerseite:
<html>
<head>…</head>
<body>
Urlaubstage: Mindesturlaub unterschritten!

Die Variante, einen Fehler durch alternative Rückgabewerte anzuzeigen, ver-


wendet PHP bei vielen nativen Funktionen. Schlägt z.B. die Umwandlung
einer JSON-Zeichenkette in ein Array mit der Funktion json_decode() fehl,
gibt sie null statt eines Arrays zurück:
$json = '["a", "b", "c"'; // Eckige Klammer fehlt!
$data = json_decode($json, true);
if ($data === null) { // Fehler: Ungültiges JSON
echo 'JSON war ungültig, was nun?';
} else { // Umwandlung erfolgreich
// Hier alles gut, $data ist eine Array
}

Auch diese Variante hat Nachteile: Der Code zur Fehlerbehandlung kann
leicht vergessen werden, die Kenntnis verschiedener Rückgabewerte ist not-
wendig. Darüber hinaus erhält der Aufrufer keine Detailinformationen über
den Fehler, lediglich den Wert null.
Die objektorientierte Programmierung bietet mit dem Konzept der Ausnahme
(engl. exception) eine flexiblere Handhabung von Fehlern. Entsteht eine Feh-
lersituation, verkündet der Code diese Tatsache mit dem »Werfen« (engl. to
throw) einer Ausnahme. Eine Ausnahme wird durch ein Objekt der in PHP
eingebauten Klasse Exception (oder einer ihrer Kindklassen) repräsentiert
und die passende Fehlermeldung dem Konstruktor übergeben:
class Employee {
private int $vacationDays;
public function __construct(int $vacationDays) {
if ($vacationDays < 20) {
throw new Exception('Mindesturlaub unterschritten!');
}
$this->vacationDays = $vacationDays;
}

225
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

// ...
}

Listing 10.10: Werfen einer Ausnahme in employee-with-exception.php

Das Auftreten einer Ausnahme führt zu einem fatalen Fehler mit Abbruch des
Skripts:
$employee = new Employee(3);
// Fatal error: Uncaught Exception: Mindesturlaub
// unterschritten.

Neben der Fehlermeldung berichtet PHP, dass die Ausnahme nicht »gefan-
gen« (engl. uncaught) wurde. Tatsächlich hat das Werfen einer Ausnahme kei-
nen konkreten Adressaten: Solange sie niemand »fängt«, kommt es zu einem
fatalen Fehler. Mit dem try-catch-Konstrukt erhält der aufrufende Code je-
doch die Möglichkeit, geworfene Ausnahmen aufzufangen:
try {
$employee = new Employee(3); // wirft Ausnahme!
echo $employee->vacationDays();
} catch (Exception $e) { // fängt Ausnahme!
echo 'Fehler: ' . $e->getMessage();
} // => Fehler: Mindesturlaub unterschritten.

Im try-Block kann etwas »versucht« werden, das potenziell schiefläuft und


eine Ausnahme wirft. Kommt es zu einer Ausnahme, bricht der try-Block
sofort ab. Die Klausel des catch-Blocks in runden Klammern »fängt« (engl.
to catch) die Ausnahme und stellt das Ausnahme-Objekt als Variable zur Ver-
fügung (im Beispiel $e). Das Programm läuft weiter und kann seinen Pro-
grammfluss der Fehlersituation anpassen. Bei Bedarf steht die Fehlerursache
im Ausnahme-Objekt mit der Methode getMessage() zur Verfügung:
<html>
<head>…</head>
<body> Urlaubstage:
<?php
try {
$employee = new Employee(3);
echo $employee->vacationDays();
} catch (Exception $e) {
echo 'Fehler! ' . $e->getMessage();

226
Native Objektorientierung in PHP 10.5

}
?>
</body>
</html>

Der erzeugte HTML-Code im Fehlerfall:


<html>
<head>…</head>
<body>
Urlaubstage: Fehler! Mindesturlaub unterschritten.
</body>
</html>

Zusammengefasst: Das Ausnahme-Konzept entkoppelt das Auftreten eines


Programmfehlers von dessen Behandlung, die Fehlerdetails sind in einem
Objekt gekapselt.

Aufgabe 1

Als viertes Argument kann der Funktion json_decode() ein »Flag« über-
geben werden, um das Verhalten im Fehlerfall umzuschalten: Statt null
zurückzugeben, wirft die Funktion dann eine Ausnahme. Finden Sie das
passende Flag (eine vordefinierte Konstante) in der PHP-Dokumentation?

10.5 Native Objektorientierung in PHP


PHP stellt auch selbst viele native Funktionalitäten im objektorientierten Stil
zur Verfügung – oft parallel zum prozeduralen Stil. Dieser Abschnitt vergleicht
die prozedurale mit der objektorientierten Herangehensweise am Beispiel des
Umgangs mit Datum und Zeit.

10.5.1 Datum & Zeit: Prozedurale Programmierung


Frühere Beispiele im Buch verwenden den prozeduralen Stil im Umgang mit
Datum und Zeit. Jeder Funktionsaufruf liefert einen primitiven Datentyp zu-
rück und ist in sich abgeschlossen. Fehler werden z.B. über den alternativen
Rückgabewert false kommuniziert. Folgendes Skript liest ein Datum aus ei-
ner hypothetischen Eingabe ($input) ein, um es nach verschiedenen Manipu-
lationen immer wieder formatiert auszugeben (das Formatzeichen e steht für
die Zeitzone):

227
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

$input = 'June 21st 2023 3pm';


$timestamp = strtotime($input); // Umwandlung in Unixzeit
if ($timestamp === false) { // Eingabe ungültig?
exit('Ungültige Datumseingabe.');
}
echo date('d.m.Y H:i:s e', $timestamp);
$timestamp = strtotime('+1 month', $timestamp);
echo date('d.m.Y H:i:s e', $timestamp);
$timestamp = strtotime('20:35:00', $timestamp);
echo date('d.m.Y H:i:s e', $timestamp);
date_default_timezone_set('America/Los_Angeles');
echo date('d.m.Y H:i:s e', $timestamp);

Listing 10.11: Prozedurale Datumsfunktionen in datetime-procedural.php

10.5.2 Datum & Zeit: Objektorientierte Programmierung


Eine elegante objektorientierte Alternative zum Umgang mit Datum und Zeit
bietet die in PHP eingebaute DateTime-Klasse (und einige Zusatzklassen).
Ein Objekt vom Typ DateTime ist wie eine Art Kalender mit umfangreichen
Methoden zum Einstellen und Formatieren von Datum und Uhrzeit. Einmal
erstellt, kann das »Kalender-Objekt« immer weiterverwendet werden, vorige
Einstellungen bleiben erhalten. Die Methodennamen beschreiben das Verhal-
ten eines solchen Objekts intuitiv, Fehler werden durch Ausnahmen kommu-
niziert. Folgendes Skript wiederholt die Operationen des prozeduralen Skripts
aus dem vorigen Abschnitt mit den objektorientierten Möglichkeiten:
$input = 'June 21st 2023 3pm';
try { 1
$datetime = new DateTime($input); 2
echo $datetime->format('d.m.Y H:i:s e'); 3
$datetime->modify('+1 month'); 4
echo $datetime->format('d.m.Y H:i:s e');
$datetime->setTime(20, 35, 0); 5
echo $datetime->format('d.m.Y H:i:s e');
$timezone = new DateTimeZone('America/Los_Angeles'); 6
$datetime->setTimezone($timezone); 7
echo $datetime->format('d.m.Y H:i:s e');
} catch (Exception $e) { 8

228
Übungen 10.6

echo 'Fehler: ' . $e->getMessage();


}

Listing 10.12: Objektorientierter Umgang mit Datum und Zeit in datetime-oop.php

ƒ 1 Der try-Block schließt Code ein, der potenziell Ausnahmen wirft.


ƒ 2 Erstellung eines DateTime-Objekts. Das erste Argument stellt das Objekt
mit den von strtotime() bekannten Datumsformaten auf ein bestimmtes
Datum ein, das es sich intern merkt. Ungültige Werte führen zu einer Aus-
nahme.
ƒ 3 Formatierte Datumsausgabe mit den von date() bekannten Formatzei-
chen. Formatiert wird das aktuell im DateTime-Objekt eingestellte Datum.
ƒ 4 Die modify()-Methode ändert das im DateTime-Objekt eingestellte Da-
tum mit den von strtotime() bekannten Datumsformaten.
ƒ 5 Die setTime()-Methode ändert nur die Uhrzeit des DateTime-Objekts.
ƒ 6 Erstellung eines DateTimeZone-Objekts für die Zeitzone von Los Angeles.
ƒ 7 Einstellen der Zeitzone des DateTime-Objekts auf eine andere Zeitzone.
ƒ 8 Abfangen potenzieller Ausnahmen aus dem try-Block.

Aufgabe 2

Welches Datum enthält ein Objekt der DateTime-Klasse, wenn es ohne Kon-
struktor-Argument erzeugt wird?

10.6 Übungen
Übung 1
Schreiben Sie ein Skript, das eine Klasse Product definiert.
ƒ Statten Sie die Klasse mit drei öffentlichen Eigenschaften für Name, Be-
schreibung und Preis aus.
ƒ Instanziieren Sie zwei verschiedene Objekte der Klassen.
ƒ Setzen Sie Werte für die Eigenschaften.
ƒ Geben Sie eine Produktliste mit allen Eigenschaften aus. Formatieren Sie
den Preis dabei im deutschen Format.

229
Kapitel 10 Einstieg in die objektorientierte Programmierung (OOP)

Übung 2
Erweitern Sie die Product-Klasse aus der vorigen Übung.
ƒ Kapseln Sie die Objektdaten in privaten Eigenschaften.
ƒ Setzen Sie die Eigenschaften durch die Konstruktor-Methode mit Hilfe der
Sondervariablen $this.
ƒ Führen Sie Validierungen ein, um nur Objekte mit sinnvollen Daten zu-
zulassen, d.h. kein leerer Name, keine leere Beschreibung, kein Preis unter
Null. Werfen Sie ansonsten Ausnahmen vom Typ Exception mit Hilfe des
Schlüsselworts throw.
ƒ Ergänzen Sie öffentliche Methoden zum Zugriff auf die Objektdaten. Ge-
ben Sie dabei den Preis bereits formatiert zurück.
ƒ Passen Sie die Produktliste entsprechend an.

Übung 3
Instanziieren Sie ein DateTime-Objekt mit dem ersten Tag des nächsten Mo-
nats mittags deutscher Zeit. Stellen Sie das Datum um sechs Wochen vor. Än-
dern Sie die Zeitzone auf New York. Geben Sie das eingestellte Datum nach
jeder Änderung formatiert aus.
Tipps:
ƒ https://www.php.net/datetime.construct
ƒ https://www.php.net/datetimezone.construct

230
Datenverwaltung
mit MySQL
Wichtige Aspekte bei der Datenverarbeitung sind die dauerhafte Speicherung
und der spätere Wiederabruf von Daten. Gewöhnliche Dateien eigenen sich
zwar grundsätzlich zur Datenhaltung (Abschnitt 8.2), fortgeschrittene An-
forderungen können sie aber nicht erfüllen. Lese- und Schreiboperationen
wachsender Dateien sind träge, gleichzeitige Zugriffe verschiedener Prozesse
können sich gegenseitig blockieren oder zu widersprüchlichen Daten führen,
das Auslesen nur von Teilen der Daten ist kaum möglich. Die Mittel für Da-
tenstrukturierung und Zugriffsbeschränkungen sind gering.
Zur professionellen Datenverwaltung kommen daher spezielle Datenbankma-
nagementsysteme (DBMS) zum Einsatz, die eine strukturierte, effiziente und
»konsistente«, d.h. widerspruchsfreie, Datenhaltung ermöglichen. Dieses Ka-
pitel zeigt Grundlagen zu Datenbanken und Tabellen und stellt MySQL als
relationales Datenbankmanagementsystem vor. Sie lernen MySQL zu instal-
lieren, SQL als Abfragesprache einzusetzen, eine Datenbank mit Tabellen an-
zulegen sowie Datensätze in Tabellen einzufügen, abzufragen, zu verändern
und zu löschen.

11.1 Was ist eine relationale Datenbank?


Eine relationale Datenbank speichert Daten tabellenbasiert. Eine Tabelle ist
ähnlich einer Excel-Tabelle ein Container zur Sammlung verwandter Daten
in einer Datenstruktur aus Spalten und Zeilen. Anzahl und Art der Spalten
sind fest definiert, die Anzahl der Zeilen beliebig. Eine Zeile mit Daten wird
Datensatz (engl. data record) genannt.

231
Kapitel 11 Datenverwaltung mit MySQL

Abb. 11.1: Datenbanktabelle

Die Datenbank selbst ist ein benannter Container, der mehrere Tabellen zu
einer Einheit zusammenfasst.

Abb. 11.2: Datenbank als Container für Tabellen

Die bekannteste Alternative zu relationalen Datenbanken sind so-


genannte non-relationale- bzw. NoSQL-Datenbanken. Sie speichern
Daten in weniger strikten Strukturen als fest vorgegebenen Tabellen.
In speziellen Anwendungsfällen bringt dies Vorteile, z.B. im Umgang
mit riesigen Datenmengen (»Big Data«). Relationale Datenbanksyste-
me sind jedoch vorherrschend.

11.2 Was ist ein Datenbankmanagementsystem?


Ein Datenbankmanagementsystem (DBMS) ist einer Software zur Verwaltung
einer Vielzahl von Datenbanken. Sie ist auf effiziente und dauerhafte Daten-
haltung spezialisiert und kontrolliert Zugriffe auf die Daten.

232
Was ist SQL? 11.3

Abb. 11.3: Datenbankmanagementsystem (DBMS)

Client-Programme kommunizieren mit dem DBMS in der einheitlichen Da-


tenbanksprache SQL. Solche Client-Programme sind z.B. Kommandozeilen-
Anwendungen, PHP-Skripte, Java-Programme oder grafische Verwaltungs-
software.
Ein Datenbankmanagementsystem speziell für relationale Datenban-
ken wird auch spezifischer als Relationales Datenbankmanagement-
system (RDBMS) bezeichnet.
Die Installation eines DBMS macht den Computer zu einem Datenbankserver.
Wie ein Webserver stellt ein Datenbankserver seinen Dienst über einen be-
stimmten Port zur Verfügung (vergleiche Abschnitt 6.1). Ein Computer kann
auch gleichzeitig Web- und Datenbankserver sein, was insbesondere für die
lokale Entwicklungsumgebung nützlich ist.

11.3 Was ist SQL?


Die Kommunikation zwischen Client-Programmen und einem relationalen
Datenbanksystem erfolgt in einer eigenen Sprache, der »Datenbanksprache«
SQL (Structured Query Language). Mit SQL lassen sich Befehle für gewünschte
Aktionen formulieren. Zur Ausführung werden sie an das Datenbanksystem
gesendet. SQL ist stark an die englische Umgangssprache angelehnt, Befehle
sind meist intuitiv verständlich. Folgender Befehl erzeugt eine Datenbank mit
dem Namen shop:

233
Kapitel 11 Datenverwaltung mit MySQL

CREATE DATABASE shop;

Das Semikolon (;) schließt einen SQL-Befehl ab. Folgender Befehl fügt einen
Datensatz in die Tabelle products der Datenbank shop ein. Ein Datensatz be-
steht aus Werten für die Spalten name und price:
INSERT INTO shop.products (name, price)
VALUES ('Notebook', 1499);

Die Großschreibung von SQL-Befehlen ist eine Konvention, die


Schreibweisen CREATE DATABASE bzw. create database sind äquiva-
lent. Die Großschreibung der Befehle sorgt für bessere Lesbarkeit.
SQL-Befehle werden in verschiedene Kategorien eingeteilt:

Kategorie Beschreibung Beispiele


Data Definition Language Erzeugen, Ändern, Löschen CREATE DATABASE,
(DDL) von Datenstrukturen CREATE TABLE
Data Manipulation Lan- Einfügen, Ändern, Abfragen INSERT, UPDATE,
guage (DML) und Löschen von Datensätzen SELECT, DELETE
Data Control Language Verwaltung von Zugriffs­ GRANT, REVOKE
(DCL) rechten
Tab. 11.1: Wichtige SQL-Befehlskategorien

Abgesehen von einigen Grundeinstellungen wird ein Datenbankmanage-


ment-System vollständig über SQL gesteuert. Auch grafische Programme zur
Datenbankverwaltung verwenden im Hintergrund SQL-Befehle.
Durch eine ISO-Standardisierung ist SQL theoretisch unabhängig vom
verwendeten Datenbanksystem – ein SQL-Befehl sollte auf verschiede-
nen Datenbanksystemen gleichermaßen funktionieren. Die meisten Sys-
teme unterstützen den Standard jedoch nicht vollständig oder ergänzen
SQL mit eigenen (»proprietären«) Funktionalitäten. Details mancher Be-
fehle unterscheiden sich daher von System zu System.

11.4 Erste Schritte mit MySQL


Die Auswahl an relationalen Datenbanksystemen ist groß, bekannte Vertre-
ter sind MySQL, MariaDB, PostgreSQL, Oracle oder Microsoft Access. In der
PHP-Entwicklung ist meist MySQL das System der Wahl.

234
Erste Schritte mit MySQL 11.4

11.4.1 Warum MySQL?


PHP unterstützt alle bekannten Datenbanksysteme. Verschiedene Gründe
machen gerade MySQL in Kombination mit PHP so beliebt:
ƒ Open-Source und kostenlos.
ƒ Hoher, weltweiter Verbreitungsgrad; daher große Community und Unter-
stützung im Internet.
ƒ Viele kostenlose Hilfswerkzeuge.
ƒ Sehr gute Performance für die meisten Anwendungsfälle.
ƒ Ausführliche Dokumentation (https://dev.mysql.com/doc).
ƒ Für alle gängigen Betriebssysteme verfügbar.
ƒ Seit 1995 kontinuierliche Weiterentwicklung. Neuere Versionen bringen
auch nicht-relationale Konzepte (NoSQL) mit.
ƒ Breite Unterstützung durch Webhoster.
ƒ Breite Unterstützung in PHP-Standardsoftware, z.B. in Content-Manage-
ment-Systemen (Wordpress, Joomla etc.) oder Shopsystemen (Magento,
WooCommerce etc.).
ƒ Bei Bedarf kostenpflichtige Enterprise-Version mit fortgeschrittenen Funk-
tionen und Support verfügbar.
Die Übernahme von MySQL durch Oracle im Jahr 2010 schürte
Ängste in der Open-Source-Gemeinde von MySQL vor einer Kom-
merzialisierung der Datenbank. Mit dem Datenbanksystem MariaDB
entstand ein paralleler Entwicklungszweig (engl. fork), unabhängig
von Oracle, der sich ebenfalls großer Beliebtheit erfreut.

11.4.2 MySQL installieren


Wie ein Webserver ist MySQL eine Software, die auf einem Host-Computer
installiert wird und im Hintergrund auf Anfragen von Clients über einen be-
stimmten Port (Standard: 3306) wartet.
Eine MySQL-Installation bringt das Client-Programm mysql für die Kom-
mandozeile mit. Es kann nach der Installation des Servers zum Testen der
Verbindung und für die ersten Schritte mit SQL verwendet werden. Im nächs-
ten Kapitel lernen Sie dann eine Möglichkeit kennen, den Server bequem über
eine grafische Oberfläche zu bedienen.

235
Kapitel 11 Datenverwaltung mit MySQL

Windows
Laden Sie den MSI-Installer unter https://dev.mysql.com/downloads/
installer herunter. Je nach Versionsnummer erhalten Sie eine Datei mit
einem Namen ähnlich zu mysql-installer-community-8.0.27.0.msi. Für
­
den Download müssen Sie keinen Benutzeraccount auf mysql.com anlegen,
klicken Sie bei der Aufforderung das unscheinbare »No thanks, just start
my download«. Starten Sie den Installer und wählen Sie im ersten Schritt der
Installation Server Only.

Abb. 11.4: MySQL-Installationstyp wählen

Folgen Sie den Einrichtungsschritten mit den vorausgewählten Einstellungen.


Vergeben Sie ein Passwort für den »root«-Benutzer – er besitzt alle Rechte zur
Verwaltung des MySQL-Systems.

Abb. 11.5: Passwort für den root-Benutzer vergeben

236
Erste Schritte mit MySQL 11.4

Der Haken bei »Start the MySQL Server at System Startup« sorgt für einen
automatischen MySQL-Start nach einem Computer-Neustart. Schließen Sie
die Installation ab.
Zur bequemen Nutzung des Kommandozeilen-Clients mysql ohne lange Pfad­
angabe ergänzen Sie analog zur PHP-Installation (Abschnitt 1.7.1) das Ver-
zeichnis C:\Program Files\MySQL\MySQL Server 8.0\bin in der Path-Umge-
bungsvariable.

Abb. 11.6: Path-Umgebungsvariable konfigurieren

Damit der mysql-Befehl in der Git Bash (Abschnitt 1.6.1) funktioniert, muss
ein Problem bei der Verwendung interaktiver Funktionen (z.B. Passwortabfra-
gen) behoben werden. Öffnen Sie die Datei ~/.bashrc, d.h. die Datei .bashrc
im Heimatverzeichnis des eingeloggten Benutzers, in einem Texteditor (z.B.
VS Code oder Notepad) und ergänzen Sie folgende Zeile:
alias mysql="winpty mysql"

Starten Sie nach dem Speichern die Git Bash neu. Bei der Ausführung des
­Befehls mysql führen Sie fortan in Wirklichkeit den »Alias«-Befehl winpty
mysql aus, ohne sich jedes Mal daran erinnern zu müssen. Das Voranstellen
des Befehls winpty behebt das Problem mit interaktiven Abfragen. Der My­
SQL-Client ist bereit:
> mysql --version
mysql Ver 8.0.27 for Win64 ...

237
Kapitel 11 Datenverwaltung mit MySQL

Der mysql-Befehl funktioniert natürlich auch in der Windows-eige-


nen (aber weniger komfortablen) »Eingabeaufforderung«.
Um PHP mit MySQL verbinden zu können, aktivieren Sie in der php.ini die
beiden Erweiterungen mysqli und pdo_mysql:
extension=mysqli
extension=pdo_mysql

macOS
Installieren Sie MySQL mit Hilfe von Homebrew:
> brew install mysql

Beachten Sie die Anweisungen am Ende der Installation: Der MySQL-Server


ist zwar installiert, aber noch nicht gestartet. Um den MySQL-Server zu star-
ten und auch nach jedem Neustart des Computers automatisch zu starten,
führen Sie folgenden Befehl aus:
> brew services start mysql

Zur Absicherung der Installation führen Sie folgenden Befehl aus:


> mysql_secure_installation

Die vorgeschlagene Komponente zur Passwortvalidierung müssen Sie nicht


installieren. Vergeben Sie auf jeden Fall ein Passwort für den »root«-Benutzer
– er besitzt alle Rechte zur Verwaltung des MySQL-Systems. Alle übrigen Fra-
gen können Sie wie vorgegeben beantworten. Der mysql-Befehl ist anschlie-
ßend auf der Kommandozeile bereit:
> mysql --version
mysql Ver 8.0.27 ...

Linux
Diese Anleitung beschreibt die MySQL-Installation unter Ubuntu-Linux und
sollte auf andere Linux-Distributionen übertragbar sein, die ebenfalls den
APT-Paketmanager verwenden (Debian, Mint u.a.). Unter Ubuntu können
Sie MySQL mit dem Befehl apt installieren (wie zuvor PHP, siehe Abschnitt
1.7.3):

238
Erste Schritte mit MySQL 11.4

> sudo apt update


> sudo apt install mysql-server

Führen Sie anschließend zur Absicherung der Installation diesen Befehl aus:
> sudo mysql_secure_installation

Die vorgeschlagene Komponente zur Passwortvalidierung (»Validate Pass-


word Component«) müssen Sie nicht installieren. Vergeben Sie auf jeden Fall
ein Passwort für den »root«-Benutzer des MySQL-Systems – er besitzt alle
Rechte zur Verwaltung des Systems. Die übrigen Fragen (Entfernung anony-
mer Benutzer, Verbot externer root-Logins, Entfernung von Test-Datenban-
ken und Neu-Laden der Benutzertabellen) können Sie bejahen. Anschließend
ist der mysql-Befehl auf der Kommandozeile bereit:
> mysql --version
mysql Ver 8.0.27 ...

Um PHP mit MySQL verbinden zu können (Kapitel 12 und 13), installieren


Sie zusätzlich das Paket php-mysql:
> sudo apt install php-mysql

Im Anschluss können Sie prüfen, ob die PHP-Erweiterungen mysqli und pdo_


mysql zur Verfügung stehen:

> php -m
... mysqli ... pdo_mysql ...

11.4.3 Verbindung zu MySQL aufnehmen und erste Befehle


ausführen
Der mysql-Befehl ist ein Client-Programm zur Kommunikation mit einem
MySQL-Server mittels SQL-Befehlen. Das help-Flag zeigt eine Übersicht aller
Funktionen:
> mysql --help

SQL-Befehle werden in einem interaktiven Modus abgesetzt – der MySQL-


Konsole. Dabei nehmen Sie mit dem mysql-Befehl zunächst eine Verbindung
zum MySQL-Server auf und authentifizieren sich mit Benutzernamen und
Passwort. In der lokalen Entwicklungsumgebung können Sie den bei der In-
stallation eingerichteten »root«-Benutzer verwenden, der uneingeschränkte

239
Kapitel 11 Datenverwaltung mit MySQL

Zugriffsrechte genießt und beliebig Datenbanken, Tabellen, Daten und wei-


tere Benutzer anlegen, ändern oder löschen kann. Eine Produktivumgebung
sollte separate Benutzer einsetzen, die nur mit notwendigen Zugriffsrechten
ausgestattet sind, um im Falle einer Sicherheitslücke mögliche Schäden zu be-
grenzen.
Folgender Befehl baut eine Verbindung mit dem MySQL-Server auf Ihrem
Computer unter Verwendung des root-Benutzers auf und startet die MySQL-
Konsole. Das Passwort wird interaktiv abgefragt:
> mysql --user=root --password
Enter password: s3cr3t
Welcome to the MySQL monitor. Commands end with ;
Server version: 8.0.27
Type 'help;' for help.
mysql> SQL-Befehl;

Der Prompt mysql> signalisiert den interaktiven Modus der MySQL-Konsole;


dort können Sie direkt SQL-Befehle schreiben und ausführen. SQL-Befehle
schließen mit einem Semikolon (;) ab. Folgender SQL-Befehl listet die vor-
handenen Datenbanken auf:
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+

Die angezeigten Datenbanken sind vorinstalliert, MySQL verwendet sie für


interne Zwecke bzw. um Auskünfte über das System zu geben. Diesen Daten-
banken müssen Sie vorerst keine Beachtung schenken.
Wie auf der Kommandozeile des Betriebssystems können Sie in der
MySQL-Konsole mit (½) und (¼) durch die Befehls-Historie navi-
gieren, um Tipparbeit zu sparen.
Die MySQL-Konsole verlassen Sie mit dem Steuer-Befehl quit (nicht Teil von
SQL, kein Semikolon nötig):

240
Datenbanken 11.6

mysql> quit
Bye

Aufgabe 1

Wie verbinden Sie sich mit dem mysql-Befehl mit einem MySQL-Server auf
einem anderen Computer (Host)?
Tipp: Sie benötigen zusätzlich eine Option für den Host.

11.5 Kommentare
Wie Programmiersprachen unterstützt SQL Kommentare, die nicht Teil eines
Befehls sind, sondern als Verständnishilfe dienen. Kommentare bis zum Zei-
lenende werden in MySQL mit zwei Bindestrichen (--) oder einer Raute (#)
notiert:
CREATE DATABASE shop; -- Kommentar bis Zeilenende
CREATE DATABASE shop; # Kommentar bis Zeilenende

Kommentare an beliebiger Stelle im SQL-Code beginnen mit /* und enden


mit */:
CREATE DATABASE /* Eingebetteter Kommentar,
auch mehrzeilig */ shop;

Hinweis

Die im Kapitel vorgestellten SQL-Befehle finden Sie in den Code-Down-


loads zu diesem Kapitel unter www.mitp.de/0395 zusammengefasst und
kommentiert in der Datei commands.sql.

11.6 Datenbanken
Datenbanken sind Container für Tabellen. Vor dem Erstellen einer Tabelle ist
folglich eine Datenbank erforderlich.

241
Kapitel 11 Datenverwaltung mit MySQL

11.6.1 Datenbank erzeugen


Die Datenbank company soll einer fiktiven Personal-Software zur Verwaltung
von Firmenangestellten dienen. Der SQL-Befehl CREATE DATABASE legt die Da-
tenbank an:
mysql> CREATE DATABASE company;

Die angelegte Datenbank erscheint in der Übersicht aller Datenbanken mit


SHOW DATABASES:

mysql> SHOW DATABASES;


+--------------------+
| Database |
+--------------------+
| company |
| … |
+--------------------+

Um Befehle innerhalb der neuen Datenbank auszuführen, wählt man sie zu-
nächst mit dem Befehl USE aus:
mysql> USE company;
Database changed

Alle folgenden SQL-Befehle beziehen sich auf die ausgewählte Datenbank.


Der Befehl SHOW TABLES listet die enthaltenen Tabellen auf:
mysql> SHOW TABLES;
Empty set (0,12 sec)

Die Liste bleibt leer, da noch keine Tabellen angelegt wurden.

11.6.2 Datenbank löschen


Das Löschen einer Datenbank erfolgt mit dem Befehl DROP DATABASE (to drop,
engl. für fallen lassen bzw. löschen):
mysql> DROP DATABASE company;

Dabei gehen alle enthaltenen Tabellen und deren Daten ohne Rückfrage ver-
loren!

242
Tabellen 11.7

11.7 Tabellen
Dieser Abschnitt setzt das Beispiel einer Datenbank company für eine fiktive
Personal-Software zur Verwaltung von Firmenangestellten aus dem vorigen
Abschnitt fort:
mysql> CREATE DATABASE company;
mysql> USE company;

11.7.1 Tabelle anlegen


Innerhalb einer Datenbank erzeugt der Befehl CREATE TABLE eine Tabelle. Fol-
gender Befehl legt in der Datenbank company die Tabelle employee mit einer
einzelnen Spalte name zur Speicherung des Namens eines Angestellten an:
mysql> CREATE TABLE employee (
name VARCHAR(100)
);
mysql> SHOW TABLES;
+-------------------+
| Tables_in_company |
+-------------------+
| employee |
+-------------------+

Ein Befehl kann sich über mehrere Zeilen erstrecken, erst nach Eingabe ei-
nes Semikolons wird der Befehl abgeschickt. Die Definition einer Tabelle folgt
dem allgemeinen Schema:
CREATE TABLE Tabellenname (
Spaltename1 Spaltentyp Optionen,
Spaltename2 Spaltentyp Optionen,
...
);

11.7.2 Spaltentypen
Neben dem Spaltennamen muss für jede Spalte ein Spaltentyp festgelegt wer-
den, dem die später eingefügten Daten entsprechen müssen. Einige gängige
Spaltentypen lauten:

243
Kapitel 11 Datenverwaltung mit MySQL

Spaltentyp Beschreibung
CHAR(N) Zeichenkette fester Länge N (bis 255 Zeichen)
VARCHAR(N) Zeichenkette variabler Länge N (bis 65.535 Zeichen bzw. 64KB)
TEXT Zeichenkette bis max. 64KB, ohne Längenangabe
LONGTEXT Zeichenkette bis max. 4GB, ohne Längenangabe
TINYINT Ganzzahl zwischen -128 bis 127 oder vorzeichenlos (»unsigned«)
von 0 bis 255
INT Ganzzahl zwischen -2147483648 bis 2147483647 oder vorzei-
chenlos von 0 bis 4294967295
FLOAT(M, D) Fließkommazahl mit M Ziffern, davon D Dezimalstellen. Für wis-
senschaftliche Daten geeignet
DECIMAL(M, D) Festkommazahl mit M Ziffern, davon D Dezimalstellen. Geeignet
zur Speicherung exakter Werte, z.B. Geldbeträge
DATE Datum im Format YYYY-MM-DD, z.B. 2023-05-27

TIME Uhrzeit im Format hh:mm:ss, z.B. 14:33:09

DATETIME Datum im Format YYYY-MM-DD hh:mm:ss, z.B. 2023-05-27


14:33:09
ENUM Wert aus fester Menge, z.B. Geschlecht: männlich, weiblich,
­divers
Tab. 11.2: Wichtige Spaltentypen in MySQL

Wozu gibt es verschiedene Spaltentypen für den scheinbar gleichen Zweck,


etwa CHAR, VARCHAR und TEXT für Zeichenketten? Je nach Typ kann das Daten-
banksystem die Speicherung und die Geschwindigkeit bei Abfragen optimie-
ren. Als Faustregel kann immer der Spaltentyp gewählt werden, der dem Ver-
wendungszweck am ehesten entspricht. Soll eine Anwendung beispielsweise
Seriennummern der fixen Länge 8 abspeichern, nach denen häufig gesucht
oder sortiert wird, ist CHAR(8) optimal. Für variable Texte von Zeitungsarti-
keln mit unbekannter Länge ist hingegen LONGTEXT die richtige Wahl.

11.7.3 Spaltenoptionen
Abhängig vom Spaltentyp können verschiedene Optionen eingestellt werden.

244
Tabellen 11.7

Optionale Werte: NULL oder NOT NULL


Standardmäßig ist eine Spalte nullable: Neben Werten, die dem Spaltentyp
entsprechen, darf die Spalte auch den Wert NULL enthalten. Wie in PHP drückt
NULL aus, dass keine Angabe vorliegt, weil es sich z.B. um eine optionale An-
gabe handelt. Eine Personal-Software würde sicherstellen, dass Name, Gehalt
und Startdatum eines Angestellten nicht optional sind (NOT NULL). Das End-
datum des Vertrags bei einem möglichen Ausscheiden aus dem Unternehmen
ist jedoch optional (NULL):
mysql> CREATE TABLE employee (
name VARCHAR(100) NOT NULL,
salary INT NOT NULL,
contract_start DATE NOT NULL,
contract_end DATE NULL
);

Zahlen mit oder ohne Vorzeichen: UNSIGNED


Zahlenspalten unterstützen standardmäßig negative (»vorzeichenbehaftete«)
Zahlen. Die Option UNSIGNED erzwingt vorzeichenlose Zahlen zum Ausschluss
negativer Zahlen, wie im Beispiel für Spalten zur Speicherung von Gehalt und
Wochenstunden:
mysql> CREATE TABLE employee (
...,
salary INT UNSIGNED NOT NULL
weekly_hours TINYINT UNSIGNED NOT NULL
);

Für Wochenstunden ist der Einsatz des kleinstmöglichen Zahlentyps TINYINT


ausreichend.

Vorgabewerte: DEFAULT
Die Option DEFAULT definiert einen Vorgabewert für eine Spalte. Falls beim
Einfügen eines Datensatzes keine explizite Angabe eines Werts für diese Spal-
te erfolgt, wird der Vorgabewert verwendet. Die Wochenarbeitszeit könnte
mit den üblichen 40 Stunden vorbelegt sein:
mysql> CREATE TABLE employee (
...,

245
Kapitel 11 Datenverwaltung mit MySQL

weekly_hours TINYINT UNSIGNED NOT NULL DEFAULT 40


);

Primärschlüssel: PRIMARY KEY


Beim Umgang mit Datensätzen in einer Tabelle entsteht das Bedürfnis, einen
einzelnen Datensatz eindeutig zu identifizieren, z.B. um ihn abzufragen, zu
verändern oder zu löschen. In der Tabelle für Angestellte gibt es bisher kein
Merkmal zur sicheren Identifizierung eines einzelnen Angestellten. Namen
könnten mehrfach auftauchen, auch eine Kombination mit dem Geburtsda-
tum wäre nicht sicher genug und umständlich.
Zur eindeutigen Identifizierung eines Datensatzes kommt folgendes Mittel
zum Einsatz: Je Datensatz vergibt man eine »ID« (engl. Abk. für identifier),
d.h. ein Merkmal, das unter allen Datensätzen in der Tabelle einmalig ist und
sich nie mehr ändert. Ein solcher Wert zur Identifizierung eines Datensatzes
heißt Primärschlüssel (engl. primary key). Eine beliebte Form für einen Pri-
märschlüssel ist die Durchnummerierung der Datensätze: 1, 2, 3, 4, 5 usw.
In der Angestellten-Tabelle kann der Primärschlüssel als ganzzahlige »Mitar-
beiter-Nummer« abgebildet und mit der Option PRIMARY KEY gekennzeichnet
werden:
mysql> CREATE TABLE employee (
employee_id INT UNSIGNED PRIMARY KEY,
...,
);

Eine Primärschlüssel-Spalte schließt den Wert NULL automatisch aus. Der Ver-
such, einen vorhandenen Wert erneut einzufügen, führt zu einem Fehler.

Automatische Primärschlüssel mit AUTO_INCREMENT


Beim Einfügen eines neuen Datensatzes wäre es mühselig herauszufinden,
welcher Wert als nächster Primärschlüssel in Frage kommt. MySQL bietet mit
der Spaltenoption AUTO_INCREMENT eine praktische Hilfe zur automatischen
Durchnummerierung von Datensätzen:
mysql> CREATE TABLE employee (
employee_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
...,
);

246
Bezeichner 11.8

Beginnend bei eins wird der Wert der Spalte mit jedem neu eingefügten Da-
tensatz automatisch um eins hochgezählt (1, 2, 3, …).

11.7.4 Tabellenstruktur anzeigen


Der SQL-Befehl DESCRIBE beschreibt die Struktur einer bestehenden Tabelle.
mysql> DESCRIBE employee;

Abbildung 11.7 zeigt die Erstellung und Beschreibung der bisher im Kapitel
entwickelten employee-Tabelle.

Abb. 11.7: Tabellenbeschreibung mit DESCRIBE

11.7.5 Tabellen löschen


Der SQL-Befehl DROP TABLE löscht eine Tabelle. Ohne Rückfrage gehen dabei
alle enthaltenen Daten verloren.
mysql> DROP TABLE employee;

11.8 Bezeichner
Bezeichner sind die selbstgewählten Namen für Datenstrukturen und deren
Komponenten, d.h. die Namen für Datenbanken, Tabellen oder Spalten.

247
Kapitel 11 Datenverwaltung mit MySQL

11.8.1 Gültige Bezeichner


Wie Variablennamen in PHP unterliegen Bezeichner Beschränkungen. Er-
laubt sind Zahlen, Buchstaben, der Unterstrich (_) und das Dollarzeichen ($).
Ausgeschlossen ist jedoch etwa der Binderstrich (-). Auch reservierte Wörter
der SQL-Sprache wie table sind unzulässig.
CREATE TABLE order (...); -- Gültig
CREATE TABLE order-items (...); -- Ungültig: Bindestrich
CREATE TABLE order_items (...); -- Gültig
CREATE TABLE table (...); -- Ungültig: Wort der SQL-Sprache

Durch Einschluss eines Bezeichners in Backticks (`...`) können beliebige


Zeichen verwendet werden:
CREATE TABLE `order-items`; -- Gültig
CREATE TABLE `table`; -- Gültig

11.8.2 Punkt-Notation
Die Punkt-Notation drückt die Zugehörigkeit einer Tabelle zu einer Daten-
bank aus und ermöglicht den direkten Zugriff auf eine Tabelle ohne vorherige
Auswahl der Datenbank mit dem USE-Befehl. Anstelle ...
USE company; DESCRIBE employee;

... geht es auch direkt:


DESCRIBE company.employee;

Backticks sind für jeden Bezeichner ggf. separat anzuwenden:


DESCRIBE `e-shop`.`order-items`;

11.9 Daten schreiben, lesen, ändern und löschen


SQL-Befehle der Kategorie »Data Manipulation Language« (DML) schreiben,
lesen, ändern oder löschen Daten in Tabellen. Die Beispiele in diesem Ab-
schnitt basieren auf der zuvor im Kapitel entwickelten Tabelle employee:

248
Daten schreiben, lesen, ändern und löschen 11.9

Abb. 11.8: Beschreibung der Tabelle employee

11.9.1 Daten einfügen: INSERT INTO


Der SQL-Befehl INSERT INTO fügt einen Datensatz nach folgendem allgemei-
nen Schema in eine Tabelle ein:
INSERT INTO Tabelle (Spalte1, Spalte2, ...)
VALUES (Wert1, Wert2, ...);

Für alle Spalten, die nicht »nullable« oder mit einem Vorgabewert belegt sind,
müssen zwingend Werte angegeben werden.
In der employee-Tabelle nummeriert MySQL die Primärschlüsselspalte em­
ployee_id automatisch (auto increment). Die Spalte contract_end erlaubt
NULL, weekly_hours hat den Vorgabewert 40 – beide sind daher optional. Fol-
gende Befehle fügen zwei Datensätze ein:
INSERT INTO employee (name, salary, contract_start)
VALUES ('Max Becker', 55000, '2018-03-15');
INSERT INTO employee (name, salary, contract_start)
VALUES ('Lea Klein', 59000, '2019-04-01');

Zeichenketten und Datumswerte müssen in Anführungszeichen gesetzt wer-


den, Zahlenwerte können darauf verzichten.
Es ist auch möglich, mehrere Datensätze in einem einzigen INSERT INTO Be-
fehl einzufügen, d.h. ohne Wiederholung der Spaltenangaben:
INSERT INTO employee (name, salary, contract_start)
VALUES ('Fritz Kuhn', 59000, '2022-04-01'),
('Jana Hocke', 60000, '2022-07-01'),
('Knut Huber', 54000, '2022-10-15');

249
Kapitel 11 Datenverwaltung mit MySQL

11.9.2 Daten abfragen: SELECT


Die Abfrage von Daten erfolgt mit dem SELECT-Befehl. In seiner einfachsten
Form nennt der Befehl nur die Spalte(n), die aus einer Tabelle abgerufen
werden sollen:
SELECT Spalte(n) FROM Tabelle;

Der Befehl zum Abruf der IDs und Namen aller Angestellten aus der Tabelle
employee lautet:

mysql> SELECT employee_id, name FROM employee;


+-------------+------------+
| employee_id | name |
+-------------+------------+
| 1 | Max Becker |
| 2 | Lea Klein |
| 3 | Fritz Kuhn |
| 4 | Jana Hocke |
| 5 | Knut Huber |
+-------------+------------+

Daten aller Spalten abrufen


Zum Abruf aller Spalten muss nicht jede Spalte einzeln aufgezählt werden, das
Sternchen-Symbol (»Asterisk«) steht stellvertretend für »alle Spalten«:
SELECT * FROM employee;

Abb. 11.9: Abfrage aller Daten aus der Tabelle employee

250
Daten schreiben, lesen, ändern und löschen 11.9

SELECT-Befehle können um Klauseln erweitert werden, um die abgefragte Da-


tenmenge zu beeinflussen. Die folgenden Abschnitte zeigen einfache Klauseln
zur Sortierung und Filterung abgefragter Daten.

Ergebnis sortieren
Die angehängte ORDER BY-Klausel sortiert Datensätze nach einer gewünschten
Spalte und Reihenfolge:

SELECT Spalte(n) FROM Tabelle


ORDER BY Spalte Reihenfolge;

Reihenfolge kann folgende Werte annehmen:


ƒ ASC für aufsteigend (engl. ascending)
ƒ DESC für absteigend (engl. descending)
Folgender Befehl listet die Angestellten aus der employee-Tabelle in umge-
kehrter Reihenfolge ihres Vertragsbeginns auf, d.h. neuere Angestellte zuerst:
SELECT * FROM employee
ORDER BY contract_start DESC;

Ergebnis filtern
Die WHERE-Klausel schränkt die abgefragten Datensätze eines SELECT-Befehls
auf bestimmte Datensätze ein:
SELECT Spalte(n) FROM Tabelle WHERE Bedingung;

Folgende WHERE-Klausel schränkt die Ergebnismenge auf einen Angestellten


mit einer bestimmten ID ein:
SELECT employee_id, name FROM employee
WHERE employee_id = 2;

11.9.3 Daten ändern: UPDATE


Der UPDATE-Befehl verändert bestehende Datensätze. Ausgewählten Spalten
können dabei neue Werte zugewiesen werden:
UPDATE Tabelle
SET Spalte1 = Wert, Spalte2 = Wert, ...;

251
Kapitel 11 Datenverwaltung mit MySQL

Ohne Einschränkung durch eine WHERE-Klausel wirkt sich der UPDATE-Befehl


auf alle Datensätze aus. Folgender Befehl setzt die Wochenstunden aller An-
gestellten in der employee-Tabelle auf 38 Stunden:
UPDATE employee SET weekly_hours = 38;

Bestehende Spaltenwerte können in die Berechnung eines neuen Werts einbe-


zogen werden. Folgender Befehl erhöht das Gehalt aller Angestellten um 1000:
UPDATE employee SET salary = salary + 1000;

Die Ausführung des Befehls ähnelt der Schleife eines Programms, sie iteriert
über alle Datensätze und aktualisiert jeweils die Spalte salary. Der zugewiese-
ne Wert salary+1000 verwendet den Spaltennamen wie eine Variable, die bis
zum Abschluss der Aktualisierung noch den unveränderten, aktuellen Wert
des Datensatzes enthält. Für einen Datensatz mit zuvor salary=60000 löst sich
die SET-Zuweisung auf zu: salary=61000.
Eine WHERE-Klausel schränkt Änderungen auf bestimmte Datensätze ein. Die
Kündigung des Angestellten mit der ID 3 wird durch Setzen eines Datums für
das Vertragsende erfasst:
UPDATE employee
SET contract_end = '2024-12-31' WHERE id = 3;

11.9.4 Daten löschen: DELETE


Der DELETE-Befehl löscht Datensätze aus einer Tabelle:
DELETE FROM Tabelle;

Ohne Einschränkungen durch eine WHERE-Klausel werden alle Datensätze ge-


löscht, wie hier aus der employee-Tabelle:
DELETE FROM employee;

Mit WHERE-Klausel werden Datensätze selektiv gelöscht, wie hier der Angestell-
te mit einer bestimmten ID:
DELETE FROM employee WHERE employee_id = 4;

Oder alle Angestellten deren Vertrag vor einem bestimmten Datum endete:
DELETE FROM employee
WHERE contract_end < '2020-01-01';

252
Übungen 11.10

Aufgabe 2

Auch der SQL-Befehl TRUNCATE TABLE löscht Tabellendaten. Finden Sie


durch eine Recherche den Unterschied zu DELETE?

11.10 Übungen
Übung 1
Erweitern Sie die Definition der Tabelle company.employee mit sinnvoll de-
finierten Spalten zur Speicherung von Geburtsdatum, Postleitzahl und Ge-
schlecht.
ƒ Legen Sie die Tabelle auf Ihrem lokalen MySQL-Server an.
ƒ Fügen Sie einige Beispiel-Datensätze ein.
ƒ Kontrollieren Sie durch Abfrage aller Datensätze.
Übung 2
Erweitern Sie die Datenabfrage der Tabelle aus Übung 1:
ƒ Sortieren Sie absteigend nach Gehalt.
ƒ Schränken Sie auf ein Gehalt >= 60000 ein.
ƒ Kombinieren Sie die Sortierung mit der Einschränkung. Finden Sie die Rei-
henfolge der Klauseln heraus.

Übung 3
ƒ Erhöhen Sie alle Gehälter in der Tabelle aus Übung 1 um 10 %. Prüfen Sie
durch Abfrage.

253
Fortgeschrittene Daten­
bankabfragen mit MySQL
Im vorigen Kapitel haben Sie die Grundlagen des relationalen Datenbankma-
nagementsystems MySQL und der Datenbanksprache SQL kennengelernt.
Die Eingabe von SQL-Befehlen erfolgte etwas mühsam über die Konsole und
die Möglichkeiten zur Filterung von Datensätzen waren begrenzt. Dieses Ka-
pitel beschäftigt sich mit fortgeschrittenen Themen und zeigt:
ƒ die Datenbankverwaltung mit Hilfe einer grafischen Benutzeroberfläche
am Beispiel von phpMyAdmin
ƒ den Zugriff auf eine Datenbank beim Webhoster
ƒ fortgeschrittene Filtermöglichkeiten zur Datenabfrage
ƒ Einsatz von SQL-Funktionen
ƒ Erstellung von Indizes für schnellere Abfragen

12.1 Grafische Datenbankverwaltung mit


phpMyAdmin
Die Eingabe von SQL-Befehlen über die MySQL-Konsole sorgt für ein sehr
gutes Grundverständnis. Insbesondere bei der Anbindung aus einem PHP-
Skript heraus (Kapitel 13) müssen SQL-Befehle ebenfalls selbst formuliert
werden. Bei der manuellen Datenbankverwaltung ist die Eingabe der SQL-
Befehle auf Dauer jedoch mühsam. Daher gibt es eine Vielzahl an kostenlosen
und kostenpflichtigen grafischen Verwaltungsprogrammen für MySQL. Ne-
ben Desktop-Programmen (z.B. MySQL Workbench, Navicat oder TablePlus)
gibt es mit phpMyAdmin eine sehr beliebte browserbasierte und kostenfreie
Alternative.
phpMyAdmin besteht aus einem Verzeichnis mit PHP-Skripten, das zur Ins-
tallation auf jeden Webserver mit PHP-Unterstützung kopiert werden kann.

255
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

Laden Sie für die lokale Verwendung die aktuellste Version von phpMyAdmin
als ZIP-Archiv von www.phpmyadmin.net/downloads herunter. Abhängig von
der Version lautet der Dateiname etwa phpMyAdmin-5.1.1-all-languages.zip.
Entpacken Sie das Archiv z.B. in Ihr Benutzerverzeichnis und benennen Sie
es der Einfachheit halber in phpMyAdmin um. Öffnen Sie das Verzeichnis in der
Kommandozeile und starten Sie einen Webserver.

phpMyAdmin benötigt die aktivierte mysqli PHP-Erweiterung, um sich


mit MySQL verbinden zu können, siehe Abschnitt 11.4.2.
Verwenden Sie einen anderen Port als bei der Webseiten-Entwicklung (z.B.
8001) damit phpMyAdmin parallel und unabhängig laufen kann:
> cd ~/phpMyAdmin
> php -S localhost:8001

Rufen Sie phpMyAdmin unter http://localhost:8001 auf. Melden Sie sich


mit dem root-Benutzer an. Im schmalen, linken Bereich sehen Sie immer eine
Übersicht der vorhandenen Datenbanken. Mit den Plus-Symbolen können Sie
schnell die Tabellen und deren Strukturen ausklappen und einsehen. Der gro-
ße rechte Bereich ist kontextabhängig.
Mit der Wiederholung der manuellen Operationen aus dem letzten Kapitel
(Datenbank und Tabelle anlegen, Datensätze einfügen und abfragen) werden
Sie schnell mit den Grundlagen vertraut. Mit der Schaltfläche Neu über der
Datenbankliste legen Sie eine neue Datenbank an:

Abb. 12.1: Datenbank mit phpMyAdmin anlegen

256
Grafische Datenbankverwaltung mit phpMyAdmin 12.1

Im Anschluss ist die neue Datenbank vorausgewählt und Sie können die erste
Tabelle darin erzeugen:

Abb. 12.2: Tabelle mit phpMyAdmin anlegen

Nach dem Speichern fügen Sie über den Reiter Einfügen neue Datensätze
ein:

Abb. 12.3: Datensatz mit phpMyAdmin einfügen

Über den Reiter Anzeigen erhalten Sie eine Übersicht aller Datensätze (ggf.
über mehrere Seiten verteilt) und einige Funktionen wie Sortierung oder Da-
tenexport:

257
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

Abb. 12.4: Datensätze mit phpMyAdmin anzeigen

Über Suche filtern Sie Datensätze nach einfachen Kriterien:

Abb. 12.5: Datensätze mit phpMyAdmin filtern

258
MySQL beim Webhoster 12.2

Weitere interessante Funktionen in phpMyAdmin:


ƒ SQL: Ausführung von SQL-Befehlen mit Unterstützung eines SQL-Editors.
ƒ Exportieren: Datensätze exportieren, z.B. als CSV, XML oder SQL. Geeig-
net für Backups oder um Daten zu teilen.
ƒ Importieren: Datensätze importieren, z.B. aus CSV, XML oder SQL.
ƒ Operationen: Tabelle umbenennen, kopieren, verschieben, leeren, lö-
schen.

12.2 MySQL beim Webhoster


In einem Webhosting-Angebot ist MySQL bereits fertig eingerichtet. Mehrere
Kunden teilen sich einen Datenbankserver und bekommen fest zugewiesene
Datenbanken und Benutzer mit passenden Zugriffsrechten. In den Einstel-
lungen finden Sie Zugangsdaten aus Benutzernamen, Passwort und Daten-
bankname.

Abb. 12.6: MySQL-Zugangsdaten beim Webhoster (Beispiel)

Die Zugangsdaten können zur Verbindung per MySQL-Konsole oder einem


grafischen Verwaltungsprogramm vom lokalen Rechner aus verwendet wer-
den. Ebenso ist es möglich, eine phpMyAdmin-Installation per FTP in den
Webspace zu kopieren, mit den Benutzerdaten zu konfigurieren und die Da-
tenbank browserbasiert von überall aus zu verwalten. Viele Webhoster stellen
auch eine feste phpMyAdmin-Installation bereit.

259
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

12.3 Fortgeschrittene Datenabfragen


Einfache Filtermöglichkeiten mit der WHERE-Klausel kennen Sie aus dem vo-
rigen Kapitel, z.B. um einen einzelnen Datensatz anhand seiner ID zu finden:
SELECT * FROM employee WHERE employee_id = 3;

Verallgemeinert erlaubt die WHERE-Klausel eine Filterung von Datensätzen


nach dem Schema:
SELECT * FROM Tabelle WHERE Bedingung;

In Gedanken: »Wähle Datensätze aus Tabelle in denen Bedingung erfüllt ist«.


Dieser Abschnitt stellt die Formulierung von Filter-Bedingungen vor. Die Bei-
spiele basieren auf einer movie-Tabelle mit Daten zu Kinofilmen:

Abb. 12.7: Tabelle movie mit Daten zu Kinofilmen

ƒ Die Skala der Bewertung (rating) reicht von 1 bis 10.


ƒ Die Film-Website (website) ist optional, d.h. »nullable«.
Hinweis

Die SQL-Datei movie.sql aus den Code-Downloads zu diesem Kapitel


(www.mitp.de/0395) enthält alle SQL-Befehle zur Anlage der in den Beispie-
len verwendeten Tabelle movie inklusive Beispiel-Datensätzen. Siehe dazu
auch Übung 1 in diesem Kapitel.

260
Fortgeschrittene Datenabfragen 12.3

12.3.1 Vergleichsoperatoren
Bei der Abfrage mit einer WHERE-Klausel gleicht MySQL Datensatz für Daten-
satz ab, ob die Bedingungen erfüllt sind und der Datensatz Teil der Ergebnis-
menge wird. Tabelle 12.1 zeigt Vergleichsoperatoren zur Formulierung von
Bedingungen.

Operator Bedeutung Beispiel


= Gleich … WHERE movie_id = 3
!= oder <> Ungleich … WHERE genre != 'Action'
> Größer als … WHERE rating > 8
>= Größer gleich … WHERE rating >= 5
< Kleiner als … WHERE length < '2:00'
<= Kleiner gleich … WHERE length <= '2:00'
BETWEEN Bereich (inklusiv) … WHERE rating BETWEEN 3 AND 7
LIKE Suchmuster … WHERE title LIKE '%007%'
IN Ein Wert aus Liste … WHERE genre IN ('Action', 'Drama')
IS NULL Gleich NULL … WHERE website IS NULL
IS NOT NULL Ungleich NULL … WHERE website IS NOT NULL

Tab. 12.1: SQL-Vergleichsoperatoren

Anführungszeichen
Zeichenketten und Datumsangaben werden in einfachen Anführungszeichen
eingeschlossen, für Zahlen ist dies nicht erforderlich:
... WHERE genre = 'action';
... WHERE id = 3;

Zur Notation von Zeichenketten unterstützt MySQL sowohl einfache als auch
doppelte Anführungszeichen:
... WHERE genre = 'action'; -- SQL-Standard
... WHERE genre = "action"; -- MySQL spezifisch

261
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

Suche mit LIKE


Der LIKE-Operator vergleicht eine Textspalte mit einem Suchmuster, welches
zwei Platzhalter (engl. wildcards) unterstützt:
ƒ der Unterstrich (_) steht für ein beliebiges einzelnes Zeichen.
ƒ das Prozentzeichen (%) steht für kein, ein oder mehrere Zeichen.
Bedingung Bedeutung Treffer Kein Treffer
LIKE '%ei%' Enthält ei Ei, Eis, Blei, Gleise Kaiser, Knie
LIKE 'ei%' Beginnt mit ei Ei, Eis Blei, Gleise
LIKE '%ei' Endet mit ei Ei, Blei, Partei Eis, Gleise
LIKE '__ei' Beginnt mit zwei beliebigen Blei, frei Ei, Eis, Gleise
Zeichen, endet mit ei
LIKE 'ei_' Beginnt mit ei, endet mit Eis, ein Blei, Gleise
einem beliebigen Zeichen
LIKE '__ei%' Beginnt mit zwei beliebigen frei, Gleise Reis, Partei
Zeichen, gefolgt von ei, en-
det mit beliebigen Zeichen
Tab. 12.2: Suchmuster mit LIKE

Vergleiche mit NULL


Vorsicht: Eine Filterung nach Datensätzen mit dem Wert NULL ist besonders.
Folgender Vergleich führt nie zu einem Treffer und funktioniert daher nicht
wie womöglich erwartet:
... WHERE website = NULL; -- liefert nie einen Treffer!

Zur Prüfung auf NULL kommen die speziellen Operatoren IS NULL bzw. IS NOT
NULL zum Einsatz. Folgende Vergleiche finden Datensätze mit bzw. ohne dem
Wert NULL in der Spalte website:
... WHERE website IS NULL;
... WHERE website IS NOT NULL;

12.3.2 Logische Verknüpfungen


Mit den logischen Operatoren AND, OR und NOT lassen sich Vergleichs-Bedin-
gungen aus dem vorigen Abschnitt zur Erfüllung komplexer Filteranforde-
rungen kombinieren.

262
Fortgeschrittene Datenabfragen 12.3

Operator Bedeutung Beispiel


AND Und … WHERE genre = 'action' AND rating > 8
OR Oder … WHERE rating > 7 OR website IS NOT NULL
NOT Verneinung … WHERE title NOT LIKE '%Avengers%'
… WHERE genre NOT IN ('action', 'drama')
… WHERE NOT genre = 'thriller'

Tab. 12.3: Logische Operatoren in SQL

Bei der Kombination mehrerer Operatoren ist die Bedeutung der gesamten
Bedingung auf den ersten Blick nicht offensichtlich:
... WHERE rating > 7 OR genre = 'thriller' AND length < 90;

Wie in der Programmierung folgt die Auflösung einer Bedingung mit mehre-
ren Operatoren nach einer festen Reihenfolge, der Operator-Präzedenz (engl.
precedence). Im Beispiel bindet AND stärker als OR, zusätzliche Klammern ver-
deutlichen dies:
... WHERE rating > 7 OR (genre = 'thriller' AND length < 90);

In Gedanken: »Filme mit einer Bewertung über 7 oder – unabhängig von der
Bewertung – Filme des Thriller-Genres mit weniger als 90 Minuten Laufzeit«.
Eine Änderung der Klammersetzung ändert das Verhalten:
... WHERE (rating > 7 OR genre = 'thriller') AND length < 90;

In Gedanken: »Filme mit weniger als 90 Minuten Laufzeit, die zusätzlich ent-
weder eine Bewertung über 7 aufweisen oder unabhängig von der Bewertung
Filme des Thriller-Genres sind«.
Um Fehlern vorzubeugen und die Lesbarkeit zu verbessern, sollten bei
der Kombination mehrerer SQL-Bedingungen immer Klammern einge-
setzt werden.
12.3.3 LIMIT: Abfrageergebnisse begrenzen
Die LIMIT-Klausel begrenzt die Anzahl der in einer Abfrage zurückgelieferten
Datensätze. Klassisches Beispiel ist die seitenweise Darstellung von Datensät-
zen aus einer großen Tabelle. Folgende Abfrage selektiert alle Datensätze der
movie-Tabelle in alphabetischer Reihenfolge:

SELECT * FROM movies ORDER BY title;

263
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

Enthält die Tabelle hunderte Datensätze, kann LIMIT die abgefragten Daten-
sätze z.B. auf zunächst 50 begrenzen:
SELECT * FROM movies ORDER BY title LIMIT 50;

Zur Abfrage der nächsten 50 Datensätze werden die ersten 50 mit Hilfe der
zusätzlichen OFFSET-Klausel (engl. für Versatz, Verschiebung) übersprungen.
Die Selektion von weiteren 50 Datensätzen beginnt nun beim 51sten Daten-
satz:
SELECT * FROM movies ORDER BY title LIMIT 50 OFFSET 50;

Die Verschiebung lässt sich immer weiter fortführen:


SELECT * FROM movies ORDER BY title LIMIT 50 OFFSET 100;

Aufgabe 1

Formulieren Sie einen SQL-Befehl zur Abfrage der fünf längsten Filme der
Tabelle movie.

12.4 Alias-Namen für Tabellenspalten


Das Schlüsselwort AS vergibt Alias-Namen für Spalten:
SELECT Spalte AS Alias, ... FROM Tabelle;

So können z.B. Spaltentitel in der Ergebnisliste angepasst werden:


> SELECT title AS 'Filmtitel', length AS 'Länge'
> FROM movies;

Das Ergebnis:
+-------------------------+----------+
| Filmtitel | Länge |
+-------------------------+----------+
| Pulp Fiction | 02:34:00 |
| Matrix | 02:16:00 |
| James Bond 007: Skyfall | 02:23:00 | ...

Aliase sind besonders bei der Arbeit mit SQL-Funktionen (nächster Ab-
schnitt) nützlich, um temporär berechneten Daten einen Namen zu geben.

264
SQL-Funktionen 12.5

12.5 SQL-Funktionen
Jeder Datensatz speichert statische Werte wie Zahlen, Zeichenketten oder
Datumsangaben. Eingebaute SQL-Funktionen können während einer Abfrage
auf diese Werte angewendet werden und dynamisch weitere Werte berechnen.
Das Prinzip erinnert an PHP-Funktionen: Die SQL-Funktion nimmt Argu-
mente entgegen, führt eine Berechnung durch und liefert einen Rückgabe-
wert:
FUNKTIONSNAME(Argument1, Argument2, ...)

Es gibt hauptsächlich zwei Gruppen von Funktionen:


ƒ Skalare Funktionen nehmen individuelle Werte (Zahl, Zeichenkette, Datum
etc.) als Argumente entgegen und erzeugen einen einzelnen Rückgabewert.
Beispiele: Rundung einer Zahl oder Länge einer Zeichenkette bestimmen.
ƒ Aggregatfunktionen fassen Daten aus mehreren Datensätzen zusammen
und erzeugen daraus einen einzelnen Rückgabewert. Beispiele: Durch-
schnittswert oder Summe einer numerischen Spalte bestimmen, Datensät-
ze zählen.

12.5.1 Skalare Funktionen


Eine skalare SQL-Funktion kann mit dem SELECT-Befehl zunächst ganz ohne
Tabellenabfrage für sich alleine aufgerufen werden, etwa zum Ausprobieren
einer Funktion. Die Funktion ROUND() z.B. rundet eine Zahl auf die angegebe-
nen Dezimalstellen:
> SELECT ROUND(1.1951, 2); -- => 1.20

Bei der Abfrage von Datensätzen können Spaltennamen wie Variablen als Ar-
gumente an eine Funktion übergeben werden. Die Funktion wird auf jeden
Datensatz des Abfrageergebnisses angewendet. Folgende Abfrage liefert ge-
rundete Geldwechselkurse in einer »virtuellen« Ergebnisspalte:
> SELECT currency, rate, ROUND(rate)
> FROM exchange_rates;
+----------+--------+----------------+
| currency | rate | ROUND(rate, 2) |
+----------+--------+----------------+
| USD | 1.1951 | 1.20 |
| GBP | 0.8595 | 0.86 |
| CHF | 1.0956 | 1.10 |

265
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

Ein Alias verbessert die Ergebnisliste:


> SELECT currency, ROUND(rate, 2) AS 'Wechselkurs'
> FROM exchange_rates;
+----------+-------------+
| currency | Wechselkurs |
+----------+-------------+
| USD | 1.20 |
| GBP | 0.86 |
| CHF | 1.10 |

Skalare Funktionen gruppieren sich in:


ƒ Zeichenketten-Funktionen.
ƒ Numerische Funktionen.
ƒ Datumsfunktionen.
Zeichenketten-Funktionen

Funktion Ergebnis Bedeutung


CONCAT('A', 'B', 'C') ABC Zeichenketten verbinden
LOWER('MySQL') mysql Umwandlung in Kleinschreibung
LENGTH('php') 3 Zeichenkettenlänge
SUBSTR('MySQL 8', 3, 3) SQL Teilstring extrahieren:
SUBSTR(String, Start, Länge)

Tab. 12.4: Beispiele für SQL-Zeichenketten-Funktionen

Beispiel: Preisspalte um ein Währungszeichen ergänzen


> SELECT name, CONCAT(price, '€') AS 'Preis'
> FROM products;
+-------------+-------+
| name | Preis |
+-------------+-------+
| Schokolade | 1.49€ |
| Müsliriegel | 0.99€ | ...

266
SQL-Funktionen 12.5

Numerische Funktionen und Operatoren

Funktionsaufruf Rückgabewert Beschreibung


CEIL(10.49) 11 Zur nächsten Ganzahl aufrunden
FLOOR(10.49) 10 Zur nächsten Ganzzahl abrunden
ROUND(10.49, 1) 10.5 Kaufmännisch runden
Tab. 12.5: Beispiele für numerische SQL-Funktionen

Neben numerischen Funktionen stehen die bekannten arithmetischen Opera-


toren: +, -, *, /, % für Berechnungen zur Verfügung.
Beispiel: 10 % Discount auf Produktpreise berechnen
> SELECT name, ROUND(price - (price*0.1), 2) AS '%Sale%'
> FROM products;
+--------------+--------+
| name | %Sale% |
+--------------+--------+
| Schokolade | 1.79 |
| Müsliriegel | 0.89 | ...

Datumsfunktionen

Funktionsaufruf Rückgabewert Beschreibung


NOW() z.B. 2022-06-28 Aktuelles
11:19:17 ­Datum
DATE_FORMAT('2022-06-28', '%d.%m.%Y') 28.06.2022 Datum
formatieren
DATE_ADD('2022-06-28', INTERVAL 14 DAY) 2022-07-12 Zeitinterval
addieren
DATE_SUB('2022-06-28', INTERVAL 10 YEAR) 2012-06-28 Zeitinterval
abziehen
QUARTER('2022-06-28') 2 Jahresquartal
Tab. 12.6: Beispiele für SQL-Datumsfunktionen

Ähnlich der PHP-Funktion date() arbeitet die SQL-Funktion DATE_FORMAT()


mit Formatierungszeichen. In SQL muss jedem Formatierungszeichen ein
Prozentzeichen vorangestellt werden.

267
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

Beispiel: Aktuelles Datum einfügen


> INSERT INTO user (username, created_at)
> VALUES ('otto', NOW());
> SELECT username, created_at
> FROM user WHERE username = 'otto';
+----------+---------------------+
| username | created_at |
+----------+---------------------+
| otto | 2022-06-28 11:19:17 |

Aufgabe 2

Formulieren Sie eine SELECT-Abfrage (ohne Tabelle), die das aktuelle Da-
tum zurückliefert.

12.5.2 Daten zusammenfassen: Aggregatfunktionen


Im Gegensatz zu skalaren Funktionen arbeiten Aggregatfunktionen nicht auf
einzelnen Datensätzen, sondern auf der Ergebnismenge selektierter Daten­
sätze.
In einer Angestellten-Tabelle employee summiert der Aufruf SUM(salary) die
Gehälter aller Datensätze zur Berechnung der Jahresausgaben für Gehälter.
Das Ergebnis ist ein einzelner Wert.
> SELECT SUM(salary) AS 'Gehaltsausgaben'
> FROM employees;
+------------------+
| Gehaltsausgaben |
+------------------+
| 1114000 |

Funktionsaufruf Beschreibung
COUNT() Anzahl der Datensätze
AVG() Durchschnittswert einer numerischen Spalte
SUM() Summe einer numerischen Spalte
Tab. 12.7: Beispiele für SQL-Aggregatfunktionen

268
Performance und Indizes 12.6

Beispiel: Durchschnittsalter aller Benutzer


> SELECT ROUND(AVG(age)) AS 'Durchschnittsalter'
> FROM user;
+--------------------+
| Durchschnittsalter |
+--------------------+
| 27 |

Beispiel: Anzahl länger als fünf Jahre registrierter Benutzer


> SELECT COUNT(*) AS '# Länger als 5J registriert' FROM user
> WHERE created < DATE_SUB(NOW(), INTERVAL 5 YEAR);
+-----------------------------+
| # Länger als 5J registriert |
+-----------------------------+
| 124 |

Die Funktion COUNT() führt keine Berechnungen mit einem Spaltenwert


durch, sondern zählt Zeilen. Daher kann sie auch »auf alle« Spalten (*) an-
gewendet werden. Die Angabe einer Spalte schließt Datensätze mit dem Wert
NULL in dieser Spalte aus:

ƒ COUNT(*): Zählt alle Zeilen.


ƒ COUNT(Spalte): Zählt Zeilen in denen Spalte nicht NULL ist.
Aufgabe 3

Formulieren Sie eine Abfrage, die alle Zeilen einer Tabelle user mit einem
Wert in der optionalen Spalte phone zählt.

12.6 Performance und Indizes


Je größer der Datenbestand, desto langsamer die Filterung von Datensätzen
mit einer WHERE-Klausel. MySQL muss jeden Datensatz einzeln mit der Bedin-
gung abgleichen (»full table scan«). Der Aufwand steigt mit der Anzahl der
Datensätze, die Geschwindigkeit (»Performance«) der Abfragen sinkt.
In der realen Welt ist dies mit einem Buch ohne Index (Stichwortverzeichnis)
vergleichbar, in dem Sie jede einzelne Seite nach dem Vorkommen eines be-

269
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

stimmten Worts absuchen müssen. Je länger das Buch, desto mehr Aufwand.
Ein Index nennt Ihnen die passenden Seitenzahlen mit geringem Suchauf-
wand.
Zur schnelleren Filterung von Datensätzen können Sie MySQL ebenfalls
durch Anlage eines Index auf bestimmte Tabellenspalten, in denen häufig ge-
sucht wird, helfen. Es gibt unterschiedliche Index-Arten:

Index-Art Bedeutung
PRIMARY KEY »Primärschlüssel« zur eindeutigen Identifizierung eines Datensat-
zes. Je Tabelle ist nur ein Primärschlüssel möglich, jeder Wert darf
nur einmal vorkommen, NULL-Werte sind nicht erlaubt.
UNIQUE »Eindeutiger Index«, jeder Wert darf nur einmal vorkommen. Ist
die Spalte »nullable«, darf der Wert NULL als einziger mehrfach
vorkommen.
INDEX Normaler »Index«, Werte können mehrfach vorkommen.
FULLTEXT »Volltext-Index«, ermöglicht eine Volltextsuche für Spalten mit
längeren Texten. Wörter werden einzeln indiziert.
Tab. 12.8: Verschiedene Index-Typen für Tabellenspalten

Indizes können bei Anlage einer Tabelle oder nachträglich definiert werden.
Für eine Tabelle mit Angestellten-Daten könnten folgende Überlegungen
plausibel sein:
ƒ Jeder Angestellte soll durch eine eindeutige ID identifizierbar sein. Jede ID
darf nur einmal vergeben sein und sich nie ändern. Häufige Abfragen per
ID sind zu erwarten. Ein »Primary Key« erfüllt die Anforderungen.
ƒ Für jeden Angestellten soll eine E-Mail gespeichert werden, die eindeutig
unter allen Angestellten ist. Sie kann sich später auch ändern. Eine häufige
Filterung nach E-Mail ist zu erwarten. Ein »Unique Key« erfüllt die Anfor-
derungen.
ƒ Eine häufige Filterung von Angestellten nach Vertragsbeginn ist zu erwar-
ten. Ein gewöhnlicher »Index« erfüllt die Anforderungen.
ƒ Die Adresse eines Angestellten dient Informationszwecken, eine Suche ist
nicht geplant. Ein Index ist nicht erforderlich.
Die passende Tabellendefinition lautet:

270
Performance und Indizes 12.6

CREATE TABLE employee (


id INT AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
contract_start DATE NOT NULL,
address VARCHAR(200) NOT NULL,
PRIMARY KEY(id),
UNIQUE KEY(email),
INDEX(contract_start)
);

In phpMyAdmin können Sie Indizes grafisch anlegen und bearbeiten:

Abb. 12.8: Indizes mit phpMyAdmin verwalten

Ein Index beschleunigt auch Abfragen mit LIKE-Suchmustern. Aus-


genommen sind Suchmuster, die mit einem Platzhalter beginnen, z.B.
email LIKE '%@gmail.com'. Für diesen Fall muss MySQL einen vollen
Scan der Tabelle durchführen. Eine performante Alternative ist die
Volltextsuche1.

1
https://dev.mysql.com/doc/refman/en/fulltext-search.html

271
Kapitel 12 Fortgeschrittene Daten­bankabfragen mit MySQL

12.7 Übungen
Übung 1
Arbeiten Sie mit phpMyAdmin:
ƒ Legen Sie die Tabelle movie aus Abschnitt 12.3 in einer Datenbank book mit
Hilfe der Datei movie.sql aus den Buchdownloads (www.mitp.de/0395) an.
Tipp: Kopieren Sie das SQL oder nutzen Sie den Datei-Import.
ƒ Selektieren Sie Filme aus den Genres Action oder Drama mit einer höheren
Bewertung als 7, neueste zuerst.
ƒ Exportieren Sie das Ergebnis als PDF.

Übung 2
Formulieren Sie weitere Abfragen auf die Tabelle movie aus Übung 1:
ƒ Geben Sie die Durchschnittsbewertung aller Filme auf eine Stelle gerundet
aus.
ƒ Listen Sie alle Filme im Format »Titel (Jahr, Länge)« auf. Formatieren Sie
die Filmlänge mit DATE_FORMAT().
ƒ Geben Sie die Anzahl der Filme aus, die vor 2012 erschienen sind. Reduzie-
ren Sie anschließend auf Filme mit Website-Link.
ƒ Finden Sie alle Filme mit dem Wort Bond im Titel.

272
PHP und MySQL
kombinieren
In den letzten beiden Kapiteln haben Sie die Verwaltung einer MySQL-Da-
tenbank mit der Datenbanksprache SQL kennengelernt. SQL-Befehle wurden
manuell oder mit phpMyAdmin erstellt und zur Ausführung an den Daten-
bankserver gesendet.
Für ein Computer-Programm ist eine Datenbank die effizienteste Option
zur Datenspeicherung. Jede Programmiersprache ermöglicht die Anbindung
verschiedener Datenbanksysteme. Dieses Kapitel erklärt die Grundlagen zur
»programmatischen« Anbindung einer MySQL-Datenbank aus einem PHP-
Skript:
ƒ Verbindung mit MySQL herstellen
ƒ SQL-Befehle ausführen
ƒ Ergebnisse von SQL-Abfragen verarbeiten
Sie lernen außerdem die sichere Speicherung von Passwörtern und eine be-
kannte Sicherheitsproblematik kennen, die »SQL-Injection«.

13.1 PHP mit MySQL verbinden


PHP verfügt über zwei Erweiterungen zur MySQL-Anbindung:
ƒ Die MySQLi-Erweiterung bietet prozeduralen und objektorientierten Zu-
griff auf MySQL.
ƒ Die PHP Data Objects-Erweiterung (PDO) bietet einen vereinheitlichten,
objektorientierten Ansatz zum Zugriff auf gut ein Dutzend verschiedene
Datenbanksysteme, darunter MySQL.
MySQLi ist die ältere der beiden Erweiterungen und kann ausschließlich
mit MySQL kommunizieren. Die PDO-Erweiterung ist eine »Abstraktions-
schicht«, um verschiedene Datenbanksysteme auf die gleiche Weise anzubin-

273
Kapitel 13 PHP und MySQL kombinieren

den. Für die meisten Anwendungsfälle ist der Einsatz der modernen PDO-
Erweiterung zu empfehlen. Der Umgang ist intuitiv, mit dem einmal erlernten
Wissen können neben MySQL auch andere Datenbanksysteme angebunden
werden.

Achten Sie auf die Aktivierung der PHP-Erweiterung pdo_mysql (siehe


Abschnitt 11.4.2).

13.1.1 Datenbankverbindung mit PDO


Die PDO-Erweiterung ist eine abstrakte Schicht zwischen PHP und verschie-
denen Datenbanksystemen, die angebotenen Klassen und Methoden sind un-
abhängig von einem konkreten Datenbanksystem. Das Vorgehen zur Verbin-
dung mit einer Datenbank ist daher auch ganz unabhängig von MySQL, die
Instanziierung eines Objekts der Klasse PDO stellt die Verbindung her:
$pdo = new PDO(Verbindungsdaten, Benutzer, Passwort);

Die Herstellung der Verbindung kann fehlschlagen, etwa bei inkorrekten Ver-
bindungsdaten, unzureichenden Zugriffsrechten oder einem nicht erreichba-
ren Datenbankserver. In einem solchen Fehlerfall wirft der Konstruktor der
PDO-Klasse eine Ausnahme vom Typ PDOException, die unbehandelt direkt
zum Abbruch des Skripts führt. Durch Fangen der Ausnahme in einem try-
catch-Konstrukt kann sinnvoll auf einen Verbindungsfehler reagiert werden.

try
$pdo = new PDO(...)
} catch (PDOException $e) {
// z.B. Log-Eintrag mit Details aus $e->getMessage()
// z.B. den Administrator per E-Mail/SMS benachrichten
// z.B. Besucher freundlich informieren und Skript beenden:
require 'friendly-error-page.php';
exit();
}
// Ab hier: Verbindung zur Datenbank ist hergestellt

13.1.2 Verbindung mit MySQL herstellen


Zur Herstellung der Verbindung mit einer konkreten Datenbank durch PDO
benötigen Sie Verbindungsdaten (Datenbanktyp, Host-Adresse, Port, Da-
tenbankname) sowie Benutzername und Passwort. Die Verbindungsdaten

274
PHP mit MySQL verbinden 13.1

werden als Zeichenkette in einem speziellen Format, dem Data Source Name
(DSN), zusammengefasst:
Datenbanktyp:host=Domain;port=Port;dbname=Datenbankname

Für MySQL lautet der Datenbanktyp mysql. Standardmäßig stellt ein MySQL-
Server seinen Dienst auf dem Port 3306 zur Verfügung. Der DSN zur Ver-
bindung mit einer MySQL-Datenbank namens book auf dem lokalen Host-
Computer localhost lautet somit:
mysql:host=localhost;port=3306;dbname=book

Das konkret angebundene Datenbanksystem äußert sich nur im DSN.


Andere Datenbanktypen sind z.B. pgsql für PostgreSQL oder oci für
Oracle.
Die Übergabe des DSN und einer passenden Kombination aus Benutzer/Pass-
wort an den PDO-Konstruktor stellt die Verbindung her:
$dsn = 'mysql:host=localhost;port=3306;dbname=book';
try {
$pdo = new PDO($dsn, 'root', 's3cr3t');
} catch (PDOException $e) {
// ggf. Maßnahmen ergreifen, z.B. Admin informieren
exit('Datenbankfehler: ' . $e->getMessage());
}
// Ab hier: Verbindung zur Datenbank ist hergestellt

Listing 13.1: Verbindung zu MySQL herstellen in _mysql-connect.php

Der Code zur Datenbankverbindung kann in die Datei _mysql-connect.php


ausgelagert werden. Skripte im Verlauf des Kapitels binden die Datei zur Her-
stellung der Verbindung ein.

13.1.3 SQL-Befehle ausführen


Nach Herstellung der Verbindung gibt es verschiedene Möglichkeiten, SQL-
Befehle durch das PDO-Objekt zur Ausführung an die Datenbank zu senden:
ƒ Die Methode PDO::exec()
ƒ Die Methode PDO::query()
ƒ Die Methode PDO::prepare() für Prepared Statements (»vorbereitete An-
weisungen«)

275
Kapitel 13 PHP und MySQL kombinieren

Die Schreibweise Klasse::Methode() (zwei Doppelpunkte!) wird in


schriftlichen Beschreibungen als Kurzform verwendet, um die Zuge-
hörigkeit einer Methode() zu einer Klasse auszudrücken. PDO::query()
beschreibt also »die Methode query() der Klasse PDO«.
Mögliche Fehler bei der Ausführung von SQL-Befehlen werfen wie der PDO-
Konstruktor ebenfalls eine Ausnahme vom Typ PDOException.

Einen SQL-Befehl mit der Methode PDO::exec() ausführen


Die Methode PDO::exec() führt einen SQL-Befehl aus und liefert die Anzahl
der betroffenen Datensätze als Ganzzahl zurück. Dies ist zur Ausführung von
Befehlen geeignet, die keine Datensätze zurückliefern, z.B. INSERT, UPDATE
oder DELETE. Folgendes Skript fügt einen Benutzer mit Benutzername und
Passwort in eine Tabelle user ein:
require __DIR__ . '/_mysql-connect.php'; // Verbindung herstellen
$affectedRows = $pdo->exec(
"INSERT INTO user (username, password)
VALUES ('jan99', 's3cr3t')"
);
echo 'Betroffene Zeilen: '. $affectedRows; // => 1

Wie in Abschnitt 11.9 beschrieben, kann INSERT auch mehrere Datensätze auf
einmal einfügen und UPDATE bzw. DELETE können sich je nach Einschränkung
durch eine WHERE-Klausel auf viele Datenätze auswirken. Entsprechend liefert
PDO::exec() die Anzahl der eingefügten, aktualisierten oder gelöschten Da-
tensätze zurück.
Datensätze mit der Methode PDO::query() abfragen
Die Methode PDO::query() führt einen SQL-Befehl aus und gibt die Ergeb-
nismenge der gefundenen Datensätze gekapselt in einem Objekt vom Typ
PDOStatement zurück. Dies ist geeignet, um SELECT-Befehle auszuführen und
auf die zurückgelieferten Datensätze zuzugreifen. Folgendes Skript listet alle
Benutzernamen aus der Tabelle user auf:
require __DIR__ . '/_mysql-connect.php';
// $stmt ist Objekt vom Typ "PDOStatement"
$stmt = $pdo->query('SELECT username FROM user');
// Ergebnismenge als Array durchlaufen
foreach ($stmt->fetchAll() as $user) {
echo $user['username']. '<br/>';
}

276
PHP mit MySQL verbinden 13.1

Die Methode PDOStatement::fetchAll() (engl. to fetch: holen) liefert die Er-


gebnismenge als Array, jedes Element repräsentiert einen Datensatz. Der Zu-
griff auf den Spaltenwert eines Datensatzes erfolgt mit dem Spaltennamen als
Array-Schlüssel.

Prepared Statements mit PDO::prepare(): Vorbereitete Anweisungen


Prepared Statements bieten eine besonders sichere und performante Art zur
Ausführung von SQL-Befehlen:
ƒ Beim Einbau dynamischer Parameter in einen SQL-Befehl schützt ein Pre-
pared Statement vor einer sogenannten »SQL-Injection«, einer gefährli-
chen Sicherheitslücke (Abschnitt 13.5).
ƒ Bei mehrfacher Ausführung des gleichen SQL-Befehls (z.B. dem Einfügen
vieler Datensätze in einer Schleife) ist ein Prepared Statement besonders
performant.
Die Methode PDO::prepare() bereitet eine SQL-Anweisung vor der eigentli-
chen Ausführung zunächst vor und liefert ein Objekt vom Typ PDOStatement
zurück. Im SQL-Code werden dynamische Parameter mit einem Fragezeichen
(?) als Platzhalter ausgedrückt. Die Ausführung des Befehls erfolgt anschlie-
ßend mit der Methode PDOStatement::execute().
Folgendes Skript fügt einen neuen Benutzer in die Tabelle user ein. Benutzer-
name und Passwort stammen z.B. aus einer Formulareingabe.
require __DIR__ . '/_mysql-connect.php';
$username = $_POST['username']; 1
$password = $_POST['password'];
// $stmt ist Objekt vom Typ "PDOStatement"
$stmt = $pdo->prepare( 2
'INSERT INTO user (username, password) VALUE (?, ?)'
);
$stmt->execute([$username, $password]); 3

ƒ 1 Dynamische Eingabedaten für Benutzername und Passwort aus unsiche-


rer Quelle, z.B. einem HTML-Formular.
ƒ 2 Vorbereitung des SQL-Befehls mit PDO::prepare(). Fragezeichen stehen
als Platzhalter für die dynamischen Daten von Benutzername und Pass-
wort. Die Methode liefert ein Objekt vom Typ PDOStatement zurück.
ƒ 3 Ausführung des SQL-Befehls mit PDOStatement::execute(). Benutzer-
name und Passwort werden in einem Array als Argument übergeben. Die

277
Kapitel 13 PHP und MySQL kombinieren

Platzhalter im SQL-Befehl werden in ihrer Reihenfolge mit den Array-Ele-


menten ersetzt.

Aufgabe 1

Im Vergleich zu PDO::query(): Wie gelangen Sie mit einem »Prepared


Statement« an die Ergebnisdatensätze einer SELECT-Abfrage? Vergleichen
Sie beide Wege.

13.2 Beispiel: MySQL-Version abfragen


Zum Test einer MySQL-Verbindung kann die Version des Datenbankservers
mit der skalaren SQL-Funktion VERSION() abgefragt werden:
require __DIR__ . '/_mysql-connect.php'; 1
$sql = "SELECT VERSION() AS 'Version'"; 2
$stmt = $pdo->query($sql); 3
$record = $stmt->fetchAll()[0]; 4
echo 'MySQL-Version: '. $record['Version']; 5
// z.B. => "8.0.27"

Listing 13.2: mysql-server-version.php

ƒ 1 Herstellung der Datenbank-Verbindung mit dem Skript aus Abschnitt


13.1.2.
ƒ 2 Die SQL-Funktion VERSION() liefert genau einen »Datensatz« mit dem
Feld Version zurück.
ƒ 3 Die PDO::query()-Methode liefert ein Objekt vom Typ PDOStatement zu-
rück, welches die Ergebnismenge enthält.
ƒ 4 PDOStatement::fetchAll() liefert die Ergebnismenge als Array mit nur
einem Element.
ƒ 5 Die abgefragte MySQL-Version befindet sich im Feld Version des Da-
tensatzes.

13.3 Beispiel: Benutzer-Accounts


Klassisches Beispiel für die Datenbankanbindung in einer PHP-Webanwen-
dung ist die Verwaltung von Benutzer-Accounts zum Login. Die Anforderun-
gen sollen lauten:

278
Beispiel: Benutzer-Accounts 13.3

ƒ Benutzer registrieren sich mit Benutzername und Passwort.


ƒ Registrierte Benutzer können sich ein- und ausloggen.
ƒ Eingeloggte Benutzer haben Zugriff auf nicht-öffentliche Inhalte.
Folgende Skripte kommen zum Einsatz:
ƒ _mysql-connect.php aus Abschnitt 13.1.2 stellt die Verbindung zu MySQL
her.
ƒ create-user-table.php legt eine Datenbank-Tabelle user zur Speicherung
von Benutzer-Accounts an.
ƒ register.php zur Registrierung eines Benutzer-Accounts.
ƒ login.php zum Benutzer-Login.
ƒ secret.php ist nur für eingeloggte Benutzer zugänglich.
Die Beispielskripte konzentrieren sich auf die Datenbank-Interaktionen.
Eine produktive Anwendung sollte Datenvalidierung und Benutzerfüh-
rung ergänzen.

Hinweis

Starten Sie zum Testen der Account-Verwaltung den Webserver mit dem
Unterverzeichnis user-accounts/ aus den Code-Downloads zum Kapitel
als Document-Root.

13.3.1 Benutzer-Tabelle anlegen


Die Speicherung der Benutzer-Accounts erfolgt in einer Datenbanktabelle
user mit folgenden Spalten und Eigenschaften:

ƒ id: Ganzzahliger Primärschlüssel (auto increment).


ƒ username: Eindeutiger Benutzername; jeder Name darf in der Tabelle nur
einmal vorkommen (Unique Key).
ƒ password: Passwort des Benutzers.
Folgendes PHP-Skript erstellt die Tabelle user:
require __DIR__ . '/_mysql-connect.php'; 1
$sql = 'CREATE TABLE `user` ( 2
`id` int AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(100) NOT NULL UNIQUE KEY,
`password` VARCHAR(100) NOT NULL
)';

279
Kapitel 13 PHP und MySQL kombinieren

try {
$pdo->exec($sql); 3
echo 'Tabelle <code>user</code> angelegt!';
} catch (PDOException $e) {
echo 'Datenbank-Fehler: ' . $e->getMessage();
}

Listing 13.3: Anlage der user-Tabelle in create-user-table.php

ƒ 1 Datenbankverbindung herstellen.
ƒ 2 SQL-Befehl zur Erstellung der Tabelle user.
ƒ 3 PDO::exec() führt den SQL-Befehl in der Datenbank aus.
Die Ausführung von SQL-Befehlen der Kategorie »Data-Definition-Lan­
guage« (DDL; siehe Tabelle 11.1) zur Erstellung von Datenstrukturen ist na-
türlich nur einmal sinnvoll. Ein erneutes Ausführen des Skripts führt zu ei-
nem Fehler, da die Tabelle user bereits existiert. DDL-Befehle sind häufig Teil
von Installations-Assistenten zur initialen Einrichtung von Datenstrukturen.

13.3.2 Benutzer registrieren


Das Skript register.php verarbeitet ein Formular zur Benutzer-Registrierung:
if (isset($_POST['username'], $_POST['password'])) { 1
require __DIR__ . '/_mysql-connect.php';
try {
$stmt = $pdo->prepare( 2
'INSERT INTO user (username, password) VALUES (?, ?)'
);
$stmt->execute([$_POST['username'], $_POST['password']]); 3
echo 'Registrierung OK, <a href="login.php">Login</a>';
} catch (PDOException $e) {
echo 'Registrierung fehlgeschlagen: '. $e->getMessage();
}
}
?>
<form action="" method="POST"> 4
<input name="username" placeholder="Benutzername"/><br/>
<input type="password" name="password" placeholder="Passwort"/>
<button type="submit">Registrieren</button>
</form>

Listing 13.4: Benutzerregistrierung im Skript register.php

280
Beispiel: Benutzer-Accounts 13.3

ƒ 1 Falls username und password im $_POST-Array vorhanden sind, wurde das


Formular abgeschickt und ein neuer Benutzer kann angelegt werden.
ƒ 2 Prepared Statement, um den neuen Benutzer in die user-Tabelle einzu-
fügen. Die »?«-Platzhalter stehen für die dynamischen Benutzereingaben.
ƒ 3 PDOStatement::execute() ersetzt die Platzhalter mit den Formulareinga-
ben und führt den INSERT-Befehl aus.
ƒ 4 HTML-Registrierungsformular mit Feldern für Benutzername (user­
name) und Passwort (password).
Nach einigen Registrierungen könnte die Tabelle wie folgt aussehen:

Abb. 13.1: user-Tabelle nach einigen Registrierungen

Passwörter sollten nie im Klartext gespeichert werden. Das Kapitel stellt


weiter unten eine Lösung dieses Problems vor.

13.3.3 Login
Das Skript login.php verarbeitet ein Login-Formular mit Eingaben für Be-
nutzername und Passwort. Existiert in der user-Tabelle ein zu den Eingaben
passender Datensatz, ist der Benutzer authentifiziert. Der Status wird in der
Benutzer-Session vermerkt und eine Weiterleitung zur Seite secret.php ange-
stoßen, die eingeloggten Benutzern vorbehalten ist.
if (isset($_POST['username'], $_POST['password'])) { 1
require __DIR__ . '/_mysql-connect.php';
try {
$stmt = $pdo->prepare( 2
'SELECT * FROM user
WHERE username = ? AND password = ?'
)
$stmt->execute ([$_POST['username'], $_POST['password']]); 3
if ($stmt->rowCount() === 1) { 4
$user = $stmt->fetch(); 5

281
Kapitel 13 PHP und MySQL kombinieren

session_start();
$_SESSION['username'] = $user['username']; 6
header('Location: secret.php'); exit(); 7
}
echo 'Benutzername/Passwort ungültig.'; 8
} catch (PDOException $e) {
echo 'Datenbankfehler: '. $e->getMessage();
}
}
?>
<form method="POST"> 9
<input name="username" placeholder="Benutzername"/><br/>
<input type="password" name="password" placeholder="Passwort"/>
<button type="submit">Login</button>
</form>

Listing 13.5: Benutzer-Login im Skript login.php

ƒ 1 Falls username und password im $_POST-Array vorhanden sind, wurde


das Login-Formular abgeschickt und der Authentifizierungsversuch kann
beginnen.
ƒ 2 Prepared Statement mit SELECT-Befehl, um einen Benutzer anhand Be-
nutzername/Passwort aus der user-Tabelle abzufragen. Die »?«-Platzhalter
stehen für die dynamischen Benutzereingaben.
ƒ 3 PDOStatement::exec() ersetzt die Platzhalter mit den Formulareingaben
und führt den SELECT-Befehl aus.
ƒ 4 PDOStatement::rowCount() gibt die Anzahl der gefunden Datensätze
(Tabellenzeilen) zurück. Wird genau ein Benutzer gefunden, ist der Login
erfolgreich.
ƒ 5 PDOStatement::fetch() liefert den nächsten Datensatz der Ergebnismen-
ge, in diesem Fall den ersten und einzigen gefundenen Datensatz.
ƒ 6 Der Benutzername des gefundenen Benutzers wird im Feld username der
zuvor gestarteten Session gespeichert. Das Vorhandensein des Felds ist In-
dikator für einen eingeloggten Benutzer.
ƒ 7 Weiterleitung auf die Seite secret.php, deren Inhalt eingeloggten Benut-
zern vorbehalten ist.
ƒ 8 Ohne passenden Benutzer in der Datenbank war die Authentifizierung
nicht erfolgreich.
ƒ 9 Login-Formular mit Eingabefeldern für Benutzername/Passwort.

282
Beispiel: Benutzer-Accounts 13.3

13.3.4 Login-Bereich
Der Inhalt der Seite secret.php ist nur für eingeloggte Benutzer zugänglich.
Das Skript leitet Zugriffe von nicht authentifizierten Benutzern zum Login auf
login.php weiter.

session_start();
if (!isset($_SESSION['username'])) { 1
header('Location: login.php'); exit();
}
if (isset($_GET['logout'])) { 2
unset($_SESSION['username']); 3
header('Location: login.php'); exit();
}
?>
<p> 4
Hallo <?= $_SESSION['username'] ?>,
Sie sind eingeloggt. <br/><img src="kitty.png"/>
<br/><a href="?logout">Logout</a> 5
</p>

Listing 13.6: Inhalt für eingeloggte Benutzer in secret.php

ƒ 1 Falls der Indikator für einen eingeloggten Benutzer das Feld username im
$_SESSION-Array nicht gesetzt ist, erfolgt eine Weiterleitung auf login.php.
ƒ 2 Ein Logout wird durch Klick auf einen Link mit gesetztem URL-Parame-
ter logout signalisiert.
ƒ 3 Beim Logout wird der Indikator für einen eingeloggten Benutzer, das
Feld username im $_SESSION-Array, gelöscht und der Benutzer auf login.
php zurückgeleitet.
ƒ 4 Der exklusive Inhalt für authentifizierte Benutzer.
ƒ 5 Logout-Link zum selben Skript secret.php mit gesetztem logout-Flag
als URL-Parameter (ein Wert ist nicht nötig).

Aufgabe 2

Erkennen Sie den Unterschied zwischen PDOStatement::fetchAll() und


PDOStatement::fetch()?

283
Kapitel 13 PHP und MySQL kombinieren

13.4 Passwörter sicher speichern


Das Beispiel des letzten Abschnitts speichert Benutzer-Passwörter im Klar-
text, d.h. unverschlüsselt. Zwar sollte eine Datenbank nicht öffentlich einseh-
bar sein, dennoch ist es eine wichtige Vorsichtsmaßnahme, Passwörter nicht
im Klartext zu speichern. Viele Benutzer verwenden die gleiche Kombina­-
tion aus Benutzername/Passwort auch bei anderen Diensten. Ein nachlässiger
Umgang kann zu Identitätsdiebstahl führen:
ƒ Dritte könnten aus Versehen oder durch Ausnutzen einer Sicherheitslücke
Zugriff auf eine Datenbank erlangen.
ƒ Passwörter sollten auch für Website-Betreiber nicht einsehbar sein.

13.4.1 Passwort-Hash erzeugen


Anstelle eines Klartext-Passworts ist es möglich, einen so genannten Pass-
wort-Hash zu speichern. Das originale Passwort wird dabei mit Hilfe mathe-
matischer Berechnungen in eine andere Zeichenkette, den Password-Hash,
transformiert:

Abb. 13.2: Transformation eines Passworts in einen Hash

Die Transformation des Passworts ist eine Einweg-Operation (One-Way Has-


hing): Es soll unmöglich sein, vom Hash-Wert einen Rückschluss auf das ori-
ginale Passwort zu ziehen. Die PHP-Funktion password_hash() berechnet ei-
nen sicheren Passwort-Hash mit einem gewünschten Algorithmus:
password_hash(Passwort, Hash-Algorithmus): string

Der Hash-Algorithmus benennt den gewünschten mathematischen Verschlüs-


selungs-Algorithmus. Algorithmen werden aufgrund steigender Rechenleis-
tung mit der Zeit unsicherer. Durch Übergabe der Konstanten PASSWORD_DE-
FAULT wählt PHP automatisch den aktuell besten Hashing-Algorithmus:

echo password_hash('s3cr3t', PASSWORD_DEFAULT);


// z.B. => $2y$10$7nz...YJc/sn8g/.yhxcpJyBpAgudd7m

Der Passwort-Hash wird anstelle des Klartext-Passworts in der Datenbank


gespeichert.

284
Sicherheitslücke: SQL-Injection 13.4

13.4.2 Passwort validieren


Wenn das Passwort nicht mehr im Original vorliegt, wie lässt sich ein einge-
gebenes Passwort beim Login auf Korrektheit prüfen? Dies geschieht durch
erneute Berechnung des Passwort-Hashs aus dem beim Login eingegebenen
Klartext-Passwort und einem Abgleich mit dem bei der Account-Registrie-
rung gespeicherten Passwort-Hash. Auch hierzu stellt PHP eine praktische
Funktion zur Verfügung:
password_verify(Passwort, Passwort-Hash): bool;

Die Funktion antwortet mit einem Wahrheitswert, ob die Berechnung des


Passwort-Hashs aus dem fraglichen Passwort zum gegebenen Passwort-Hash
führt. Eine Speicherung des Klartext-Passworts ist nicht mehr erforderlich.
$passwordInQuestion = 's3cr3t'; // z.B. aus Formular
$passwordHash = '$2y$10$7nz...YJc/sn8g/.yhxcp...dd7m';
if (password_verify($passwordInQuestion, $passwordHash)) {
echo 'Passwort korrekt!';
} else {
echo 'Passwort nicht korrekt!';
}

Abb. 13.3: Passwort-Überprüfung in password-hashing.php

Wie erkennt PHP den Algorithmus, der zu einem früheren Zeitpunkt zur Ver-
schlüsselung verwendet wurde? PHP bettet diese Information praktischerwei-
se im Passwort-Hash ein und hat diese Information damit automatisch zur
Verfügung. Detaillierte Informationen finden Sie in der PHP-Dokumentation
zum Thema »Password Hashing«1.

13.5 Sicherheitslücke: SQL-Injection


Eine SQL-Injection ist eine Sicherheitslücke im Quelltext einer datenbankge-
stützten Software-Anwendung. Dabei gelingt es einem Angreifer auf Grund
mangelhafter Prüfung von Benutzereingaben, eigene Datenbankbefehle ein-
zuschleusen. Mögliche Folgen sind unbefugte Einsicht, Diebstahl, Löschung
oder Manipulation von Daten. Web-Anwendungen sind anfällig für SQL-
Injections, wenn Eingabedaten z.B. aus Formularen oder URL-Parametern
direkt in SQL-Befehle übernommen werden.

1
https://www.php.net/manual/faq.passwords

285
Kapitel 13 PHP und MySQL kombinieren

Der SQL-Befehl für den Benutzer-Login aus Abschnitt 13.3.3 fragt die Daten-
bank, ob ein passender Benutzer mit entsprechendem Password in der user-
Tabelle existiert. Dies könnte auf den ersten Blick auch mit einer einfachen
Code-Variante funktionieren, in der die Benutzereingaben aus dem POST-
Formular (Benutzername und Passwort) direkt in das SQL eingebaut werden,
hier mit Hilfe der sprintf()-Funktion:
$sql = sprintf(
"SELECT * FROM user
WHERE username = '%s' AND password = '%s'",
$_POST['username'],
$_POST['password']
);
$stmt = $pdo->query($sql);
if ($stmt->rowCount() === 1) { // falls ein Benutzer gefunden
// Benutzer eingeloggt!
}

Listing 13.7: Code mit Sicherheitslücke für eine SQL-Injection

Dieser Code ist anfällig für eine SQL-Injection. Über die Formulareingaben
können auch Zeichen mit SQL-Sonderfunktion eingeschleust werden, z.B. das
einfache Anführungszeichen. In Kenntnis eines gültigen Benutzernamens,
z.B. jan99, kann ein Angreifer die Passwortsicherung aushebeln und den
Account übernehmen.

Abb. 13.4: SQL-Injection zum unberechtigten Login

Der resultierende SQL-Code funktioniert nicht mehr wie vom Programmierer


beabsichtigt: Der Angreifer kann über das Passwort-Eingabefeld SQL-Code
statt des beabsichtigten Passworts einschleusen. Das resultierende SQL lautet:
SELECT * FROM user
WHERE username='???' AND password='' OR username = 'jan99'

Klammern verdeutlichen die Rangfolge (»Präzedenz«) der Operatoren, um


die Auswirkung der SQL-Injection zu verdeutlichen:
...WHERE (username='???' AND password='') OR username='jan99'

286
Sicherheitslücke: SQL-Injection 13.4

In Kenntnis eines Benutzernamens kann sich ein Angreifer ohne das passende
Passwort in jeden Account einloggen.
Sichtbare technische Fehlermeldungen können einem Angreifer beim
Versuch helfen, die passende Eingabe für eine SQL-Injection zu finden.
Eine produktive Anwendung muss Fehlermeldungen daher immer in
eine Log-Datei schreiben und nie direkt anzeigen.
Folgende Eingabe schleust sogar einen vollständigen SQL-Befehl ein und
löscht alle Daten aus der user-Tabelle:

Abb. 13.5: SQL-Injection zur Daten-Manipulation

Das erste Semikolon im Passwort-Eingabefeld beendet den vom Programmie-


rer beabsichtigten SQL-Befehl, danach kann der Angreifer einen beliebigen
weiteren Befehl ergänzen. Die zwei Bindestriche (--) am Ende leiten einen
SQL-Kommentar ein, um das schließende Anführungszeichen aus dem ur-
sprünglichen SQL-Code zu ignorieren. Das Ergebnis:
SELECT * FROM user
WHERE username='???' AND password=''; DELETE FROM user; --'

Zum Schutz vor SQL-Injections müssen SQL-Sonderzeichen in eingebetteten


Daten durch Maskierung von der Interpretation ausgeschlossen und dadurch
unschädlich gemacht werden.
Die im bisherigen Verlauf des Kapitels gezeigte Technik der »Prepared State-
ments« bietet genau diesen Schutz vor SQL-Injections automatisch, da Einga-
bedaten nicht direkt, sondern über die »?«-Platzhalter in das SQL eingebaut
werden. Die PDO-Erweiterung kümmert sich dabei verlässlich um die Mas-
kierung der SQL-Sonderzeichen:
$stmt = $pdo->prepare(
'SELECT * FROM user WHERE username = ? AND password = ?'
);
$stmt->execute([$_POST['username'], $_POST['password']]);
if ($stmt->rowCount() === 1) { // Benutzer eingeloggt! }

Listing 13.8: Prepared Statement: Sichere Einbettung von Benutzerdaten

287
Kapitel 13 PHP und MySQL kombinieren

In MySQL maskiert der vorangestellte Backslash das einfache Anführungszei-


chen: \'. Mit einem Prepared Statement wird bei der Eingabe von »' OR user-
name = 'jan99« als Passwort der resultierende Befehl zum Login daher lauten:
SELECT * FROM user
WHERE username='???' AND password='\' OR username = \'jan99'

Mit maskierten einfachen Anführungszeichen funktioniert die Abfrage wie


beabsichtigt und fragt nach einem Benutzer mit dem Benutzernamen ??? und
dem Passwort »' OR username = 'jan99«. Ebenso im Fall der versuchten Da-
tenlöschung: Das eingeschleuste SQL wird korrekt als Passwort »'; DELETE
FROM user --« interpretiert:
... AND password = '\'; DELETE FROM user --'

Prepared Statements bieten automatischen Schutz vor SQL-Injections und


führen Abfragen zudem effizienter aus. Die eigene Einbettung von Benutzer-
eingaben in SQL-Befehle ist fehleranfällig und sollte vermieden werden.
Die Gefahren von SQL-Injections wie auch des Cross Site Scriptings (XSS)
aus Abschnitt 7.2.2 verdeutlichen das wichtigste Mantra zur Vorbeugung
von Sicherheitslücken: Traue niemals Benutzereingaben!

13.6 Übungen
Übung 1
Erstellen Sie ein Skript list-users.php zur Auflistung der registrierten Benut-
zer des Beispiels aus Abschnitt 13.3 in einer HTML-Tabelle.

Übung 2
Verbessern Sie das Beispiel aus Abschnitt 13.3 durch Ersetzung des Klartext-
Passworts mit einem Passwort-Hash. Verschlüsseln Sie das Passwort bei der
Registrierung vor dem Speichern in der Datenbank mit password_hash().
Prüfen Sie das Passwort beim Login mit password_verify().

Übung 3
Ergänzen Sie das Beispiel aus Abschnitt 13.3 um die Speicherung des letz-
ten Login-Datums. Ergänzen Sie dazu eine DATETIME-Spalte last_login in der
user-Tabelle. Aktualisieren Sie das Datum an der Stelle eines erfolgreichen
Logins für den passenden Benutzer mit dem UPDATE SQL-Befehl.

288
Abschlussprojekt: Ein
Blog programmieren
Das letzte Kapitel vereint Gelerntes aus dem gesamten Buch zu einem größe-
ren Projekt, der Programmierung eines Blogs mit diesen Features:
ƒ Startseite mit Übersicht aller Blog-Artikel und Kategorie-Filter
ƒ Artikel-Detailansicht mit komplettem Artikelinhalt und:
ƒ Download des Artikels als PDF
ƒ Feedback-Formular mit Übermittlung per E-Mail
ƒ Login/Logout für Administratoren
ƒ Zusätzliche Möglichkeiten für eingeloggte Administratoren:
ƒ Erstellung neuer Artikel
ƒ Bearbeitung von Artikeln
ƒ Löschung von Artikeln
Das Projekt rundet das Buch mit Tipps und Tricks, nützlichen Techniken
und der Einbindung externer Bibliotheken für die PDF-Erstellung sowie zum
­E-Mail-Versand ab. Zum Abschluss erhalten Sie einen Ausblick auf fortge-
schrittene Themen.

14.1 Installation
Der Quelltext des Blogs befindet sich in den Code-Downloads zu diesem Ka-
pitel (www.mitp.de/0395) im Unterverzeichnis blog/. Beim Start des Webser-
vers muss die Document-Root auf das Unterverzeichnis blog/public/ einge-
stellt sein. Haben Sie das Projektverzeichnis z.B. in Ihr Benutzerverzeichnis
(~) nach ~/blog verschoben, erfolgt der Start des Entwicklungs-Webservers
mit:

289
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

> cd ~/blog
> php -S localhost:8000 -t public/

Starten Sie phpMyAdmin (Kapitel 12) zur Verwaltung der Datenbank parallel
auf einem anderen Port:
> cd ~/phpMyAdmin
> php -S localhost:8001

Im Browser ist das Blog unter http://localhost:8000 erreichbar, phpMy­


Admin unter http://localhost:8001.
Zur Einrichtung der nötigen Datenbank blog mit den Tabellen article und
user sowie einigen Testdaten führen Sie einmalig das SQL-Skript setup.sql
aus, z.B. über den Reiter Importieren in phpMyAdmin.
Das erneute Ausführen von setup.sql setzt das Projekt komplett zurück.
Die Datenbank blog wird gelöscht und mit allen Strukturen und Testda-
ten neu angelegt.

14.2 Übersicht: Das fertige Blog


Browsen Sie zur Orientierung durch das fertige Blog, siehe Abbildung 14.1
und Abbildung 14.2.

Abb. 14.1: Startseite des Blogs

290
Verwendete Techniken 14.3

Abb. 14.2: Detailansicht eines Artikels

Mit dem Benutzernamen admin und gleichlautendem Passwort admin melden


Sie sich als Administrator an und erhalten zusätzliche Funktionen, siehe Ab-
bildung 14.3.

Abb. 14.3: Zusatzfunktionen für eingeloggte Admins

14.3 Verwendete Techniken


Das Blog-Projekt verwendet weitere Techniken, die das Buch bisher noch
nicht vorgestellt hat.

291
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

14.3.1 Webdesign mit »Bootstrap«


Die Gestaltung von Webseiten mit den Frontend-Technologien HTML, CSS
und JavaScript erfordert weiteres Wissen und Aufwand. Schnelle und ansehn-
liche Erfolge lassen sich mit Open-Source Baukasten-Systemen wie Boot-
strap (https://getbootstrap.com) erzielen, das im Blog-Projekt zum Einsatz
kommt. Bootstrap bietet konsistente Vorlagen für Textformatierungen, Me-
nüs, Schaltflächen, Formulare, Tabellen und vieles mehr. Informationen zur
Einbindung und Verwendung gibt die umfangreiche Online-Dokumentation.

14.3.2 Methodenaufrufe verketten


Objekterzeugung und Methodenaufrufe erfolgten bisher in separaten Schrit-
ten:
$date = new DateTime('now');
echo $date->format('d.m.Y');

Setzt man die Erzeugung des Objekts in Klammern, lassen sich beide Schritte
in eine Anweisung kombinieren:
echo (new DateTime('now'))->format('d.m.Y');

Liefert eine Methode ein Objekt zurück, können beliebig viele weitere Verket-
tungen entstehen:
echo (new DateTime('now'))
->modify('+1 month')
->format('d.m.Y');

Listing 14.1: Methodenaufrufe verketten – method-chaining.php

14.3.3 Letzte eingefügte Datensatz-ID ermitteln


Nach einem INSERT SQL-Befehl liefert die PDO-Methode PDO::lastInsertId()
die von MySQL per »auto increment« erzeugte ID eines neuen Datensatzes:
$stmt = $pdo->prepare('INSERT INTO article ...');
$stmt->exec([...]);
$id = $pdo->lastInsertId(); // Neue Artikel-ID, z.B. 3

Nach dem Einfügen eines neuen Artikels kann damit direkt die URL zur Wei-
terleitung auf die Artikelansicht gebildet werden.
header('Location: /article.php?id=.' . $id);

292
Verwendete Techniken 14.3

14.3.4 POST-Anfrage erkennen


Die Entscheidung, ob ein POST-Formular abgeschickt wurde, erfolgte bisher
durch Prüfung aller Formularfelder mit isset():
if (isset($_POST[Feld1], $_POST[Feld2])) {...}

Alternativ zeigt das Feld REQUEST_METHOD (engl. für Anfrage-Methode) des su-
perglobalen $_SERVER-Arrays an, ob das Skript mit der POST-Methode aufge-
rufen und daher ein Formular abgesendet wurde:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Formular wurde abgesendet..
} else {
// Formular wurde nicht abgesendet ...
}
?>
<form method="POST">...</form>

14.3.5 Versteckte Formularfelder


HTML-Formulare bieten neben den sichtbaren input-Elementen (text, pass-
word, number usw.) den speziellen Typ hidden (engl. für versteckt):

<input type="hidden" name="Name" value="Wert"/>

Der Wert des Formularfelds wird wie jedes andere Feld an den Server über-
mittelt, das Feld ist für Betrachter jedoch unsichtbar. Versteckte Felder ent-
halten bei Bedarf Zusatzinformation für die Verarbeitung, z.B. die ID eines
Artikels, den ein Administrator löschen möchte:
<form action="/article-delete.php" method="POST">
<input name="id" type="hidden" value="3"/>
<button type="submit">Löschen</button>
</form>

14.3.6 Composer
Bei vielen wiederkehrenden Programmieraufgaben können Sie auf fertige,
kostenlose Lösungen zurückgreifen, z.B. für die Erzeugung von PDF-Dateien
oder den Versand von E-Mails.

293
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

Das Open-Source-Werkzeug Composer (https://getcomposer.org) macht


hunderttausende PHP-Softwarepakete für die unterschiedlichsten Aufgaben
einfach zugänglich. Zur Installation unter Windows führen Sie den Installa­
tions-Assistenten1 mit allen Standard-Optionen aus. Unter macOS und Linux
führen Sie das PHP-Installationsskript2 aus und verschieben die dabei erzeug-
te Datei composer.phar in ein passendes Verzeichnis für ausführbare Kom-
mandozeilen-Programme:
> php installer
> mv composer.phar /usr/local/bin/composer

Im Anschluss steht Composer als Kommandozeilen-Programm composer zur


Installation von PHP-Paketen bereit:
> composer
Composer version 2.1.7
...

Passende Pakete finden Sie über das Paket-Verzeichnis Packagist (https://


packagist.org); z.B. das Paket dompdf/dompdf zur Umwandlung von HTML-
Code in ein PDF-Dokument:

Abb. 14.4: Suche nach Paket zur PDF-Erstellung

1
https://getcomposer.org/Composer-Setup.exe
2
https://getcomposer.org/installer

294
Verwendete Techniken 14.3

Im Wurzelverzeichnis eines PHP-Projekts installiert der Composer-Unterbe-


fehl require (engl. für benötigen) ein Paket:
> cd ~/blog
> composer require dompdf/dompdf
...

Composer lädt das Paket (ggf. mit weiteren abhängigen Paketen) herunter
und installiert es in das Verzeichnis vendor/ (engl. für Anbieter). Ein Skript,
das die Klassen und Funktionen der installierten Pakete nutzen möchte, muss
zunächst die Datei vendor/autoload.php einbinden. Nach der Installation von
dompdf/dompdf kann z.B. eine Instanz der Klasse Dompdf\Dompdf erzeugt wer-
den, die ein PDF-Dokument repräsentiert:
require __DIR__ . '/vendor/autoload.php';
$pdf = new Dompdf\Dompdf();

Die Verwendung und Möglichkeiten der Pakete beschreiben die über Pa-
ckagist verlinkten Dokumentationen.

Im Blog-Projekt sind die Pakete dompdf/dompdf3 zur PDF-Generierung


und symfony/mailer4 für den E-Mail-Versand bereits vorinstalliert.
In den Dateien composer.json und composer.lock im Wurzelverzeichnis spei-
chert Composer Informationen zu den in einem Projekt verwendeten Paketen
(siehe Composer-Dokumentation5).
Die Namen von Composer-Paketen bestehen aus zwei Teilen, ge-
trennt von einem Schrägstrich. Der erste Teil bezeichnet den »An-
bieter« (z.B. der Name einer Organisation oder eines Entwicklers),
der zweite Teil den eigentlichen Namen der Software. Unter dem
Symfony-Framework (siehe Abschnitt 14.6.3) als Anbieter erscheinen
neben symfony/mailer weitere Pakete wie symfony/validation (Daten-
validierung) oder symfony/translation (Übersetzungen). Manche Pa-
kete stehen nur für sich selbst und verwenden den Software-Namen
gleichzeitig als Anbieter – wie bei dompdf/dompdf.

3
https://dompdf.github.io
4
https://github.com/symfony/mailer
5
https://getcomposer.org/doc

295
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

Aufgabe 1

Welche Merkmale könnten Ihnen helfen, die Qualität eines Composer-Pa-


kets einzuschätzen? Betrachten Sie dazu die Informationen auf Packagist.

14.4 So funktioniert das Blog


Dieser Abschnitt erklärt die Funktionsweise des Blogs mit Quelltext-Aus-
schnitten. Detaillierte Erklärungen finden Sie auch in den Kommentaren der
Quelltext-Dateien.

14.4.1 Verzeichnisstruktur
Abbildung 14.5 zeigt die Verzeichnisstruktur des Projekts und gibt Hinweise
auf den Zweck der Dateien bzw. Verzeichnisse.

Abb. 14.5: Verzeichnisstruktur des Blog-Projekts

14.4.2 Die verschiedenen Webseiten


Jedes öffentliche Skript in der Document-Root public/ steht für einen An-
wendungsfall, z.B.
ƒ index.php: Startseite mit Übersicht aller Artikel.
ƒ article.php: Detailansicht eines Artikels.
ƒ article-pdf.php: Artikel als PDF herunterladen.
ƒ article-feedback.php: Feedback per E-Mail senden.

296
So funktioniert das Blog 14.4

ƒ article-create.php: Artikel anlegen.


ƒ login.php: Administrator-Login.
ƒ usw.
Jedes Skript führt die für den Anwendungsfall nötige Logik aus und erzeugt
schließlich die passende HTML-Ausgabe. Die auf allen Seiten einheitlichen
Kopf- und Fußbereiche werden aus den Dateien _header.php bzw. _footer.
php eingebunden. Das allgemeine Schema für einen Anwendungsfall lautet:

<?php
// Hier: PHP-Anweisungen je nach Anwendungsfall ...
$pageTitle = 'Titel der Seite';
require __DIR__.'/../_header.php'; // Kopfbereich
?>
Hier ist der Hauptbereich zwischen Kopf- und Fußbereich:
Enthält HTML und PHP je nach Anwendungsfall.
<?php
require __DIR__.'/../_footer.php'; // Fußbereich

Einige Anwendungsfälle erzeugen keine HTML-Ausgabe, sondern leiten nach


erfolgreicher Ausführung auf eine andere Seite weiter. Das Skript article-
create.php etwa leitet nach Einfügen eines neuen Artikels in die Datenbank
zur Ansicht des neuen Artikels auf article.php weiter; das Skript article-
delete.php leitet nach dem Löschen eines Artikels auf die Startseite weiter.

14.4.3 Initialisierungsaufgaben
Jedes öffentliche Skript aus der Document Root bindet zunächst die allgemei-
ne Datei _initialize.php ein. Sie führt Aufgaben durch, die für jede Seite des
Blogs relevant sind, und »initialisiert« damit die Web-Anwendung. Dazu zählt
z.B. die Herstellung der Datenbankverbindung, der Start der Session oder die
Einbindung der Datei vendor/autoload.php, um Composer-Pakete nutzen zu
können. Dieser Abschnitt erläutert einige der Initialisierungsaufgaben.

Konfiguration mit Konstanten


Konstanten legen die Zugangsdaten für die Datenbank, den E-Mail-Versand
und weitere Parameter des Blogs wie die E-Mail-Adressen für Feedback-­
Absender und -Empfänger fest. Die Werte müssen entsprechend angepasst
werden. Ein Ausschnitt:

297
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

define('DATABASE_DSN', 'mysql:host=localhost;dbname=blog');
define('DATABASE_USER', 'root');
define('DATABASE_PASSWORD', 's3cr3t');
define(
'MAILER_DSN', // z.B. Versand via Google Gmails SMTP-Server
'smtp://mein.name@gmail.com:PASSWORD@smtp.gmail.com:587'
);
define('FEEDBACK_RECIPIENT', mein.name@example.com');
define('BLOG_CATEGORIES', [ // Verfügbare Themen-Kategorien
'php' => 'PHP',
'javascript' => 'JavaScript',
'mysql' => 'MySQL',
]);

Listing 14.2: Ausschnitt der Konfiguration in _initialize.php

PHP-Einstellungen mit ini_set()


ini_set() stellt wichtige PHP-Einstellungen zur Laufzeit sicher, sie haben
Vorrang vor der php.ini. Diese Einstellungen sind damit unabhängig von der
Installationsumgebung (lokal oder Webhoster):
ini_set('display_errors', true); // true für Entwicklung
ini_set('error_reporting', E_ALL); // alle Fehler melden
ini_set('error_log', __DIR__.'/blog.log'); // Log-Datei

Listing 14.3: Ausschnitt für PHP-Einstellungen in _initialize.php

Fehler zentral behandeln


Eine unerwartete (d.h. nicht gefangene) Ausnahme irgendwo im Programm­
ablauf beendet das Skript an Ort und Stelle; je nach PHP-Einstellungen mit
oder ohne (technische) Fehlermeldung. Dies kann beim Besucher einen
schlechten Eindruck hinterlassen. Die Definition einer eigenen Funktion als
sogenanntem Exception Handler (»Ausnahme-Behandler«) erlaubt es, noch
kurz vor Beendigung des Skripts auf die Ausnahme-Situation zu reagieren.
Die folgende selbst definierte Funktion exceptionHandler() empfängt die un-
erwartete Ausnahme als Parameter und bindet die Seite error-fatal.php ein,
die eine höfliche Entschuldigung zeigt:
function exceptionHandler(Throwable $exception): void {
require __DIR__.'/error-fatal.php';
}

298
So funktioniert das Blog 14.4

Die PHP-Funktion set_exception_handler() teilt PHP die Verwendung des


eigenen Exception-Handlers mit.
set_exception_handler('exceptionHandler');

Abb. 14.6: Höfliche Entschuldigung für unerwartete Fehler

In error-fatal.php entscheidet die PHP-Einstellung display_errors über die


Anzeige von Fehlerdetails:
<?php if (ini_get('display_errors')): ?>
<h5>Fehlerdetails</strong></h5>
<pre><?= $exception ?></pre>
<?php endif; ?>

Listing 14.4: Ausschnitt aus error-fatal.php

Nach der Definition des Exception-Handlers ist für eine saubere Fehlerbe-
handlung nicht überall ein try-catch-Block nötig, z.B. bei der Herstellung der
Datenbankverbindung:
$pdo = new PDO(DATABASE_DSN, ...);

Schlägt die Datenbankverbindung fehl und wirft eine Ausnahme, tritt der zen-
trale Exception-Handler in Aktion und bindet die eigene Fehlerseite ein.

Eigene Funktionen
Häufig benötigte Aufgaben sind in eigene Funktionen ausgelagert, z.B. zur
Formatierung eines Datums:

299
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

function formatDate(string $date): string {


return (new DateTime($date))->format('d.m.Y');
}

Listing 14.5: Eigene Funktion in _initialize.php

Der Login-Status eines Benutzers ist in der Session gespeichert (siehe auch
Abschnitt 9.2.3):
function isLoggedIn(): bool {
return isset($_SESSION['user']);
}

Der Status wird mehrfach benötigt, um die Webseite entsprechend anzupas-


sen, z.B. zur Ein- bzw. Ausblendung der Schaltfläche Neuer Artikel in der
Navigation:
<?php if (isLoggedIn()): ?>
<a href="/article-create.php">Neuer Artikel</a>
<?php endif; ?>

Listing 14.6: Ausschnitt der Navigation in _header.php

Die Programmzeilen zur häufig benötigten Weiterleitung nach erfolgreicher


Formularauswertung inkl. Flash-Nachricht (Kapitel 9) sind ebenfalls in einer
Funktion festgehalten:
function redirectWithSuccessMessage(
string $toUrl, string $successMessage
): void {
$_SESSION['flash_success'] = $successMessage;
header('Location: ' . $toUrl); exit();
}

Unterhalb der Navigation im Kopfbereich (_header.php) erfolgt die Anzeige


(und anschließende Löschung) einer Flash-Nachricht nach einer Weiterlei-
tung:
<?php if (isset($_SESSION['flash_success'])): ?>
<div class="alert alert-success">
<?= $_SESSION['flash_success']; ?>
</div>

300
Anwendungsbeispiele 14.5

<?php unset($_SESSION['flash_success']); ?>


<?php endif ?>

Listing 14.7: Anzeige einer Flash-Nachricht in _header.php

14.5 Anwendungsbeispiele
Dieser Abschnitt beschreibt einige Anwendungsfälle des Blogs. Alle Fälle sind
ausführlich in den Quelltext-Dateien kommentiert.

14.5.1 Startseite mit Artikelübersicht


Die Startseite index.php mit der Artikelübersicht bildet zunächst den passen-
den SELECT-Befehl zur Abfrage der Datensätze, absteigend nach Erstellungs-
datum sortiert. Falls der URL-Parameter category vorhanden ist, werden nur
Datensätze der entsprechenden Kategorie ausgewählt. Dies ist nach dem Klick
auf einen der Kategorie-Links der Fall und wenn diese Kategorie auch tatsäch-
lich existiert.
$parameters = [];
$category = $_GET['category'] ?? null;
$sql = 'SELECT * FROM article';
if (isset(BLOG_CATEGORIES[$category])) {
$sql .= ' WHERE category = ?';
$parameters[] = $category;
}
$sql .= ' ORDER BY created_at DESC';
$stmt = $pdo->prepare($sql);
$stmt->execute($parameters);
$articles = $stmt->fetchAll();

Listing 14.8: index.php (Teil 1): Artikel abfragen

Zwischen Kopf- und Fußbereich (_header.php bzw. _footer.php) mit dem al-
len Seiten gemeinsamen HTML-Gerüst erfolgt die Ausgabe des individuellen
HTML für die Startseite. Dort werden die Kategorie-Links und eine Kurz-
Vorschau je Artikel mit Links zur jeweiligen Detailansicht gebildet:
$pageTitle = 'meinBlog!';
require '../_header.php'; // HTML für Kopfbereich
?>
<p>Willkommen in meinem Blog.</p> ...

301
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

<p>
<?php
foreach(BLOG_CATEGORIES as $id => $category) {
printf('<a href="?category=%s">%s</a> ', $id, $category);
}
?>
</p>
<?php foreach($articles as $article): ?>
<h2><?= htmlspecialchars($article['title']); ?></h2> ...
<p>
<?= htmlspecialchars(
substr($article['content'], 0, 150)) ?> ...
<a href="article.php?id=<?= $article['id'] ?>">mehr</a>
</p>
<?php endforeach ?>
<?php
require '../_footer.php'; // HTML für Fußbereich

Listing 14.9: index.php (Teil 2): Artikelübersicht anzeigen

Beachten Sie, wie die (dynamisch erzeugten) Kategorie-Links mit gesetztem


URL-Parameter category wieder auf die Startseite verlinken und damit den
SQL-Befehl anpassen.
<a href="?category=php">PHP</a>

Links zur Anzeige der Detailansicht eines Artikels mit dem Skript article.
php enthalten die Artikel-ID als URL-Parameter:

<a href="article.php?id=<?= $article['id'] ?>">mehr</a>

14.5.2 Artikel anlegen


Das Skript article-create.php ist nur für eingeloggte Administratoren zu-
gänglich. Zunächst erfolgt eine Prüfung, die nicht eingeloggte Besucher mit
einer Weiterleitung auf die Startseite abweist:
if (!isLoggedIn()) {
redirectWithErrorMessage('/', 'Zugriff nur für Admins.');
}

Listing 14.10: Zugriffsschutz in article-create.php

302
Anwendungsbeispiele 14.5

Für eingeloggte Administratoren hat das Skript folgende Funktionen:


ƒ Erster Aufruf: Anzeige des leeren Formulars zur Eingabe der Artikeldaten.
ƒ Absenden des Formulars mit ungültigen Eingaben: Wiederanzeige des
Formulars mit allen bisherigen Eingaben und Hinweisen zu Eingabefeh-
lern.
ƒ Absenden des Formulars mit gültigen Eingaben: Speichern des neuen
Artikels mit den Formulareingaben in der Datenbank und Weiterleitung
zur Detailansicht.
$formErrors = []; 1
$category = $_POST['category'] ?? ''; 2
$title = $_POST['title'] ?? '';
$content = $_POST['content'] ?? '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { 3
$formErrors = validateArticle($category, $title, $content);
if ($formErrors === []) { 4
$stmt = $pdo->prepare('INSERT INTO article
(category, title, content) VALUE (?, ?, ?)');
$stmt->execute([$category, $title, $content]);
redirectWithSuccessMessage(
'/article?id='.$pdo->lastInsertId(), 5
'Artikel erstellt.');
}
}
// ... Anzeige des Formular, siehe Teil 2 unten

Listing 14.11: Neuen Artikel anlegen in article-create.php (Teil 1)

ƒ 1 Leeres Array $formErrors zum Sammeln möglicher Eingabe-Fehler.


ƒ 2 Für den Wert jedes Formularfelds wird eine Variable initialisiert und
entweder mit einem leeren Wert (erster Aufruf) oder dem Wert aus dem
Formular vorbelegt (falls abgesendet).
ƒ 3 Falls die Anfragemethode POST lautet, wurde das Formular abgeschickt.
ƒ 4 Die Methode validateArticle() (siehe _initialize.php) validiert die
Eingaben und gibt mögliche Fehler in die Variable $formErrors zurück.
Ohne Fehler wird der neue Artikel gespeichert und der Benutzer auf die
Detailansicht weitergleitet.
ƒ 5 Die neue Artikel-ID ist über PDO::lastInsertId() verfügbar und wird
zur Bildung der Weiterleitungs-URL auf die Detailansicht des Artikels ge-
nutzt.

303
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

Nach der bisherigen Programmlogik muss nicht mehr unterschieden werden,


ob das Formular bereits abgeschickt wurde oder nicht; nun muss das Formu-
lar entweder für den ersten Aufruf oder den Fall eines Eingabefehlers ange-
zeigt werden. Die Datei _article-form.php enthält das HTML-Formular und
wird parallel auch für das Bearbeiten eines Artikels eingesetzt (siehe article-
edit.php).

// ... Programmlogik aus Teil 1, siehe oben


$pageTitle = 'Artikel erstellen';
require '../_header.php';
require '../_article-form.php';
require '../_footer.php';

Listing 14.12: Neuen Artikel anlegen in article-create.php (Teil 2)

Der Aufbau des Formulars in _article-form.php verläuft für jedes Formular-


feld ähnlich, wie am Feld für den Titel des Artikels zu sehen:
<form method="POST">
<input type="text" name="title"
class="<?= isset($formErrors['title']) ? 'is-invalid' : '' ?>"
value="<?= htmlspecialchars($title); ?>">
<div class="invalid-feedback">
<?= $formErrors['title'] ?? '' ?>
</div>
<!-- ... Weitere Formularfelder ...-->
<button type="submit">Veröffentlichen</button>
</form>

Listing 14.13: Teile des Artikel-Formulars aus _article-form.php

ƒ Liegt ein Validierungsfehler für das Feld im Array $formErrors vor, wird
eine CSS-Klasse ergänzt, um das Feld mit Hilfe von Bootstrap visuell als
fehlerhaft zu markieren (roter Rahmen). Die Fehlermeldung wird unter
dem Feld ausgegeben.
ƒ Das Feld wird mit dem Wert der Variable $title vorbelegt; liegt ein Va-
lidierungsfehler vor, gehen dem Benutzer dadurch seine bisherigen Ein-
gaben nicht verloren. Bei Nutzung des Formulars zur Bearbeitung eines
Artikels kommt es dadurch auch zur Vorbelegung mit dem bisher gespei-
cherten Titel.

304
Anwendungsbeispiele 14.5

Aufgabe 2

Untersuchen Sie die Funktion validateArticle() in der der Datei _ini-


tialize.php. Welche Validierungen werden angewendet und wie werden
Fehler einem Feld zugeordnet?

14.5.3 Artikel als PDF downloaden


Jede Artikelansicht enthält einen Link zum PDF-Download:
<a href="/article-pdf.php?id=<?= $article['id'] ?>">PDF Download</
a>

Listing 14.14: PDF-Download-Link in article.php

Das Skript article-pdf.php erzeugt keine HTML-Ausgabe, sondern bietet


den Artikel als PDF zum Download an. Zur Erzeugung der PDF-Datei kommt
das mit Composer (Abschnitt 14.3.6) installierte PHP-Paket »Dompdf« zum
Einsatz. Es konvertiert HTML-Code in ein PDF, d.h. die Gestaltung des PDF
erfolgt wie bei einer Webseite mit HTML.
$articleId = $_GET['id'] ?? null;
$stmt = $pdo->prepare('SELECT * FROM article WHERE id = ?');
$stmt->execute([$articleId]);
if ($stmt->rowCount() !== 1) {
redirectWithErrorMessage('/', 'Artikel nicht gefunden.');
}
$article = $stmt->fetch();
$filename = sprintf('Artikel-%s.pdf', $article['id']);
$pdfContent = sprintf(
'<h1>%s</h1><p>%s</p>', // HTML zur Konvertierung
$article['title'],
$article['content']
)
$pdf = new Dompdf\Dompdf();
$pdf->loadHtml($pdfContent); // HTML für PDF-Inhalt laden
$pdf->render(); // HTML in PDF konvertieren
$pdf->stream($filename); // PDF an Browser senden

Listing 14.15: Artikel als PDF downloaden in article-pdf.php

305
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

Falls der zur ID passende Artikel existiert, wird ein Objekt vom Typ Dompdf\
Dompdf erzeugt, das ein zunächst leeres PDF repräsentiert. Dompdf::loadHtml()
lädt den gewünschten Inhalt als HTML, Dompdf::render() konvertiert das
HTML in das PDF-Format. Schließlich bietet Dompdf::stream() das fertige
PDF dem Besucher unter dem angegebenen Dateinamen zum Download an.
Ist Ihnen der Backslash (\) im Klassennamen Dompdf\Dompdf aufge-
fallen, der in einem Bezeichner eigentlich nicht erlaubt ist? Er hat mit
dem Konzept der Namespaces (»Namensräume«) zu tun, siehe dazu
Abschnitt 14.6.1.

14.5.4 Feedback als E-Mail versenden


Grundsätzlich können Sie in PHP E-Mails mit der nativen Funktion mail()
versenden. Für lokale Tests müssen Sie dazu allerdings einen E-Mail-Server
installieren und konfigurieren. Darüber hinaus ist die Nutzung fortgeschritte-
ner E-Mail-Funktionen wie HTML-Inhalte, Dateianhänge, (B)CC-Empfänger
usw. nicht trivial. Aufgrund von Spamschutz-Maßnahmen stufen viele Anbie-
ter »selbstgebaute« E-Mails von unbekannten E-Mail-Servern häufig als Spam
ein. Es empfiehlt sich daher, ein fertiges PHP-Paket für den E-Mail-Versand
zu verwenden, welches Ihnen diese Probleme abnimmt – wie der im Blog-
Projekt vorgestellte »Symfony Mailer«.
Die Artikelansicht article.php enthält ein POST-Formular zum Absenden
von Leser-Feedback an das Skript article-feedback.php. Neben dem Feed-
back-Text überträgt das Formular in einem versteckten Formularfeld die Ar-
tikel-ID:
<form action="/article-feedback.php" method="POST">
<input name="id" type="hidden" value="<?= $article['id'] ?>"/>
<textarea name="feedback"></textarea>
<button type="submit">Feedback senden</button>
</form>

Das Skript article-feedback.php erzeugt keine HTML-Ausgabe, sondern


versendet das erhaltene Feedback mit Klassen aus dem per Composer ins-
tallierten PHP-Paket symfony/mailer als E-Mail und leitet den Besucher an-
schließend mit einer Danke-Nachricht zur Artikelansicht zurück. Bitte be-
achten Sie den vollständigen Code mit ausführlichen Kommentaren aus den
Buch-Downloads.

306
Anwendungsbeispiele 14.5

$articleId = $_POST['id'] ?? null; // Versteckte ID auslesen


$article = ... // Laden des Artikels aus der Datenbank
$transport = Symfony\Component\Mailer\Transport::fromDsn(MAILER_
DSN); 1
$mailer = new Symfony\Component\Mailer\Mailer($transport); 2
$email = (new Symfony\Component\Mime\Email()) 3
->from(FEEDBACK_SENDER) // Absender
->to(FEEDBACK_RECIPIENT) // Empfänger
->subject('Feedback zu: '.$article['title']) // Betreff
->text($_POST['feedback']); // Inhalt
$mailer->send($email); 4
redirectWithSuccessMessage( // Weiterleitung nach Versand
'/article.php?id='.$articleId, 'Danke für Ihr Feedback.'
);

Listing 14.16: Feedback-Versand per E-Mail mit symfony/mailer

ƒ 1 Das Transport-Objekt entscheidet über den konkreten Versandweg, der


sich aus dem Konfigurations-String der Konstante MAILER_DSN (siehe _ini-
tialize.php) ergibt.
ƒ 2 Das Mailer-Objekt wird mit dem gewünschten Transportweg initialisiert
und steuert den E-Mail-Versand.
ƒ 3 Das Email-Objekt repräsentiert eine konkrete E-Mail.
ƒ 4 Der Mailer versendet die individuell erstellte E-Mail.
Der Symfony Mailer bietet verschiedene »Transportwege« für den E-Mail-
Versand an, z.B. das verbreitete SMTP-Protokoll. E-Mail-Anbieter wie Google
Gmail oder Webhoster mit E-Mail-Paket bieten SMTP-Zugangsdaten (Benut­
zername, Passwort, Host, Port), die Sie ansonsten auch zur Konfiguration
­Ihres E-Mail-Programms verwenden.
define('MAILER_DSN', 'smtp://User:Passwort@SMTP-Server:Port');

Listing 14.17: SMTP-Konfiguration in _initialize.php

Über einen Webhoster ist der E-Mail-Versand mit einer E-Mail-


Adresse der eigenen Domain möglich. Legen Sie dort eine Mailbox
mit der gewünschten Adresse an und recherchieren Sie die SMTP-
Zugangsdaten. Für die Verwendung von Gmail finden Sie im Blog-
Quellcode weitere Hinweise.

307
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

Aufgabe 3

Die Erstellung des Transport-Objekts beim E-Mail-Versand zeigt mit


Transport::fromDsn() ein weiteres Sprachelement, den Aufruf einer sta-
tischen Methode. Finden Sie die Funktionsweise mit Hilfe der PHP-Doku-
mentation?

14.6 Ausblick: Was kommt als Nächstes?


Zum Abschluss des Buchs gibt Ihnen dieser Abschnitt Einblicke in fortge-
schrittene Themen, um Sie auf den weiteren Weg in die PHP-Programmie-
rung neugierig zu machen.

14.6.1 Namespaces (Namensräume)


Wenn Sie versuchen, eine eigene Klasse mit dem Namen DateTime zu definie-
ren, kommt es zu einem Fehler:
class DateTime {}
// => Fatal error: Cannot declare class DateTime
// because the name is already in use

Der Grund ist plausibel: der Klassename DateTime wird bereits von der gleich-
namigen, nativen PHP-Klasse verwendet (Abschnitt 10.5.2).
Namespaces (»Namensräume«) sind ein Konzept, um Namenskonflikte bei
Klassen (und Funktionen) zu vermeiden. Ohne die Verwendung von Name-
spaces – wie in den Kapiteln zuvor – befinden sich alle Klassen im selben,
»globalen« Namensraum. Dies ist vergleichbar mit Dateien, die sich ein einzi-
ges Verzeichnis teilen müssen und jeder Dateiname nur einmal vorkommen
kann. Ähnlich wie die Einführung eines Verzeichnisbaums mit Unterver-
zeichnissen funktioniert das Konzept der Namensräume:
namespace Blog; // Wechsel in den Namespace "Blog"
class DateTime {} // Klasse liegt im Namespace "Blog"

// kein Konflikt mit DateTime aus globalem Namespace:


new Blog\DateTime();

Die Definition der Klasse DateTime befindet sich nun unterhalb des Name-
spaces Blog und wird mit Blog\DateTime referenziert, um sie von DateTime
aus dem globalen Namespace zu unterscheiden. Der Backslash fungiert wie

308
Ausblick: Was kommt als Nächstes? 14.6

ein Verzeichnistrenner für Namespaces, die beliebig tief verschachtelt sein


­können.
Composer-Pakete verwenden immer ihren eigenen Namespace, um nicht in
Namenskonflikte mit anderen Paketen, bzw. dem globalen Namespace, zu ge-
raten. Zum Beispiel beim PDF-Export eines Blog-Artikels in Abschnitt 14.5.3:
$pdf = new Dompdf\Dompdf();

Die Klasse Dompdf ist unterhalb des gleichnamigen Namespace Dompdf defi-
niert, der Backslash trennt Namespace und Klassenname.
Eine kompakte (englischsprachige) Video-Einführung in Namespaces gibt es
auf SymfonyCasts6.

14.6.2 Fortgeschrittene Entwicklungsumgebung mit XAMPP


Dieses Buch hat Sie mit dem in PHP eingebauten, einfachen Webserver in die
lokale Entwicklung eingeführt. Eine fortgeschrittene Entwicklungsumgebung,
die einer Produktionsumgebung bei einem Webhoster ähnelt, können Sie sich
z.B. mit dem XAMPP-Projekt (www.apachefriends.org) unter Windows, mac­
OS und Linux einrichten.

14.6.3 Reguläre Ausdrücke


Im Buch haben Sie String-Funktionen kennengelernt, die Zeichenketten auf
einfache Muster prüfen, z.B. ob ein @-Zeichen enthalten ist:
if (str_contains('@', $email)) { ... }

Zur Prüfung von Zeichenketten auf komplexe Muster gibt es eine Art eigene
Sprache, die regulären Ausdrücke. Folgender Funktionsaufruf prüft z.B. mit
dem regulären Ausdruck ^[0-9]{5}$, ob die Variable $zip eine syntaktisch
gültige deutsche Postleitzahl enthält, d.h. genau fünf aufeinanderfolgende Zif-
fern zwischen 0 und 9:
if (preg_match('/^[0-9]{5}$/', $zip)) {
// Ja, $zip enthält syntaktisch korrekte Postleitzahl
}

Die PHP-Dokumentation widmet den regulären Ausdrücken ein ausführli-


ches Kapitel7; im Internet finden Sie zahlreiche Tutorials.
6
https://symfonycasts.com/screencast/php-namespaces
7
https://www.php.net/manual/book.pcre

309
Kapitel 14 Abschlussprojekt: Ein Blog programmieren

14.6.4 Frameworks
Das Blog-Projekt zeigt, dass Teilaufgaben wie eine praktische Verzeichnis-
struktur, eine Datenbankverbindung, die Definition von Konfigurationspara-
metern, ein Login, Techniken zur Formularvalidierung oder Weiterleitungen,
die Behandlung von Fehlern u.v.m. eigentlich unabhängig vom Projektinhalt
sind. Das heißt, nicht nur ein Blog benötigt diese Funktionalitäten, sondern
auch ein Online-Shop, ein Browser-Spiel, eine Nachrichten-Website usw.
Aus diesem Grund haben sich so genannte Frameworks (»Rahmenwerke«)
etabliert, die einen vorgefertigten Rahmen zur Verfügung stellen, innerhalb
dessen individuelle Web-Anwendungen mit erprobten Lösungen schnell und
sicher erstellt werden können. Populäre PHP-Frameworks sind etwa Sym-
fony (https://symfony.com), Laravel (https://laravel.com) oder Laminas
(https://getlaminas.org).

14.6.5 PHP-Standardsoftware: Content Management und Co.


Während Frameworks einen Rahmen für die Programmierung von individu-
ellen Web-Anwendungen aller Art zur Verfügung stellen, gibt es auch etab-
lierte PHP-Standardsoftware für spezielle Anwendungsfälle wie Online-Shops
(Magento, Sylius, OXID etc.), Content Management zur Verwaltung von Web-
Auftritten bzw. Blogs (Wordpress, TYPO3, Joomla etc.) oder Foren (phpBB,
vBulletin etc.). Diese Software-Anwendungen basieren wiederum meist selbst
auf Frameworks.

14.6.6 Versionsverwaltung mit git und GitHub


Bei der langfristigen Quellcode-Verwaltung eines Software-Projekts erge-
ben sich schnell Fragen nach der Nachvollziehbarkeit von Änderungen, nach
Backups, den Möglichkeiten zur konfliktfreien Zusammenarbeit mehrerer
Entwickler, der Bereitstellung von Quelltext über das Internet usw. Diese Pro-
bleme lösen Systeme zur Versionsverwaltung (engl. Version Control System –
VCS), das bekannteste davon ist git (https://git-scm.com). Spezielle Internet-
Plattformen verwalten den Quelltext von Software-Projekten auf Basis von git
und vereinfachen die Zusammenarbeit und Verbreitung der Software. Die
bekannteste dieser Plattformen ist GitHub (https://github.com). So wird der
Quellcode von PHP selbst auf GitHub gepflegt8, ebenso wie die Quellcodes
der bekannten PHP-Frameworks, von PHP-Standardsoftware und Compo-
ser-Paketen. GitHub ist nicht auf PHP beschränkt, Sie finden dort ebenso die
Quellcodes bekannter Open-Source-Software wie den des Frontend-Frame-
8
https://github.com/php/php-src

310
Übungen 14.7

works Bootstrap, des Quellcode-Editors VS Code oder des Systemkerns des


Linux-Betriebssystems. Mit einem eigenen Account können Sie auf GitHub
auch selbst eigene Quellcodes kostenlos verwalten.

14.6.7 Testing mit PHPUnit


Zur Qualitätssicherung muss Software nach Änderungen getestet werden.
Hinzugefügte Funktionalitäten sollen arbeiten wie gewünscht, ohne bestehen-
de Funktionalitäten zu beschädigen. Die manuelle Durchführung von Tests ist
auf Dauer mühsam und kann durch Automatisierung stark vereinfacht wer-
den. Der wichtigste Baustein für automatisierte Tests sind sogenannte Unit-
Tests, die das korrekte Verhalten kleinstmöglicher Code-Einheiten (»Units«)
wie Klassen oder Funktionen testen. Dabei schreiben Entwickler neben dem
eigentlichen Code zusätzlichen Test-Code, der alle möglichen Anwendungs-
fälle einer Code-Einheit ausführt und mit einem erwarteten Ergebnis ver-
gleicht. Unterstützung zur Erstellung von Unit-Tests bietet das beliebte Test-
Framework PHPUnit (https://phpunit.de).

14.7 Übungen
Übung 1
Untersuchen Sie im Blog-Projekt dieses Kapitels den Anwendungsfall zur
Bearbeitung eines Artikels im Skript article-edit.php. Wie funktioniert die
Logik?

Übung 2
Schreiben Sie ein Skript, das ein PDF mit beliebigem Inhalt im Landschafts-
bzw. Querformat (»landscape«) zum Download erstellt. Installieren Sie dazu
mit Composer das Paket dompdf/dompdf in einem leeren Verzeichnis und
konsultieren Sie die Paket-Dokumentation.

Übung 3
Senden Sie mit dem Symfony Mailer eine E-Mail mit HTML-Inhalt und ei-
nem zusätzlichen CC-Empfänger. Installieren Sie dazu mit Composer das
Paket symfony/mailer in einem leeren Verzeichnis und konsultieren Sie die
Paket-Dokumentation.

311
Stichwortverzeichnis

Stichwortverzeichnis
Symbole Benutzereingabe ....................................... 45
$_COOKIE .............................................. 197 Benutzerklassen ...................................... 191
$_FILES ................................................... 185 Benutzer-Registrierung ......................... 280
$_GET ...................................................... 153 Benutzersitzung
$_POST .................................................... 167 Siehe Session
$_SERVER ...................................... 142, 200 Bezeichner (Datenbank) ........................ 247
$_SESSION .............................................. 203 Bibliothek ................................................ 105
$this .......................................................... 217 Block
127.0.0.1 ................................................... 133 Siehe Anweisungsblock
200 OK ..................................................... 141 Blog ........................................................... 289
404 Not Found ........................................ 141 Boolean ...................................................... 57
Bootstrap ................................................. 292
A break ........................................................... 93
Aggregatfunktion (SQL) ............... 265, 268 Browser-Konsole .................................... 140
Alias (SQL) .............................................. 264 Bug .............................................................. 97
AND ......................................................... 262 C
Anführungszeichen
doppelte .............................................. 52 Camel Case ................................................ 47
einfache ............................................... 51 Casting
Anführungszeichen (SQL) .................... 261 Siehe Type Casting
Anweisung ................................................. 66 CLI
Anweisungsblock ...................................... 73 Siehe Kommandozeile
API ............................................................ 183 Client .................................................. 14, 131
Argument clientseitig .................................................. 20
Funktion ........................................... 106 Compiler .................................................... 26
Kommandozeile ................................. 29 Composer .......................................... 22, 293
Array .......................................................... 59 composer.json ......................................... 295
assoziatives ......................................... 61 composer.lock ......................................... 295
mehrdimensionales ........................... 62 continue ..................................................... 93
AS ............................................................. 264 Cookie ...................................................... 195
Ausdruck ................................................... 66 Ablaufdatum ..................................... 201
Ausgabe-Maskierung ............................. 164 lesen ................................................... 197
Ausnahmen ............................................. 224 löschen .............................................. 202
AUTO_INCREMENT ........................... 246 setzen ................................................. 196
Cookie-Banner ....................................... 210
B CREATE TABLE ..................................... 243
Backend ..................................................... 19 Cronjob ...................................................... 21
Bedingte Anweisung ................................ 74 Cross-Site-Scripting (XSS) .................... 163
Bedingte Wiederholung ........................... 91 CSS ............................................................. 16
Bedingungen ............................................. 81 CSV .......................................................... 193
Benutzer-Accounts verwalten ............... 278

313
Stichwortverzeichnis

D DSN (Data Source Name) ..................... 275


Data Control Language (DCL) ............. 234 Dynamischer Parameter (SQL) ............ 277
Data Definition Language (DDL) ........ 234 E
Data Manipulation Language (DML) . 234,
248 echo ...................................................... 17, 39
Datei Eigenschaft .............................................. 214
Zugriffsrechte ................................... 190 Eingabeaufforderung ............................... 27
Dateiberechtigung .................................. 192 Einkaufswagen ........................................ 210
Dateien Elternklasse ............................................. 222
schreiben und lesen ......................... 180 E-Mail versenden .................................... 306
Datei-Uploads ......................................... 184 Endlosschleife ........................................... 93
dauerhaft speichern ......................... 186 Entitäten
eindeutiger Name ............................ 187 Siehe HTML-Entitäten
Einstellungen .................................... 188 Entwicklungsumgebung .......................... 24
Fehler ................................................. 187 Entwicklungszyklus ................................ 137
Datenbank ...................................... 231, 241 error_log .................................................. 104
löschen .............................................. 242 error_reporting ....................................... 104
non-relationale ................................. 232 Escape-Sequenzen .................................... 52
Performance ..................................... 269 EVA-Prinzip .............................................. 45
relationale ......................................... 231 Exception Handler ................................. 298
Datenbankmanagementsystem ... 231, 232 Exceptions
Datenbankserver .................................... 233 Siehe Ausnahmen
Datenkapselung Exponentialschreibweise ......................... 57
Siehe Geheimnisprinzip Expression
Datensatz ................................................. 231 Siehe Ausdruck
filtern ................................................. 260 F
Datentyp ....................................... 43, 48, 50
Fallunterscheidung ................................... 78
skalarer ................................................ 50
Fehlersichtbarkeit ................................... 103
DateTime ................................................. 228
Fehlerstufen ............................................. 102
DBMS
Flag ..................................................... 30, 161
Siehe Datenbankmanagementsystem
Flash-Nachricht ...................................... 300
Debugging ................................................. 97
Float ............................................................ 57
DEFAULT
for ............................................................... 88
Siehe Vorgabewert (Datenbank)
foreach ........................................................ 86
Deklaration ................................................ 46
Formular
Dekrement ................................................. 56
Inhalte wiederanzeigen ................... 161
DELETE ................................................... 252
versteckte Felder .............................. 293
DESCRIBE ............................................... 247
Formulareingabe ..................................... 277
Dienst ....................................................... 131
Framework .............................................. 310
Direktive (php.ini) .................................. 100
Frontend .................................................... 19
display_errors .......................................... 103
FTP .................................................. 148, 192
Document-Root ...................................... 134
FULLTEXT .............................................. 270
Domain .................................................... 130
Funktion .................................................. 105
Domain Name System (DNS) ............... 130
Arrays ................................................ 115
Dompdf ........................................... 294, 305
Datum und Zeit ............................... 118
DROP DATABASE ................................. 242
Definition ......................................... 123
DROP TABLE ......................................... 247
eigene ................................................ 122

314
Stichwortverzeichnis

mathematisch ................................... 112 INSERT INTO ........................................ 249


native ................................................. 105 Instanz ...................................................... 213
Rückgabewert ................................... 124 Instanziierung ................................ 213, 218
SQL .................................................... 265 Integer .................................................. 48, 54
Strings ............................................... 113 IP-Adresse ............................................... 129
Vorgabewert ..................................... 124 IS NOT NULL ......................................... 262
IS NULL ................................................... 262
G Iteration ..................................................... 85
Ganzzahl
Siehe Integer J
Geheimnisprinzip ................................... 220 JavaScript ................................................... 24
GET-Methode ................................ 165, 169 JSON .................................................. 19, 183
GET-Parameter
Siehe URL-Parameter K
git .............................................................. 310 Kindklasse ............................................... 222
Git Bash ..................................................... 27 Klartext .................................................... 284
GitHub ..................................................... 310 Klasse ....................................................... 213
Gleichheit .................................................. 82 Klausel (SQL) .......................................... 251
Gleitkommazahl Kommandozeile .................................. 25, 26
Siehe Float Kommandozeilenprogramm .................. 20
GUI Konstante ............................................. 43, 67
Siehe Grafische Benutzungsoberfläche benannte ............................................. 68
literale .................................................. 67
H magische ............................................. 69
Header ...................................................... 141 vordefinierte ....................................... 68
Homebrew ................................................. 35 Konstruktor ............................................. 218
Host .......................................................... 131 Kontrollstruktur ....................................... 73
HTML ................................................ 15, 138 alternative Syntax .............................. 94
Element ....................................... 15, 139 Kopfzeile
Tag ....................................................... 15 Siehe Header
HTML-Entitäten ..................................... 164
HTTP ....................................................... 140 L
Laufzeit ...................................................... 98
I Laufzeitfehler ............................................ 98
Identität ...................................................... 82 Lerdorf, Rasmus ....................................... 41
if-Anweisung LIKE ......................................................... 262
Siehe Bedingte Anweisung LIMIT ....................................................... 263
if-else-Verzweigung Literal ......................................................... 51
Siehe Verzweigung localhost ................................................... 132
if-Klausel .................................................... 74 Logging .................................................... 103
Index ........................................................ 270 Login ............................................... 204, 281
INDEX ..................................................... 270 Logische Fehler ......................................... 99
Index-Datei ............................................. 136 Logische Operatoren (SQL) .................. 262
Indizes (SQL) .......................................... 269
Inheritance M
Siehe Vererbung mail() ........................................................ 306
Initialisierung .......................................... 297 MariaDB .................................................. 235
Inkrement .................................................. 55 Maskierung ........................................ 52, 164

315
Stichwortverzeichnis

Maskierung (SQL) .................................. 287 Oracle ....................................................... 235


memory_limit ......................................... 100 ORDER BY .............................................. 251
Metadaten ....................................... 144, 185 Output-Escaping
Methode ................................................... 216 Siehe Ausgabe-Maskierung
statische ............................................. 308
verketten ........................................... 292 P
MIME-Type .................................... 145, 189 Packagist .................................................. 294
MySQL ............................ 231, 234, 255, 273 Parameter ....................................... 106, 123
Konsole ............................................. 239 optional ............................................. 109
mysql-Befehl ........................................... 239 Passwort-Hash ........................................ 284
MySQLi-Erweiterung ............................. 273 Passwort speichern ................................. 284
MySQL-Version ...................................... 278 Payload ............................................ 144, 167
PDF-Download ....................................... 305
N PDO::exec() ............................................. 276
Nameserver ............................................. 130 PDO::lastInsertId() ................................ 292
Namespace ..................................... 306, 308 PDO (PHP Data Objects) ............ 273, 274
NoSQL ..................................................... 232 PDO::prepare() ....................................... 277
NOT ......................................................... 262 PDO::query() .......................................... 276
NULL ......................................................... 58 Permissions
Null coalescing operator ........................ 200 Siehe Zugriffsrechte
NULL (Datenbank) ................................ 245 PHP ............................................................ 13
NULL (SQL) ............................................ 262 Entstehungsgeschichte ...................... 41
Erweiterungen .................................. 102
O installieren .......................................... 31
Objekt .............................................. 212, 213 Interpreter ........................................... 25
Zustand ............................................. 216 PHP/FI ....................................................... 41
Objektorientierte Programmierung ..... 212 phpinfo() .................................................. 137
OFFSET ................................................... 264 php.ini ...................................................... 100
OOP phpMyAdmin ......................................... 255
Siehe Objektorientierte Programmie- Port .................................................... 256
rung PhpStorm ................................................... 38
Operator PHPUnit .................................................. 311
arithmetisch ....................................... 54 Pipe ........................................................... 110
Casting ................................................ 64 Platzhalter (SQL) .................................... 262
Dekrement .......................................... 56 Port ........................................................... 131
Inkrement ........................................... 55 POST-Methode ...................... 166, 169, 293
instanceof .......................................... 223 Präzedenz .................................................. 83
logisch ................................................. 83 Präzedenz (SQL) ..................................... 263
new .................................................... 214 Prepared Statements ............................... 277
Objekt (->) ........................................ 215 Primärschlüssel ....................................... 246
ternär ............................................. 58, 77 PRIMARY KEY ...................................... 270
Vergleich ............................................. 81 Produktionsumgebung ............................ 24
Verknüpfung ...................................... 53 Programmierparadigma ........................ 211
Zuweisung .......................................... 46 Prompt ....................................................... 26
Zuweisung, kombiniert ..................... 55 Protokoll .................................................. 131
Zuweisung, verknüpfende ................ 53 Prozedurale Programmierung .............. 211
Option ........................................................ 30 Punkt-Notation (Datenbank) ............... 248
OR ............................................................. 262

316
Stichwortverzeichnis

Q stateless .................................................... 145


Quelltext .................................................... 25 Statement
Query-String ........................................... 152 Siehe Anweisung
String .................................................... 48, 51
R Superglobals ............................................ 142
RDBMS Syntactic sugar .......................................... 54
Siehe Relationales Datenbankmanage- Syntax ......................................................... 98
mentsystem Syntaxfehler ............................................... 97
readline() ................................................... 44 T
Regulärer Ausdruck ............................... 309
Relationales Datenbankmanagement­ Tabelle (Datenbank) ...................... 231, 243
system ................................................ 233 anlegen .............................................. 243
Request ............................................ 129, 140 löschen .............................................. 247
require ...................................................... 176 Tag .............................................................. 39
Response ......................................... 129, 140 Timestamp
Rückgabewert .......................................... 106 Siehe Zeitstempel
try-catch ................................................... 226
S Type Casting .............................................. 64
Schleife ....................................................... 85 Type Hints ............................................... 125
Schlüssel-Wert-Paar ................................. 59 Type Juggling ............................................ 63
SELECT .................................................... 250 Typisierung
Semantische Strukturierung ................... 15 dynamische ......................................... 49
Server ....................................................... 131 statische ............................................... 50
serverseitig ................................................. 17 U
Session ...................................................... 202
starten ................................................ 202 UNIQUE .................................................. 270
wieder aufnehmen ........................... 203 Unit-Test .................................................. 311
Session-ID ............................................... 203 Unixzeit .................................................... 118
Sichtbarkeit ............................. 215, 217, 218 UNSIGNED ............................................ 245
Signatur .................................................... 108 UPDATE .................................................. 251
Sitzung URL .................................................... 14, 129
Siehe Session URL-Parameter ....................................... 153
Skalare Funktion (SQL) ......................... 265 V
Datum ............................................... 267
numerische ....................................... 267 Validierung .............................................. 155
Zeichenketten ................................... 266 var_dump() ............................................... 48
Skript .......................................................... 26 Variable ................................................ 43, 46
Skriptsprache ...................................... 23, 26 Bezeichner .......................................... 47
Spaghetti-Code ....................................... 212 Geltungsbereich ............................... 126
Spaltenoption (Datenbank) ................... 244 Vererbung ................................................ 221
Spaltentyp (Datenbank) ......................... 243 Vergleichsoperatoren (SQL) ................. 261
Sprache wechseln .................................... 199 Verkettung ............................................... 292
SQL .................................................. 233, 275 Versionsverwaltung ................................ 310
Kommentare ..................................... 241 Verzweigung .............................................. 75
SQL-Befehlskategorien .......................... 234 Visual Studio Code ................................... 36
SQL-Injection .......................................... 285 void ........................................................... 112
Standardsoftware .................................... 310 Volltextsuche ........................................... 271
Vorgabewert (Datenbank) ..................... 245

317
Stichwortverzeichnis

W X
Wahrheitswert XAMPP .................................................... 309
Siehe Boolean XSS
Web-API ............................................ 20, 183 Siehe Cross-Site-Scripting
Webhoster ................................................ 146
Webhosting .............................................. 259 Z
Web Scraping .......................................... 182 Zählschleife ............................................... 88
Webseite Zeichenkette
dynamische ......................................... 14 Siehe String
statische ............................................... 14 Zeitstempel .............................................. 118
Webserver ......................... 14, 131, 133, 137 Zeitzone ................................................... 121
Webservice ................................................ 18 Zugriffsrechte .......................................... 190
Webspace ................................................. 146 Unix ................................................... 191
WHERE-Klausel ............................ 251, 260 Zuweisung ................................................. 44
while ........................................................... 91
Wildcard .................................................. 262

318
Philipp Hasper

C++
Schnelleinstieg
Programmieren lernen in
14 Tagen
Einfach und ohne Vorkenntnisse

Zahlreiche
Praxisbeispiele
und Übungen

• C++ programmieren lernen ohne Vorkenntnisse


• Alle Grundlagen für den professionellen Einsatz
• Einfache Praxisbeispiele und Übungsaufgaben

Mit diesem Buch gelingt Ihnen der einfache Einstieg in die C++-Programmierung.
Alle Grundlagen werden in 14 Kapiteln anschaulich und leicht nachvollziehbar
anhand von Codebeispielen erläutert. Übungsaufgaben am Ende der Kapitel
helfen Ihnen, das neu gewonnene Wissen schnell praktisch anzuwenden und
zu vertiefen.
Der Autor führt Sie Schritt für Schritt in die Welt der Programmierung ein: von
den Grundlagen über Objektorientierung bis zur Entwicklung von Anwendungen
mit grafischer Benutzungsoberfläche. Dabei lernen Sie ebenfalls, was guten Pro-
grammierstil ausmacht und wie man Fehler in Programmtexten finden und von
vornherein vermeiden kann.
So sind Sie perfekt auf den Einsatz von C++ im professionellen Umfeld vorbe-
reitet.

Probekapitel und Infos erhalten Sie unter:


ISBN 978-3-7475-0322-5 www.mitp.de/0322
Michael Weigend

PYTHON 3
Schnelleinstieg
Programmieren lernen in
14 Tagen
Einfach und ohne
Vorkenntnisse zum Profi

Zahlreiche
Praxisbeispiele
und Übungen

• Programmieren lernen ohne Vorkenntnisse


• In 14 Kapiteln Schritt für Schritt zum Profi
• Einfache Praxisbeispiele und Übungsaufgaben

Mit diesem Buch gelingt Ihnen der Einstieg in die Python-Programmierung ohne
Mühe. Sie benötigen keinerlei Vorkenntnisse.
Alle Grundlagen werden anschaulich und einfach nachvollziehbar anhand von
Codebeispielen erklärt. Übungsaufgaben in unterschiedlichen Schwierigkeits-
stufen am Ende der Kapitel helfen Ihnen, das neu gewonnene Wissen praktisch
anzuwenden und zu vertiefen.
Der Autor führt Sie Schritt für Schritt in die Welt der Programmierung ein: von
den Grundlagen über Objektorientierung bis zur Entwicklung von Anwendungen
mit grafischer Benutzungsoberfläche. Dabei lernen Sie ebenfalls, was guten Pro-
grammierstil ausmacht und wie man Fehler in Programmtexten finden und von
vornherein vermeiden kann.
So gelingt es Ihnen in Kürze, Python effektiv in der Praxis einzusetzen.

Probekapitel und Infos erhalten Sie unter:


ISBN 978-3-7475-0328-7 www.mitp.de/0328

Das könnte Ihnen auch gefallen