This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Bleiben Sie einfach auf dem Laufenden: www.hanser.de/newsletter Sofort anmelden und Monat für Monat die neuesten Infos und Updates erhalten.
Jörg Krause Programmieren lernen in
PHP 5
Der Autor: Jörg Krause, Berlin
www.hanser.de
Alle in diesem Buch enthaltenen Informationen wurden nach bestem Wissen zusammengestellt und mit Sorgfalt getestet. Dennoch sind Fehler nicht ganz auszuschließen. Aus diesem Grund sind die im vorliegenden Buch enthaltenen Informationen mit keiner Verpflichtung oder Garantie irgendeiner Art verbunden. Autor und Verlag übernehmen infolgedessen keine Verantwortung und werden keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieser Informationen – oder Teilen davon – entsteht, auch nicht für die Verletzung von Patentrechten, die daraus resultieren können. Ebenso wenig übernehmen Autor und Verlag die Gewähr dafür, dass die beschriebenen Verfahren usw. frei von Schutzrechten Dritter sind. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt also 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.
Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar.
Schnellübersicht Die folgende Übersicht hilft Ihnen, die für Sie relevanten Kapitel schnell zu finden. Sie finden passend dazu auch eine Griffleiste am rechten Rand.
Schnellübersicht ..................................................................................................5 Vorwort.................................................................................................................7 Inhaltsverzeichnis ...............................................................................................9 1 Vor dem Start .............................................................................................17 2 Dynamik in der Webseite.........................................................................25 3 Variablen und Konstanten .......................................................................45 4 Rechnen und Verarbeiten.........................................................................55 5 Komplexe Datenstrukturen: Arrays ........................................................83 6 Das Steuerrad für PHP: Kontrollstrukturen...........................................99 7 Sammeltransporte: Objekte....................................................................137 8 Daten bewegen: Dateisystem und FTP.................................................161 9 Daten aus dem Browser: Formulare und Links ...................................195 10 Big Brother: Sessions und Cookies ......................................................223 11 Datenbanken: So werden Daten gespeichert .......................................249 12 Symbiose: PHP und JavaScript ..............................................................297 13 1000 kleine Helfer: Die PHP-Funktionen .............................................313 14 Fehlersuche und Konfiguration.............................................................347 A Listings......................................................................................................367 B Quickreferenz MySQL 4.........................................................................373 C Empfehlungen..........................................................................................387 D Index..........................................................................................................391 E An den Autor............................................................................................399
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
Vorwort
7
Vorwort Mit der Version 5 erreicht PHP ein Entwicklungsstadium, das die Webskriptsprache fest in der Welt der Webprogrammierung etabliert. Vor allem der Wegbereiter der Webprogrammierung – Perl – dürfte es in Zukunft noch schwerer haben, seine Anhänger zu begeistern. Die einfache Formel lautet: Wenn kleine und mittlere Webprojekte privat oder kommerziell und meist im Hosting beim Provider betrieben werden, ist PHP die allererste Wahl. Die gute Nachricht für den Rest der Welt – also J2EE und ASP.NET – ist, dass bei PHP5 alles so bleibt wie es war. Viele neue Funktionen, die unübersichtlichen Bibliotheken und ein chaotisches Benennungssystem belassen die Sprache PHP5 konsequent in der Ecke der Bastler und Semiprofis. Wer sich da einbildet, das ganz große Ding mit PHP5 schreiben zu können, fischt leider am falschen Ufer. Die gute Nachricht für die Fans – und solche die es werden wollen – ist die Tatsache, dass man immer noch einfach so drauflos programmieren kann. Neue OOP-Techniken stellen Entwicklern gute Ergänzungen bereit, und eine Vereinheitlichung der XML-Bibliotheken schafft Sicherheit in der Projektplanung. Es hat lange, sehr lange gedauert, bis sich die Community zu einer neuen Version durchringen konnte. Es kann als Sieg der Einfachheit gefeiert werden, dass Applikationsserver, Framework und echtes OOP dabei durchgefallen sind. Denn mit diesen Funktionen wäre PHP5 vergleichbar geworden mit anderen, inzwischen nicht minder etablierten Sprachen. Auf diesem Niveau käme dann PHP nicht wirklich gut weg. Andererseits gibt es eben einen gigantischen Markt an Homepagebastlern und kleinen, kommerziellen Anwendern, die von PHP erheblich profitieren. Denen nützen Applikationsserver, Framework und echtes OOP nicht wirklich; der Preis – Inkompatibilität, Lernkurve, Ressourcenhunger – wäre zu hoch. Die Community hat richtig entschieden – alles bleibt wie es war. PHP5 verfügt über die neue Zend Engine 2.0, den von Zend entwickelten Sprachkern. Das ist der Teil, den niemand sofort bemerkt. Die Änderungen, die den Versionssprung von 4 auf 5 rechtfertigen, fanden also vor allem im Verborgenen statt. Nicht zuletzt deshalb bietet dieses Buch gegenüber dem Vorgängertitel neben dem Neuen auch viel Bewährtes an. Jörg Krause Berlin, im März 2004
Werden Sie ein guter Programmierer!..................................................29
2.2.1
Ein wenig Theorie ....................................................................................................... 29
2.2.2
Mit Paradigmen vom Problem zur Lösung .............................................................. 30
2.2.3
Programmieren mit Stil .............................................................................................. 31
2.3
PHP ist einfach........................................................................................32
2.3.1
Sofort etwas ausprobieren .......................................................................................... 32
2.3.2
Wo wird programmiert?............................................................................................. 33
2.3.3
Wo wird PHP abgearbeitet? ....................................................................................... 33
2.3.4
PHP und HTML .......................................................................................................... 34
2.4 2.4.1
Erstellen und Testen...............................................................................38 Sind Sie startklar?........................................................................................................ 38
3
Variablen und Konstanten ......................................................... 45
3.1
Variablen erzeugen und erkennen........................................................47
Index ............................................................................................ 391
E
An den Autor.............................................................................. 399
1.1 Wer sind Sie?______________________________________________________ 17
1 Vor dem Start Bevor Sie anfangen, die ersten Schritte als PHPEntwickler zu gehen, sollten Sie dieses Kapitel lesen. Es wird Ihnen helfen, das Buch sehr effektiv zu verwenden.
1
Vor dem Start
1.1 Wer sind Sie?______________________________________________________ 19
1.1
Wer sind Sie?
Wenn Sie dieses Buch gerade durchblättern – im Buchladen oder bereits Ein Wort zur zu Hause vor dem Computer – werden Sie sich vielleicht fragen: »Ist das Zielgruppe das richtige Buch für mich?« Ein paar Vermutungen, wer Sie sein könnten, habe ich als Grundlage beim Schreiben angestellt: • Sie sind Webentwickler und haben bisher Webseiten in HTML, JavaScript und Flash erstellt. • Sie haben eine kleine Webagentur oder arbeiten bei einer solchen. • Sie machen eine Umschulung zum Webprogrammierer oder planen diese. • Sie haben sich schon immer für das Internet interessiert, sich bisher aber nie getraut, selbst etwas zu programmieren. Vielleicht suchen Sie nur einfach nach Wegen, Ihre Webseiten cooler und interessanter zu machen. Sie haben gehört, das PHP eine spannende, umfangreiche und leicht zu erlernende Sprache ist. Außerdem können Sie bereits mit Windows umgehen und wissen, wie man Dateien anlegt, bearbeitet und speichert. PHP ist eine Skriptsprache, die für die Erstellung dynamischer Webseiten eingesetzt wird. Dieses Buch behandelt nur diesen Anwendungsfall. Darin eingeschlossen ist der Zugriff auf Datenbanken, Dateien und auf die Eingaben, die Benutzer über den Browser absenden. Das Buch zwingt Ihnen keinen speziellen Lernstil auf und verzichtet Für jeden Lernstil deshalb auf eine straffe Kapitelfolge, schulmeisterliche Fragen am geeignet Kapitelende und ein Zeitregime. Lesen Sie es von Anfang bis Ende oder überspringen Sie Abschnitte, wenn der Stoff Ihnen bekannt oder uninteressant erscheint. Die einzige sinnvolle Lernmethode lautet »üben, üben, üben«.
Was Sie mitbringen sollten Dieses Buch setzt keine umfassenden Programmierkenntnisse voraus. Allerdings sollten Sie prinzipiell mit der Erstellung von statischen Webseiten – also mit HTML – vertraut sein. Ebenso wird vorausgesetzt, dass das eingesetzte Betriebssystem beherrscht wird. Wenn Sie schon erfolgreich Seiten auf einen Webserver beim Provider überspielt und zum Laufen gebracht haben, sind alle wichtigen Voraussetzungen erfüllt.
1.2
Hinweise zum Buch
In diesem Abschnitt finden Sie allgemeine Hinweise zum Buch und zur Schreibweise.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
20
______________________________________________________ 1 Vor dem Start
1.2.1 Schreibweise von reservierten Namen
Schreibweisen im Buch
Dieses Buch stützt sich auf die übliche Schreibweise bei der Syntaxangabe. Da PHP überwiegend aus Funktionen besteht, wird die Funktionsschreibweise primär zu finden sein. Der folgende Ausdruck beschreibt das Syntaxdiagramm einer Funktion:
int function(string param1, 0|1 [, int param2]) Diese Schreibweise tritt auch im Fließtext auf und ist folgendermaßen zu interpretieren. Der Funktionsname ist fett hervorgehoben. Wenn die Klammern zwingend erforderlich sind, gehören sie zum Funktionskopf und sind ebenso hervorgehoben. Vor der Funktion steht der Datentyp (int), den die Funktion zurückgibt. Im Beispiel gibt die Funktion also ganzzahlige Werte zurück. Parameter sind alternativ aber nicht optional, wenn sie durch | getrennt sind. Parameter in []-Klammern sind optional. Der Aufruf der Funktion könnte im Skript so aussehen:
$intvar = function("test", 1, 45); Soweit es sich vermeiden ließ, wurde auf reine Syntaxdiagramme verzichtet. Dafür gibt es eine umfassende Referenz. Selbst gewählte Namen
Selbst gewählte Namen, beispielsweise für Variablen, werden kursiv gesetzt: $time, myfunction.
Skripte und Dateien
An einige Stellen wird auf Skripte oder Dateien verwiesen. Diese stehen dann in Kapitälchen: PHP.INI.
1.2.2
Wichtige Symbole
Im Buch wird an vielen Stellen mit Symbolen gearbeitet, um die Orientierung zu erleichtern. Erklärungen mit diesem Symbol beziehen sich auf das jeweils davor stehende Listing. Alle Skripte werden ausführlich diskutiert, damit Sie die Funktion sofort nachvollziehen können. Dieses Symbol weist auf Warnungen und Hinweise hin, die im Zusammenhang mit dem betrachteten Stoff stehen. Oft sind auch typische Fehlermeldungen damit gekennzeichnet.
Der Infokasten Im Infokasten finden Sie Erklärungen zu wichtigen Begriffen, Hintergrundinformationen und Grundlagen.
1.2.3
Was nicht aufgeführt wurde
Dieses Buch soll schnell und kompakt in die Welt der Programmierung von Webseiten einführen. Breiten Raum nimmt deshalb die Erläuterung grundlegender Methoden und Techniken ein. PHP5 besticht den professionellen Programmierer dagegen durch eine Vielfalt von Funktionen, die oft elegante Lösungen ermöglichen. Darauf wird nicht in aller Breite
1.3 Installation ________________________________________________________ 21 eingegangen. Einige Kapitel enthalten jedoch Übersichten der verfügbaren Funktionen. In der Referenz zu diesem Buch können Sie sich über die genaue Verwendung aller Funktionen informieren.
1.2.4
Navigationshilfen
Im Buch finden Sie zwei Navigationshilfen für ein schnelles Auffinden So arbeiten Sie der passenden Lösung Ihres Problems. Beachten Sie allerdings, dass mit dem Anhang dieses kleine Buch keinen Anspruch auf Vollständigkeit erhebt oder als Lösungsbuch konzipiert wurde, sondern in die grundlegenden Programmiertechniken einführen soll. • Anhang A Listings (ab Seite 367) Hier finden Sie die Listings auf einen Blick. Alle Skripte können Sie sofort zur Ausführung bringen. Wer nichts abschreiben will, findet auf der Webseite zum Buch alle Skripte zum herunterladen und zum ausprobieren. • Anhang D Index (ab Seite 391) Der Index umfasst neben den normalen Stichworten auch alle Vorkommen von PHP-Funktionen. Die relevante Stelle ist, wenn vorhanden, fett gekennzeichnet. Alle anderen Vorkommen deuten meist auf die Verwendung in anderen Skripten hin. Außerdem wird intensiv mit Verweisen gearbeitet, die durch ein Symbol gekennzeichnet sind. Folgen Sie den Verweisen, um schneller an alle relevanten Informationen zu gelangen. Ein chronologisches Durcharbeiten ist dagegen nicht immer die beste Wahl.
1.3
Verweis...
Installation
Vor den ersten Schritten mit PHP steht – wenn Sie nicht online arbeiten möchten oder wollen, die Installation. Da sich die Installation je nach Version etwas unterscheidet und darüber hinaus viele Varianten beim Betriebssystem existieren, würde eine Installationsbeschreibung, die alle Ansprüchen gerecht wird, das halbe Buch in Anspruch nehmen. Anstatt vieler Seiten finden Sie hier die besten deutschen Tutorials im Web, die die Installation genau beschreiben. Starten Sie dort, um Ihr Entwicklungssystem aufzubauen. Die Tutorials werden gelegentlich aktualisiert, sodass Sie mit der neuesten PHP-Version funktionieren. Dies kann ein Buch, dass über einen längeren Zeitraum angeboten wird, nicht leisten.
1.3.1
LAMP
LAMP steht für »Linux Apache MySQL PHP«. Jörg Baach hat auf seiner Für Suse-Linux Site eine Anleitung, die sich auf die SUSE-Distribution stützt:
www.baach.de/lamp-tutorial.html
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
22
______________________________________________________ 1 Vor dem Start
Für Debian-Linux
Inspiriert von diesem Tutorial gibt es eine modifizierte Ausgabe, die auf die Debian-Distribution ausgelegt ist:
www.linux-root.de/elug/lamp.html
1.3.2
WAMP
WAMP steht für »Windows Apache MySQL PHP«. Hier wird als Betriebssystem Windows eingesetzt – der Rest unterscheidet sich nicht von der Linuxvariante. Einfacher ist die Installation vor allem deshalb, weil meist fertige Binärdateien eingesetzt werden und die Kompilation wegfällt. Ein gutes Tutorial von Ron Grahnert finden Sie hier:
www.php-homepage.de/artikel/?nr=8
1.3.3
WPMP
Die Abkürzung WPMP steht für »Windows PWS MySQL PHP«. Eine Anleitung für den PWS finden Sie unter folgender Adresse:
www.webknecht.de/php/index.html Der PWS wird mit Windows 95/98 und der NT 4.0 Workstation geliefert. Windows 2000, XP Professional und Windows Server 2003 verwenden dagegen den IIS, siehe nächster Abschnitt.
1.3.4
WIMP
WIMP bezeichnet »Windows IIS MySQL PHP« und bezieht sich auf den IIS 4, 5 oder 6, also auf die Webserver von Windows Me, Windows 2000 Workstation, XP Professional und Windows 2000/2003 Server sowie Windows NT 4.0 Server mit Option Pack. Eine deutsche Anleitung finden Sie unter:
Generell ist auch das Manual zu PHP eine gute Anlaufstelle. Hier finden Sie neben den bereits gezeigten Webservern weitere Hinweise. Leider sind noch nicht alle Teile übersetzt. Den umfassenderen englischen Text finden Sie an folgender Stelle:
www.php.net/manual/en/installation.php Die (teilweise) deutsche Version finden Sie hier:
http://www.php.net/manual/de/installation.php
1.4 Softwarequellen ____________________________________________________ 23 Windows: IIS/PWS/OmniHTTPd Ältere Windows-Webserver und der OmniHTTPd werden auf der folgenden Seite beschrieben:
http://www.php.net/manual/de/install-windows95-nt.php Unix: Andere Unixe Neben Linux wird PHP oft auch auf Solaris oder anderen Unixen einge- Solaris setzt. Die folgende Adresse hilft Ihnen dabei, PHP korrekt zu installieren: BSD
http://www.php.net/manual/de/install-unix.php
1.4
Softwarequellen
Nachfolgend finden Sie eine Liste mit Adressen, unter denen Sie sich die neueste Software beschaffen können. • PHP-Quellen und Binärversionen für Windows
http://www.php.net • Apache Webserver
http://www.apache.org • MySQL
http://www.mysql.com PHP5 verfügt über die integrierte Datenbank SQLite, die für einfachste Projekte ausreichend ist. Für die ersten Schritte mit Datenbanken ist deshalb MySQL nicht mehr zwingend erforderlich.
1.5
Hilfe im Web
Hilfe im Web versprechen viele Webseiten. Die besten englischen und Englische und deutschen Seiten finden Sie hier: Deutsche • Die internationale PHP-Homepage (engl.)
www.php.net • Die Zend-Homepage (engl.)
www.zend.com • Das deutsche PHP-Center (dt.)
www.php-center.de • PHP-Homepage (dt.)
www.php-homepage.de
Websites
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
24
______________________________________________________ 1 Vor dem Start • Dynamic Webpages (dt.)
www.dynamic-webpages.de • PHP Builder (engl.)
www.phpbuilder.com • Das PHP-Archiv (dt.)
www.phparchiv.de
1.6 Die Website nur für Leser
Website zum Buch
Speziell für die Leser dieses Buches gibt es eine Website unter der folgenden Adresse (klicken Sie auf die Beschriftung neben dem Buchcover):
http://www.php.comzept.de Dort finden Sie unter anderem: • Forum zur Kontaktaufnahme mit anderen PHP-Anfängern • Alle Skripte zum Buch – als ZIP-Archiv zum Herunterladen • Aktualisierungen zu den im Buch verwendeten Skripten • Hinweise auf Neuerscheinungen, Projekte usw., die für PHPEntwickler interessant sind • Kontaktmöglichkeit zum Autor für Kritik, Hinweise usw. Was ich leider nicht leisten kann Für die praktische Programmierung gibt es viele Foren, Newsgroups und Mailinglisten und sehr viel weiterführende Literatur. Bitte haben Sie Verständnis dafür, dass ich Anfragen nach technischer Hilfe im Allgemeinen nicht beantworte. Anregungen werden jedoch auf jeden Fall gelesen und gehen in Aktualisierungen auf der Website und künftige Auflagen des Buches ein. Schreiben Sie per E-Mail an folgende Adresse:
Dieses Buch behandelt PHP in der Version 5.0. Alle Skripte wurden mit dieser Version und den Standardeinstellungen getestet. Es mag sein, dass einige Skripte mit älteren Versionen funktionieren, diese sind jedoch nicht Gegenstand des Buches und wurden nicht getestet. Dieses Buch behandelt ausschließlich PHP5. Es wurde größtenteils mit der dritten Beta-Version geschrieben und mit dem ersten Release Candidate (RC1) überarbeitet. Es ist möglich, dass die finale Version oder künftige Ausgaben von PHP5 in Details ein davon abweichendes Verhalten aufweisen.
1.7 Welche PHP-Version? ______________________________________________ 25
2 Dynamik in der Webseite HTML ist die Sprache des Internet. Jede Website besteht aus mehr oder weniger gut geschriebenem HTML, kombiniert mit Bildern, Flash-Animationen und im Browser ablaufenden Skripten. All das zusammen sorgt zwar für bunte Seiten oder schrille Effekte, nicht jedoch für Interaktivität. Eine Reaktion des Servers auf Benutzereingaben wird nur erzielt, wenn die empfangenen Daten verarbeitet werden. Eine wichtige Sprache, mit der dies erfolgen kann, ist PHP – PHP Hypertext Preprocessor.
2
Dynamik in der Webseite
2.1 HTML ist langweilig_________________________________________________ 27
2.1
HTML ist langweilig
Bevor Sie beginnen, die ersten Skripte zu schreiben, denken Sie einen Wie Webserver Moment darüber nach, wie ein Webserver arbeitet. Wenn Sie den Brow- arbeiten ser öffnen, und eine Adresse eingeben, zerlegt der Browser diese zuerst in zwei Teile. Betrachten Sie folgende Adresse:
http://www.php.comzept.de/php-lernen/index.php Diese Angabe besteht zum einen aus dem Protokoll: http. Dies steht für Hypertext Transfer Protocol und definiert die »Sprache«, in der Webserver und Browser miteinander reden. Danach folgt die Adresse des Servers, bestehend aus seinem eigenen Namen »www.php« und der Domäne »comzept.de«. Dieser Name wird zuerst in eine IP-Nummer, die weltweit eindeutig ist, aufgelöst. Das passiert aber transparent im Hintergrund und wird normalerweise vom Benutzer nicht wahrgenommen. Der dritte Teil enthält nun Pfad- und Dateiangaben, hier »phplernen/index.php«. Diesen Teil sendet der Browser mit dem entsprechenden HTTP-Kommando GET an den Server. Der reagiert darauf entweder mit der Auslieferung der angeforderten Datei oder einer Fehlerinformation, wenn die Datei nicht gefunden wurde. So war das Web ursprünglich konzipiert worden. HTML-Seiten sollten nur durch Hyperlinks verbunden werden. Das genügte, um Informationen strukturiert darzustellen und Designern eine gewisse Freiheit bei der Darstellung zu erlauben.
2.1.1
Mysterium CGI
Das Ziel einer solchen Anforderung kann auch ein Skript auf dem Web- Was ist CGI? server sein. Der Webserver ruft dann das mit der Dateierweiterung verbundene Programm auf. Dessen Ausgabe wiederum wird an den Browser gesendet. Praktisch können Sie also mit einem Skript alles kontrollieren was zum Browser geht. Die Schnittstelle zwischen Webserver und solchen Programmen zur Ausführung von Skripten ist sehr alt, sehr gut dokumentiert, sehr bekannt und sehr langsam. Ihr Name ist CGI – Common Gateway Interface. CGI selbst macht nichts. Die erste Skriptsprache, die sich CGI bediente, war Perl. Perl ist sehr weit verbreitet und kann virtuos mit Zeichen umgehen. Leider ist Perl auch sehr schwer zu erlernen und für Anfänger nicht zu empfehlen. Mit CGI ist es möglich, die Anforderungen eines Browsers auf ein Skript CGI ermöglicht umzuleiten. Solche Anforderungen können neben der Adresse einer Interaktivität Datei auch die Inhalte eines Formulars sein. Damit besteht nun die Möglichkeit, den Inhalt eines Formulars, das der Benutzer ausgefüllt hat, zu verarbeiten und darauf gezielt zu reagieren. Diese Reaktion kann sehr vielfältig ausfallen. Sie können das CGI-Skript veranlassen, die Daten zu überprüfen und dem Benutzer eine Fehlermeldung zurückzusenden, wenn er etwas vergessen hat. Oder Sie legen die Daten in einer Textdatei
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
28
____________________________________________ 2 Dynamik in der Webseite ab, die Sie sich später anschauen. Wenn Sie schon etwas professioneller programmieren, werden Sie dagegen eine Datenbank verwenden.
Vor- und Nachteile von CGI
Über CGI wurden viele Dinge geschrieben. Ein wichtiger Kritikpunkt ist die Geschwindigkeit. CGI startet mit jeder Anforderung einen so genannten Prozess auf dem Server, also eine Instanz des Skript-Interpreters. Wenn gleichzeitig 50 Benutzer zugreifen, werden 50 Instanzen gestartet, jede mit einigen Megabyte Speicherverbrauch. Ganz realistisch muss man heute sagen, hat die Entwicklung der Hardwareressourcen diesen Nachteil fast beseitigt. 50 Megabyte Speicher sind sehr wenig und 50 gleichzeitige Besucher sind verdammt viel. PHP kann man deshalb gut – sowohl auf der Entwicklungsmaschine auch auf einem Server im Netzwerk, als CGI-Applikation laufen lassen. Damit ist auch klar, das CGI nicht gleich Perl ist, in diesem Buch ist CGI gleich PHP.
Wie es arbeitet
Ein anderer Nachteil ist die Arbeitsweise des Protokolls HTTP. Jede Anforderung des Browsers ist ein für sich abgeschlossener Prozess. Da CGI den Interpreter jedes mal startet und danach wieder aus dem Speicher entfernt, gehen auch alle Einstellungen verloren. Das »Wiedererkennen« des Benutzers auf der nächsten Seite stellt deshalb ein Problem dar. Dank PHP stehen dafür aber spezielle Funktionen zur Verfügung – die Sessionfunktionen. Darauf wird selbstverständlich detailliert eingegangen. Also ist auch dieser Nachteil nicht wirklich schlimm.
2.1.2 Schnittstellen der Webserver
Was Webserver noch können
Webserver bieten eigene Schnittstellen. Diese sind – im Gegensatz zu CGI – von Server zu Server verschieden. Glücklicherweise gibt es heute nicht mehr sehr viele konkurrierende Webserver. In der PHP-Welt, wo Produktionsserver überwiegend unter Linux laufen und für Entwicklungsumgebungen Windows verwendet wird, dominiert der Apache Webserver, gefolgt von dem Internet Information Server von Microsoft. Beide eignen sich für den Betrieb von PHP und für beide gibt es neben der CGIVersion auch eine spezielle Variante, die auf die jeweiligen proprietären Schnittstellen zugreift. Damit beschäftigt sich dieses Buch aber nicht, weil es nicht beim Programmieren hilft und meist keinen Unterschied macht, ob das Skript mit einer Webserverschnittstelle oder CGI läuft. Die zusätzlichen Möglichkeiten der Webserverschnittstellen mögen Profis zu schätzen wissen, die eigene Server betreiben und ihre Applikationen darauf optimieren. Wenn Sie mit Ihren Skripten zu irgendeinem Provider gehen, werden Sie jedoch oft auf Server mit CGI treffen und solche optimierten Skripten funktionieren dann nicht. Beschäftigen Sie sich damit erst, wenn Sie PHP richtig gut können und ein eigener Server zur Verfügung steht.
2.1.3 Was PHP alles kann
Hochgesteckte Erwartungen
PHP löst viele Probleme, die CGI-Programmierer mit Perl in den vergangenen Jahren hatten und immer noch haben. Viele Dinge sind »einfach da«. Es wird dennoch immer wieder in speziellen Kästen auf diese Hin-
2.2 Werden Sie ein guter Programmierer!_________________________________ 29 tergründe hingewiesen, weil Sie nur so lernen, was dahinter steckt. Wenn etwas nicht wie erwartet funktioniert oder Sie komplexere Probleme angehen, hilft dieses Wissen.
2.2
Werden Sie ein guter Programmierer!
Die Programmierung in PHP ist sehr einfach. Das verleitet zu einem wirren und unsauberen Stil bei der Notation der Befehle. Wenn Sie gute Software schreiben möchten, müssen Sie sich auch ein wenig mit dem Stil auseinandersetzen. Auch darauf wird in diesem Buch eingegangen.
2.2.1
Ein wenig Theorie
Manche Leute studieren ein paar Jahre Informatik, um Programmierer zu werden. Andere tun das nicht und werden auch Programmierer. Vielleicht fragen Sie sich auch: »Warum also Informatik lernen, wenn es auch ohne geht?« In der Praxis hat sich herausgestellt, dass viele Anfänger an bestimmten Problemen scheitern oder ungewöhnlich schlechte Lösungen finden, weil bestimmte Grundlagen fehlen. Dieses Buch kann und soll kein Studium der Informatik ersetzen. Es wäre naiv zu glauben, dass man nur anhand einer modellhaft gezeigten Programmiersprache wirklich programmieren lernen kann. In diesem Abschnitt werden dennoch einige Grundlagen gezeigt. Wenn Sie Quereinsteiger sind, opfern Sie ein paar Minuten, um den nachfolgenden Text zu lesen. Er hilft Ihnen, sich auch bei kleinen Projekten nicht völlig zu blamieren. Syntax und Semantik Im Zusammenhang mit der Beschreibung von Programmiersprachen ist immer von den Begriffen Syntax und manchmal auch Semantik die Rede. Die Syntax beschreibt die äußere Gestaltung des Vokabulars der Sprache. Syntax Dazu gehört der Aufbau der Befehle, Vorschriften über den Aufbau des Programms und auch die Verwendung von Parametern. Die Semantik klärt die Bedeutung der Anweisung. Programmierer und Semantik auch Computer erfahren, was mit dem Code zu tun ist, wenn er ausgeführt wird. Compiler und Interpreter Bei der Konstruktion von Programmen, die Software ausführen, haben sich zwei grundlegende Konzepte herausgebildet. Interpreter lesen ein Programm zeilenweise, analysieren die Zeichen und Interpreter übersetzen sie dann in die Maschinensprache des Computers. Dieser führt den Code dann aus. Compiler lesen das gesamte Programm und übersetzen es in Maschinen- Compiler code, den der Prozessor ausführen kann. Erst wenn dieser Prozess abgeschlossen ist, wird der fertige Code als ausführbare Datei abgelegt und kann nun beliebig oft gestartet werden.
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
30
____________________________________________ 2 Dynamik in der Webseite
PHP ist ein Interpreter
Interpreter sind einfacher zu bedienen und zu konstruieren, dafür jedoch langsamer, weil der Code gegebenenfalls mehrfach übersetzt wird. Compiler sind komplexer und aufwändiger, der erzeugte Code meist schneller. PHP ist als Interpreter implementiert.
2.2.2
Mit Paradigmen vom Problem zur Lösung
Wenn Sie eine bestimmte Aufgabenstellung mit Hilfe eines Programms lösen müssen, sollten Sie einen systematischen Weg kennen, der dies effektiv ermöglicht. Sich einfach an den Editor zu setzen und drauflos hacken, führt nur selten zum Erfolg. Auch wenn das Programm letztendlich funktioniert, ist die Qualität doch fraglich. Spätere Änderungen oder Erweiterungen werden möglicherweise erschwert oder ganz unmöglich. Programmierparadigmen Imperative Programmierung
Die Programmierung unterliegt einem bestimmten Paradigma, das von der gewählten Sprache und den dahinter liegenden Konzepten abhängt. PHP wird nach dem Prinzip der imperativen Programmierung verarbeitet (vom lat. »imperare«, befehlen). Programme bestehen also aus einer Folge von Befehlen, die bestimmte Aktionen auslösen. Diese Art von Sprachen basiert wesentlich auf dem Konzept der Variablen, bei denen Daten einem Speicherplatz zugeordnet werden, der unter einem gewählten Namen verfügbar ist. Die Programmiersprache kümmert sich dabei um den benötigten Platz für die Daten und die Verwaltung des Speichers.
Applikative und prädikative Programmierung
Nur der Ordnung halber sei an dieser Stelle auf zwei andere Konzepte verwiesen. Applikative Programmiersprachen sind so organisiert, dass das Programm durch die Anwendung von Funktionen besteht. Typischer Vertreter ist LISP. Prädikative Programmiersprachen verlangen die Formulierung von Beweisen (Prädikaten) als Handlungsanleitung für den Computer. Wichtigster Vertreter ist PROLOG.
Objektorientierte Programmierung
In den letzten Jahren hat sich die Programmierung mit Objekten weitreichend durchgesetzt. Fast alle Programmiersprachen bieten eine Unterstützung für dafür, auch PHP. Indes ist PHP keine objektorientierte Sprache, weil ihr grundlegende Eigenschaften dazu fehlen. Echte objektorientierte Sprachen betrachten alle Elemente – auch Variablen – als Objekte. Diese zeichnen sich durch das Vorhandensein von Eigenschaften und Methoden aus. Bekannte Vertreter sind Java und C# , in der Skriptwelt ist die konsequenteste Umsetzung Ruby.
Mischformen
Viele Sprachen kann man nicht sicher einem bestimmten Paradigma zuordnen. So ist C++ sicher eine objektorientierte Programmiersprache. Die Ableitung von der imperativen Programmiersprache C führte jedoch dazu, dass der Programmierer beide Konzepte verwenden und auch imperativ entwickeln kann.
2.2 Werden Sie ein guter Programmierer!_________________________________ 31
2.2.3
Programmieren mit Stil
Sicher hat jeder Programmierer seinen Stil und kennt viele mögliche Auch Programme Varianten für eine saubere Programmierung. Anfänger sollten sich je- haben Stil doch nicht von fremden stilistischen Formen leiten lassen, ohne den Sinn zu erkennen, der hinter der einen oder anderen Schreibweise steckt. Die besonderen Anforderungen des Webs sind auch nicht jedem Profi völlig vertraut. Die folgenden Tipps zeigen, worauf es ankommt. Eine auch optisch ansprechende Codierung wird erreicht, wenn Sie: • Code-Konventionen einhalten und • Formatierung und Strukturierung beachten.
Das doppelköpfige Wesen einer dynamischen Webseite Dynamische Webseiten bestehen aus zwei grundlegenden Teilen: Code und Design. Das Design einer Webseite wird mit HTML, Cascading Style Sheets und Bildern erstellt. Der Code dagegen wird mit PHP erstellt. Die konsequente Trennung erleichtert die Programmierung und Wartung.
Trennen Sie Code vom Design Die Trennung hilft, leicht beherrschbare Programmteile zu erhalten. Die Wiederverwendbarkeit des Codes wird gesteigert. Sie können mit Gestaltungswerkzeugen arbeiten, die eingeschlossene Codes nicht verstehen. In großen Teams können Sie das Design von Designern erledigen lassen und die reine Programmierarbeit Softwareentwicklern übergeben. Zur Wiederverwendbarkeit von Design und Code Wenn Sie etwas ändern möchten, müssen Sie es nur an einer Stelle tun. Konsistente Gestaltungsmerkmale und Bedienerführung erleichtern den Nutzern die Navigation. Behandeln Sie deshalb Design-Elemente und Code als Komponenten, die Sie immer wieder verwenden. Verwenden Sie keine komplexen URLs URLs mit vielen Parametern zeigen Hackern die innere Struktur Ihrer Applikation und legen Angriffspunkte offen. Nutzer werden vielleicht auch Ihre Seite – und damit den URL – als Lesezeichen ablegen. Wenn Sie die innere Struktur ändern, verlieren Sie Leser, die über die Lesezeichenverwaltung des Browsers auf Ihre Site zugreifen. Unterstützen Sie Proxies und Caches Nicht jeder Nutzer hat schnelle Festverbindungen. Wenn der Cache des Browsers unterstützt wird, verkürzen Sie aktiv die Ladezeiten. Nutzen Sie dynamische Seiten nur dort, wo es sinnvoll ist.
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
32
____________________________________________ 2 Dynamik in der Webseite
Ein paar wichtige Begriffe »Proxies« sind Computer, die innerhalb eines Netzwerks alle Anfragen aus dem Netzwerk abfangen und selbst an das äußere Netzwerk weiterleiten. Ankommende Daten werden vom Proxy empfangen und an das innere Netzwerk weitergeleitet. Dabei können vielfältige Speicher- und Filtermaßnahmen definiert werden, u.a. auch Firewall-Funktionen. Als »Cache« wird ein kleiner, aber sehr schneller Zwischenspeicher bezeichnet. Häufiger benötigte Daten werden vom Cache schneller geliefert als es der Standardspeicher des Systems ermöglicht.
2.3
PHP ist einfach
PHP ist eine Skriptsprache. Im Unterschied zu einer als Compiler implementierten Programmiersprache müssen die Codes nicht übersetzt werden. Sie schreiben Ihr Programm einfach auf, in einem normalen Editor, und führen es dann aus. Dies geschieht durch Aufruf des Programms im Browser. PHP selbst – konkret die ausführbaren Dateien – arbeitet mehr im Hintergrund. Eigentlich sollten Sie nie in die Verlegenheit kommen, am Prompt oder in einem Fenster außerhalb des Browsers PHP aufzurufen.
2.3.1 Skripte lokal ausführen
Sofort etwas ausprobieren
Wenn Sie gleich loslegen möchten, aber keine Lust haben die Skripte abzuschreiben, gehen Sie folgendermaßen vor. Sie installieren sich lokal PHP und laden die fertigen Skripte von der Website. Die ZIP-Datei mit allen Skripten können Sie unter dieser Adresse laden:
http://www.php.comzept.de/lernen/skripte5.zip Dies kostet weniger Online-Gebühren als bei der Ausführung auf einer Website. In Abschnitt 1.3 Installation ab Seite 21 finden Sie Adressen für Installationsanleitungen für alle wichtigen Betriebssysteme und Webserver. Diese Anleitungen sind aktueller und umfangreicher, als es die Darstellung in einem Buch erlauben würde. Spielt Windows eine Rolle? Windows versus Linux
Die meisten Webserver im Internet nutzen Linux und den Webserver Apache. Die meisten Programmieranfänger nutzen jedoch WindowsComputer, weil Sie dort mit Office-Paketen, Grafikprogrammen und Editoren vertraut sind. PHP läuft unter Linux und Windows gleichermaßen. Sie können also Ihre vertraute Umgebung unter Windows weiter verwenden und die fertigen Applikationen dann auf einem leistungsfähigen Unix-Server einem größeren Publikum vorstellen. Insofern spielt Windows schon eine Rolle. In diesem Buch wird auf Betriebssysteme
2.3 PHP ist einfach ____________________________________________________ 33 nicht weiter eingegangen, alle Skripte laufen unverändert auf allen Plattformen.
2.3.2
Wo wird programmiert?
PHP ist eine Skriptsprache, deren Elemente direkt in HTML-Seiten ein- Dateierweiterung gebaut werden. Durch Änderung der Dateierweiterung .PHP oder .PHP5 der HTML-Seite wird daraus eine PHP-Skriptdatei. Um PHP-Skripte ablaufen lassen zu können, muss ein entsprechend eingerichteter Server vorhanden sein. Schreiben können Sie PHP mit allen gängigen Editoren. Auch wenn Sie Editor an speziellen Features wie Syntaxhervorhebung oder automatische Vervollständigung interessiert sind, sieht das Angebot bereits recht gut aus. Empfehlenswert sind, wenn Sie ein paar Euro investieren möchten, phpED von Nusphere, Zend Studio und Maguma. Sie können PHP nicht nur über den Webserver ausführen. PHP ist jedoch PHP ausführen speziell für die Programmierung von Webseiten entworfen worden, andere Varianten werden deshalb hier nicht betrachtet. Beachten Sie, dass der Webserver nicht mit einem lokalen Pfad angesprochen werden kann. Sie müssen eine URL eingeben. Den lokalen Rechner erreichen Sie normalerweise als »localhost« oder über die IP-Nummer »127.0.0.1«. Dies sind reservierte Bezeichnungen, die anderswo keine Verwendung finden.
2.3.3
Wo wird PHP abgearbeitet?
PHP läuft auf dem Webserver, was am Anfang verwirrend ist, wenn der Webserver zugleich die Entwicklungsumgebung ist. Lesen Sie hier etwas über den Unterschied zwischen client- und serverseitiger Programmierung. PHP läuft nicht im Browser ab Schwierigkeiten bereitet am Anfang erfahrungsgemäß das Verständnis darüber, wo und wann der Code abläuft. PHP wird auf dem Server verarbeitet. Was immer der Benutzer im Browser anrichtet, Sie werden darauf erst reagieren können, wenn die Daten auf dem Server eintreffen. Dazu muss der Benutzer einen Hyperlink anklicken oder ein Formular absenden. Alle anderen Varianten nutzen clientseitige Techniken wie beispielsweise JavaScript, um diese Aktionen zu simulieren. Clientseitiges Scripting wird mit JavaScript oder – wenn Sie Flash-Fan Clientseitiges sind – mit ActionScript ausgeführt. Das funktioniert folgendermaßen: Scripting • Der Benutzer fordert eine Seite beim Webserver an • Der Webserver sendet diese Seite an den Browser • Der Browser erkennt im Code der Seite clientseitige Skripte und führt diese aus
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
34
____________________________________________ 2 Dynamik in der Webseite • Einige Befehle werden nicht sofort ausgeführt, sondern nur wenn bestimmte Ereignisse eintreten, beispielsweise das Anklicken einer Schaltfläche
Serverseitiges Scripting
Beim serverseitigen Scripting gibt es ein paar Unterschiede: • Der Server führt Skripte aus, bevor sie zum Browser gesendet werden • Clientseitiger Code kann im Browser eingesehen werden, serverseitiger dagegen nicht – ihre Skripte bleiben dem Benutzer garantiert verborgen • Clientseitiger Code muss vom Browser unterstützt werden, serverseitiger dagegen nur vom Server. Beim Browser kommt nur einfacher HTML-Code an. • Jede Seite wird neu zusammengestellt und an den Browser gesendet, auf dem Server bleibt keine Kopie zurück. Oft können Sie das eine oder andere Problem sowohl mit client- als auch serverseitigem Scripting lösen. Manchmal ist auch eine Kombination aus beidem sinnvoll oder ermöglicht raffinierte Lösungen für knifflige Probleme. Bedenken Sie, dass Sie bei jeder Aktion, die PHP ausführen soll, die Daten erst zum Server senden und dort bearbeiten müssen.
2.3.4
PHP und HTML
PHP wird im Gegensatz zu Perl nicht als eigenständiges CGI-Programm ausgeführt, sondern direkt im HTML-Code untergebracht. Dazu muss natürlich eine Möglichkeit geschaffen werden, wie der PHP-Prozessor die für ihn bestimmten Teile erkennt und interpretiert. Dateierweiterung So sehen die Dateien aus
Der erste Schritt besteht in der Benennung einer HTML-Seite mit einer anderen Dateierweiterung. Sie kennen bisher Erweiterungen wie *.HTM oder *.HTML. Jede HTML-Datei können Sie einfach mit der Erweiterung *.PHP5 (nur PHP5) oder *.PHP (empfohlen) versehen. Jetzt wird bei einem Aufruf dieser Datei der Webserver die Seite nicht sofort zum Browser senden, sondern zuerst an den PHP-Interpreter übergeben. Die Dateierweiterung PHP5 sollten Sie nur wählen, wenn auf dem betreffenden Serversystem gleichzeitig andere PHP-Versionen laufen. Dies kann in einer Umstellungsphase notwendig sein.
So arbeitet PHP
Der PHP-Interpreter arbeitet nun die Datei von oben nach unten ab. Reine HTML-Teile wird er unverändert wieder zurückgeben. Sie werden davon nichts bemerken, abgesehen von der höheren Belastung Ihres Servers und, bei langsamen Maschinen, einer verringerten Ausgabegeschwindigkeit. Finden sich in der Datei PHP-Anweisungen, werden diese ausgewertet und ausgeführt.
2.3 PHP ist einfach ____________________________________________________ 35 PHP einbinden Es gibt insgesamt vier Möglichkeiten, PHP in der Seite unterzubringen. Nur eine davon ist »zukunftskompatibel«. Dieser so genannte XML-Stil dient der Erkennung von PHP-Sequenzen und funktioniert auch, wenn mit XHTML gearbeitet wird:
XML-Stil
Wenn Sie zukunftsorientiert programmieren wollen, sollten Sie den . XHTML ist wichtig für besten Wahl künftige Projekte, weil es die Kompatibilität zu XML sichert und zu sauberem Code führt. Für die anderen drei Einbindungsmöglichkeiten, die PHP aus historischen Gründen kennt, gibt es keinen sinnvollen Einsatzzweck. Wie die Daten zum Browser gesendet werden Reines HTML – außerhalb der PHP-Tags – wird unverändert zum Brow- echo ser gesendet. Interessanter ist es jedoch oft, berechnete Daten zu übertragen. Dazu kennt PHP die Anweisung echo. Sie können hinter echo beliebig viele durch Kommata getrennte Argumente angeben, die zum Browser gesendet werden. Alternativ gibt es auch die Funktion print. Hier darf nur ein Argument print angegeben werden. Längere Ausgaben, die sich aus mehreren Teilen zusammensetzen, können mit dem Operator ».« (Punkt) verbunden werden. Es gibt sicher viele Situationen, wo es egal ist, ob echo oder print ver- Unterschiede wendet wird. Die variable Argumentliste von echo mag häufiger benötigt zwischen echo werden – auch wegen des einen Buchstaben kürzeren Namens ist echo und print vielleicht mehr im Einsatz. Es gibt aber Ausdrücke, in denen auch Ausgaben erfolgen und die Rückgabe eines Wertes erwartet wird. Werte können aber nur von Funktionen zurückgegeben – dann muss print verwendet werden. Einen Geschwindigkeitsunterschied gibt es übrigens nicht. Ausgaben sind Zeichenketten, sie werden deshalb explizit in Anführungszeichen gesetzt. Variablen – darauf wird in Kapitel 3 Variablen und Konstanten eingegangen – werden auch in Zeichenketten erkannt und durch ihren Wert ersetzt, wenn doppelte Anführungszeichen verwendet werden. Wollen Sie das nicht oder soll diese Funktion unterdrückt werden, können einfache Anführungszeichen verwendet werden. Beispiele finden Sie in nahezu jedem Skript in diesem Buch. Blättern Sie Wo sind die einfach mal durch und achten Sie auf die grau hinterlegten Listings und Beispiele? die Nutzung der Anweisung echo oder der Funktion print.
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
36
____________________________________________ 2 Dynamik in der Webseite Tipps zum Einbau von PHP in HTML
Übersichtliche Skripte schreiben
Am Anfang ist die Versuchung groß, unübersichtliche Skripte zu bauen. Denn Leerzeichen, Zeilenumbrüche und Tabulatoren spielen keine Rolle. Sie können eine HTML-Seite so schreiben:
... aber auch so:
... Je nach Umfang des in PHP geschriebenen Codes wird die eine oder andere Variante günstiger sein. Eine optisch eindeutige Trennung von HTML und PHP hat sich in der Praxis als sinnvoll erwiesen. Das Semikolon
Befehle werden in PHP generell mit einem Semikolon abgeschlossen. Nur wenn der Befehl allein zwischen steht, ist das optional. Sie sollten sich angewöhnen, das Semikolon immer zu setzen. HTML mit PHP erzeugen Als Vorgriff auf die Darstellung der Sprache sei die Standardmethode zur Ausgabe der Texte an den Browser gezeigt:
"; echo "
"; echo "
Eine Tabelle mit einer Zelle
"; echo "
"; echo ""; ?> Das sieht, auch wenn es wenig mit PHP zu tun hat, zumindest übersichtlich aus. Alternativ: Heredoc
Alternativ können Codeblöcke auch in der so genannten Heredoc-Syntax ausgedrückt werden. Dabei steht nach dem Ausgabebefehl oder einer Funktion oder Variablen, der Zeichen zugewiesen werden können, ein Einleitungssymbol. Am Ende folgt dann ein Endesymbol. Beides können Sie selbst festlegen:
Eine Tabelle mit einer Zelle
SYMBOL; ?>
2.3 PHP ist einfach ____________________________________________________ 37 Heredocs – der Begriff stammt aus Perl, wo die Technik erstmals zum Achtung! Einsatz kam – sind etwas tückisch. So muss der Ende-Text (im Beispiel Fehlerquelle! die Zeichenfolge »SYMBOL«) exakt mit dem Anfang übereinstimmen. Allein ein Leerzeichen davor oder dahinter macht den Befehl zunichte und PHP reagiert mit merkwürdigen Fehlern. Eingleitet wird das Startsymbol immer mit den drei spitzen Klammern: <<<. Fehlersuche Passiert Ihnen im HTML-Code ein Fehler, werden Sie sich vielleicht den Quelltext der erzeugten Seite anschauen wollen. Der sieht nun folgendermaßen aus:
Eine Tabelle mit einer Zelle
Das ist nicht ganz die optimale Form, auch wenn es perfekt funktioniert. Besser wäre sicher ein Code, der folgendermaßen aussieht:
Eine Tabelle mit einer Zelle
Ausgabe ohne Steuerzeichen
Ausgabe mit Steuerzeichen
Dazu schreiben Sie den Code im oben angegebenen Beispiel wie folgt auf:
\n"; echo "\t
\n"; echo "\t\t
\nEine Tabelle mit Zelle\n
\n"; echo "\t
\n"; echo "\n"; ?> Verarbeitung von Zeichenketten Die Sonderzeichen »\n« und »\t« entsprechen dem Zeilenumbruch und Bedeutung von dem Tabulator. Sie werden aber nur von PHP übersetzt, wenn Sie inner- \n und \t halb einer Zeichenkette auftreten, die mit doppelten Anführungszeichen »""« umschlossen ist. Dem Browser sind diese Zeichen egal – sie entsprechen denen mit einem Editor erzeugten und werden bei der Anzeige ignoriert. Zur Aufbereitung des Quellcodes sind sie aber ideal geeignet. Unix, Windows und Mac aus Sicht des Zeilenumbruchs Viele Editor-Programme bieten die Options, den Quelltext explizit als »Windows«- oder »Unix«-kompatibel zu sichern. Manche bieten noch eine Option für den Mac an. Das liegt daran, dass es für den Zeilenumbruch eine Geschichte gibt. Urspünglich waren Drucker das Ausgabemedium (in den 60er Jahren, bevor es Monitore gab). Da wurden zwei Steuerzeichen benötigt, um auf die nächste Zeile zu gelangen: Wagenrücklauf
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
38
____________________________________________ 2 Dynamik in der Webseite (\r, return) und Neue Zeile (\n, new line). Windows-System verlangen die Kombination \r\n auch für den Bildschirm, während sich Unix mit \n zufrieden gibt. Damit es nicht zu einfach wird, erwartet der Mac ein alleinstehendes \r. PHP5 erkennt alle Varianten und macht beim Lesen keinen Unterschied, also \r == \r\n == \n.
2.4
Erstellen und Testen
Das Schreiben von Skripten geht immer mit dem Testen einher. Sie werden immer wieder, auch als fortgeschrittener Programmierer, ein Stück Code schreiben, ausprobieren, wieder ändern, erneut testen usw. Im Gegensatz zu HTML toleriert PHP Fehler kaum und zwingt, um Fortschritte zu erzielen, zu einer fehlerfreien Umsetzung. Das gilt zumindest für die Syntax. Ob die Programmlogik, die Sie sich wünschen, auch umgesetzt wurde, können Sie natürlich nur selbst herausfinden. Bearbeiten von Skripten
Das Bearbeiten von Skripten läuft immer nach folgendem Schema: • Schreiben Sie den Code in einem Editor • Speichern Sie den Code in einem Verzeichnis des Webservers • Nun öffnen Sie den Browser und geben die Adresse des Skripts ein • Das Skript wird ausgeführt und PHP zeigt das Resultat oder Fehlermeldungen an • Jetzt wird das Skript geändert und erneut gespeichert • Im Browser drücken Sie auf Aktualisieren, um es erneut auszuführen Gewöhnen Sie sich an diesen Ablauf von bearbeiten, speichern und ausführen. Sie können das auf einem Computer machen und zwischen den Fenstern wechseln oder auch mit mehreren Computern arbeiten.
2.4.1 Das erste Skript
Sind Sie startklar?
Wenn Sie sich jetzt fit fühlen, das erste Skript auszuprobieren, dann ist dies eine gute Gelegenheit. Zuerst ein paar Annahmen: • Der Name des Webservers in diesem Buch lautet: »localhost«. Ersetzen Sie den Namen durch den Ihres Servers, wenn Sie nicht lokal arbeiten. »localhost« repräsentiert normalerweise das lokale System. • Das virtuelle Verzeichnis, in dem die Skripte liegen, hat den Namen »php4lernen«. Verwenden Sie diesen Namen, müssen Sie an den Pfadangaben in diesem Buch nichts ändern. Sie können aber auch jeden anderen Namen verwenden. Manchmal sind dann Änderungen an Skripten notwendig.
2.4 Erstellen und Testen ________________________________________________ 39 • Wenn als Webserver Apache verwendet wird, lautet der Pfad zum Stammverzeichnis »c:\htdocs« bzw. »/usr/local/htdocs«. Wenn Sie den IIS verwenden, lautet der entsprechende Pfad beispielsweise »c:\inetpub\wwwroot«. Der vollständige Pfad, wo die Skripte liegen lautet also:
c:\htdocs\php5lernen
Win / Apache
c:\inetpub\wwwroot\php5lernen
Win / IIS
/usr/local/htdocs/php5lernen
Linux / Apache
Vorausgesetzt, die Skripte liegen nun in dem beschriebenen Verzeichnis, rufen Sie diese im Browser folgendermaßen auf:
http://www/php-lernen/start.php Welchen Webserver Sie nun verwenden oder welches Betriebssystem, Jeder Webserver spielt jetzt keine Rolle mehr. ist geeignet Für die Skripte wird die Erweiterung PHP verwendet. In den Listingunterschriften steht jeweils der Skriptname in Klammern, beispielsweise (start). Auf der CD finden Sie dann »start.php«. Vor dem ersten Skript Bevor Sie die ersten Programme ausprobieren, muss Ihre Entwicklungsumgebung laufen. Erstellen Sie zum Test einfach eine Datei mit dem Namen test.php. Sie sollte exakt folgenden Inhalt haben:
phpinfo ist eine Funktion, die Informationen über die Installation von PHP5 ausgibt. Sie sollten dann das Skript im Browser aufrufen, beispielsweise über folgende Zeile: http://localhost/php5lernen/test.php Es wird eine sehr lange Seite ausgegeben, deren erster Teil etwa wie folgt aussieht:
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
40
____________________________________________ 2 Dynamik in der Webseite
Abbildung 2.1: PHP5 mit wichtigen Informationen
Das erste Skript Das erste Skript haben Sie sich vielleicht schon von der CD zum Buch geladen. Es ist hilfreich, dies beim ersten Mal nicht zu tun und statt dessen abzutippen. Sie lernen so schneller den üblichen Ablauf beim Programmieren. Im Gegensatz zum Test wird hier bereits ein kleines Programm zum Ablauf gebracht: Listing 2.1: Das erste Skript zum Ausprobieren (start)
PHP-Lernen
Willkommen PHP5! "; } ?>
Sie müssen jetzt noch nicht verstehen, was all die Zeichen bedeuten. Wichtig ist, dass Ihr Browser Ihnen die in der folgenden Abbildung gezeigte Seite ausgibt.
2.4 Erstellen und Testen ________________________________________________ 41 Abbildung 2.2: Ergebnis des Skripts aus Listing 2.1
Es funktioniert nicht! Es gibt nun zwei Möglichkeiten: Es funktioniert oder auch nicht. Wenn Wenn es nicht Sie die Ausgabe sehen, wie in Abbildung 2.2 gezeigt, dann steht Ihrer funktioniert erfolgreichen Karriere als PHP-Programmierer nichts mehr im Wege. Es gibt aber mehrere Probleme, die auftreten können: • Sie sehen den Quelltext des Skripts
Quelltext
In diesem Fall versteht der Webserver die Dateierweiterung PHP nicht. Lesen Sie das für Ihr System passende Tutorial auf einer der Webseiten, die in Abschnitt 1.3 Installation ab Seite 21. • Sie sehen nichts
Gar nichts
Vermutlich ist im Skript ein Fehler, der die Ausführung verhindert. passiert Lassen Sie den PHP-Code weg, erzeugt dieses Skript nur eine leere HTML-Seite. Achten Sie auf den Titel in der Kopfzeile des Browsers; dort muss »PHP-Lernen« stehen:
• Eine Fehlermeldung erscheint
Fehlermeldungen
In der Meldung finden Sie einen Hinweis auf die Zeile, in der PHP den Fehler entdeckt hat. Manchmal irrt PHP sich an dieser Stelle auch. Angegeben wird außerdem der physische Pfad zur Datei. Die angegebene Zeile ist dennoch ein guter Punkt, um mit der Suche zu beginnen.
• Sie erhalten eine Fehlermeldung, die auf Segmentfehler, Schutzver- Schutzverletzung letzungen u.ä. hinweist Sie haben ein Modul unter Windows verwendet, dass nicht stabil funktioniert. Dies sollte in der finalen Version nicht mehr auftreten. Versuchen Sie, die neueste PHP5-Version von der PHP-Website zu beschaffen.
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
42
____________________________________________ 2 Dynamik in der Webseite Es funktioniert!
So geht es weiter
Herzlichen Glückwunsch! Sie haben die größte Hürde beim Einstieg in PHP genommen. Ihr Webserver läuft, Ihr Skript läuft, der Browser funktioniert – Sie müssen jetzt nur noch ein paar PHP-Funktionen lernen und schon kann es losgehen. Es gibt übrigens mittlerweile weit über 2 000 Funktionen in PHP. Die kennen zwar auch Profis nicht alle, aber Sie wissen nun: Egal was für Herausforderungen auf Sie zukommen, Sie haben die beste und leistungsfähigste Skriptsprache für kleine und mittlere Webserverprojekte in der Hand (bei großen Systemen sieht das anders aus). Schauen Sie sich noch einmal das kleine Skript an, dass als Testobjekt diente. Der PHP-Teil ist fett hervorgehoben:
PHP-Lernen
Willkommen PHP! "; } ?>
Bis zum ersten
for ($i = 1; $i < 7; $i++) PHP verwendet hier eine Sprachanweisung, einen so genannten Befehl: for. Diese Anweisung verlangt einige Parameter, die zur Steuerung eingesetzt werden. Hier werden drei Parameter verwendet: • $i = 1 Dies ist eine Zuweisung. Variablen fangen immer mit $ an. • $i < 7 Dieser Ausdruck ist Wahr, solange der Inhalt von $i kleiner als 7 ist. Solange das der Fall ist, wird die Schleife ausgeführt. • $i++ Diese Anweisung erhöht den Inhalt von $i bei jedem Durchlauf der Schleife um 1. Das innere der Schleife ist eine Ausgabe:
echo "Willkommen PHP! ";
2.4 Erstellen und Testen ________________________________________________ 43 Weil die Zeichenkette in doppelten Anführungszeichen steht, untersucht PHP den Text nach Variablen. Die Laufvariable $i wird deshalb bei jedem Durchlauf durch ihren aktuellen Wert ersetzt. Die zweite geschweifte Klammer schließt den Block. Wie es genau funktioniert und welche Möglichkeiten es noch gibt, wird in den folgenden Kapiteln beschrieben.
V V V V V V 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 A A B B C C D D E E
2.4 Erstellen und Testen ________________________________________________ 45
3 Variablen und Konstanten Bestandteil jeder Programmiersprache sind Variablen. In Variablen werden Daten während der Laufzeit eines Skripts gespeichert. Die Daten können jederzeit verändert werden – durch Zuweisung oder Berechnung. Konstanten werden dagegen nur einmal zugewiesen und lassen sich dann nicht mehr ändern.
3
Variablen und Konstanten
3.1 Variablen erzeugen und erkennen ____________________________________ 47
3.1
Variablen erzeugen und erkennen
Variablen speichern Daten während der Laufzeit eines Programms. Technisch sind es Speicherstellen im Computerspeicher, die vom Programm aus über Namen erreicht werden. PHP kümmert sich selbst um den benötigten Speicherplatz und dessen Verwaltung. Variablen entstehen in PHP durch Zuweisung eines Wertes. Eine explizi- Wie Variablen te Deklaration, wie sie einige andere Programmiersprachen benötigen, entstehen gibt es nicht. Deklarationen teilen dem verarbeitenden System vor der ersten Verwendung mit, dass die Variable existiert. PHP erledigt die damit verbundenen Vorgänge im Augenblick der ersten Verwendung. Der Variablenname kann beliebig lang sein und besteht aus Buchstaben Der Name der und Zahlen und dem Unterstrich. Er darf jedoch nicht mit einer Zahl Variablen beginnen. Dafür muss jedem Namen das Zeichen $ vorangestellt werden. Hier einige korrekte Beispiele für Namen:
$vorname $langer_name $LangerName $feld12 PHP erlaubt theoretisch die Verwendung von Umlauten in Variablen- Umlaute namen. Nutzen Sie das bitte nicht. Die Lesbarkeit auf fremdsprachigen Systemen wäre fraglich und ebenso die Unterstützung in späteren Versionen. PHP unterscheidet beim Variablennamen zwischen Groß- und Kleinschreibung. $name und $Name sind zwei verschiedene Variablen. Achten Sie sorgfältig auf die Schreibweise – Fehler werden in der Regel nicht bemerkt, weil die vorherige Deklaration nicht notwendig ist. Variablen erhalten einen Wert durch Zuweisung. Dazu dient der Zuwei- Der Zuweisungssungsoperator =. operator
$Programmiersprache = "PHP"; $Feld12 = 23; Wenn nun an einer anderen Stelle im Skript die Variable $Feld12 verwendet wird, geht dort der Wert »23« in die Berechnung ein. Selbstverständlich kann der Inhalt nicht nur durch die Zuweisung eines feststehenden Wertes entstehen, sondern auch auf Basis einer Berechnung. Auf der rechten Seite der Zuweisung stehen deshalb oft komplexere Gebilde, die selbst konstante Werte oder Variablen enthalten. Solche Konstruktionen werden als »Ausdrücke« bezeichnet. Sie ergeben ein eindeutiges Resultat.
$Ergebnis = 12 + 24; $Quadrat = $wert * $wert; Die umfangreichen Möglichkeiten, die PHP für die Berechnung bietet, werden im Kapitel 4 Rechnen und Verarbeiten ab Seite 55 betrachtet.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
48 _____________________________________________3 Variablen und Konstanten
Kleine Namenskunde: So sollten Sie Variablen benennen Es ist empfehlenswert, »sprechende« Variablennamen zu verwenden. Dabei geht man davon aus, dass Variablen Zustände beschreiben – die Namen werden deshalb in der Regel Substantiven entsprechen. Zum Vergleich führen Funktionen Aktionen aus, sie werden deshalb mit Verben bezeichnet. Häufig sind auch Namen zu sehen, die zur Trennung mehrerer beschreibender Worte Großbuchstaben verwenden. Ebenfalls üblich sind Vorsätze, die auf die Art der Daten hinweisen, die in der Variablen gespeichert werden, beispielsweise »str« für Zeichenketten. Mehr dazu finden Sie im nächsten Abschnitt 3.2 Datentypen auf Seite 48. Damit keine Missverständnisse aufkommen: Dies sind nur Vorschläge und sie haben sich in der Praxis bewährt. Solche Konventionen sind kein Zwang. Sie werden Ihnen aber helfen, in größeren Projekten den Überblick zu bewahren.
3.2 Elementare Datentypen
Datentypen
Für die spätere Verarbeitung ist es wichtig, etwas über Datentypen zu wissen. Es gibt einige elementare Datentypen, die PHP intern erkennen und unterscheiden kann: • Ganzzahlen Dieser Datentyp verweist auf Zahlen ohne gebrochenen Anteil. Sie können positive oder negative Zahlen verwenden. Die größte Zahl ist 2 147 483 647, die kleinste -2 147 483 647. • Gleitkommazahlen Hiermit können Sie Zahlen mit gebrochenem Anteil darstellen. Ebenso ist die Angabe in Zehnerpotenzen möglich. Der Wertebereich ist größer als bei Ganzzahlen und reicht etwa bis ±1,8·10308. Der exakte Wert ist plattformabhängig. • Zeichenketten Zeichenketten bestehen aus einem oder mehreren Zeichen. Im Englischen werden sie als »string« bezeichnet. Eine Zeichenkette kann maximal 2 147 483 647 Zeichen lang sein (das entspricht 2 GByte). Sie können darin also auch ganze Dateien abspeichern (was aber nicht immer sinnvoll ist). • Logische Werte Logische Werte können nur zwei Zustände annehmen: Wahr und Falsch. In PHP werden diese auch durch die Konstanten TRUE und FALSE bezeichnet. Dabei spielt ausnahmsweise Groß- und Kleinschreibung keine Rolle. Denn intern stellt PHP dies durch Zahlen dar – 0 für FALSE und 1 für TRUE. Entsprechend vordefinierte Kon-
3.2 Datentypen ________________________________________________________ 49 stanten übernehmen die Zuordnung. Diese Konstanten existieren in mehreren Schreibweisen.
3.2.1
Datentypen darstellen und erzwingen
Wenn Sie programmieren, rechnen Sie natürlich mit einem bestimmten Datentyp. Die Zahlen »1« und »2« können – als Zeichenketten betrachtet – zu »12« verbunden werden. Als Ganzzahlen erkannt wird natürlich »3« daraus, wenn Sie die Verbindung als Addition ausgeführt wird. Der Datentyp wird auf mehreren Wegen beeinflusst: 1. Implizit durch die Art der Darstellung
Den richtigen Datentyp erkennen
2. Durch Verwendung bestimmter Operatoren 3. Durch Umwandlungsfunktionen Beim Umgang mit Datentypen kommt man nicht umhin, die interne Interne Bezeichnung zu kennen. Hier eine Auswahl einfacher Datentypen: Benennung • integer Dies bezeichnet Ganzzahlen (1, 5, 23, -6738 usw.) • double Hiermit werden Gleitkommazahlen bezeichnet (1.5, 14.99, -356.75) • string Damit werden Zeichenketten benannt (»Clemens«, »Haide«) • boolean Bezeichner für Boolesche Werte (TRUE / FALSE) Welche Bedeutung dies hat wie es verwendet wird, zeigen die nachfolgenden Abschnitte. Zahlen Zahlen werden unter ganz bestimmten Umständen als erkannt. Treten Zahlen diese Merkmale nicht auf, handelt es sich um eine Ganzzahl. Gleitkommazahlen werden durch eines oder mehrere der folgenden Merkmale erkannt: • Verwendung des Dezimalpunktes • Verwendung des Exponentialoperator »e« oder »E« • Verlassen des Wertebereiches der Ganzzahlen Beachten Sie, dass PHP intern die englische Schreibweise von Zahlen verwendet, das Komma also als Punkt geschrieben werden muss. Zum Umwandeln in die bei uns gebräuchliche Schreibweise kann die Funktion number_format eingesetzt werden (siehe dazu Seite 62). Das folgende Beispiel zeigt die Darstellung verschiedener Zahlen:
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
50 _____________________________________________3 Variablen und Konstanten Listing 3.1: Zahlenwerte anzeigen (var_zahlen)
10; 100; 1E3; 10000.0; echo echo echo echo
$z1; $z2; $z3; $z4;
?> ?> ?> ?>
$z1 und $z2 sind Ganzzahlen, $z3 und $z4 dagegen Gleitkommazahlen. Abbildung 3.1: Ausgabe von Listing 3.1
Wenn Sie also eine Ganzzahl wie $z4 im letzten Beispiel haben, intern aber mit Gleitkommawerten rechnen, setzen Sie einfach ».0« dahinter, um eine der oben gezeigten Bedingungen zu erfüllen. Datentyp erzwingen
PHP erlaubt die Angabe eines speziellen Operators, der den internen Datentyp verändert. Dazu wird einfach der Name des Datentyps in runde Klammern gesetzt und dem Wert oder der Variablen vorangestellt.
'; echo (integer) ($z1 / $z2); ?> Die Ausgabe zeigt, wie PHP intern den Datentyp ändert:
Abbildung 3.2: Ausgabe von Listing 3.2 Dieser Operator kann folgende Formen annehmen, wobei rechts jeweils eine alternative Schreibweise für die Zahlentypen steht: • (integer), (int) • (double), (float)
Alles hat einen Namen Diese Art von Operatoren werden auch »cast«-Operatoren genannt – vom englischen »cast«, was soviel wie Guss oder Abdruck bedeutet. Eine Funktion mit dem Namen »cast« gibt es dagegen in PHP5 nicht.
3.3 Variablen genauer betrachtet ________________________________________ 51 Zeichenketten Zeichenketten werden durch zwei Bedingungen erkannt: • PHP kann keine Zahl erkennen – dann wird immer eine Zeichenkette daraus
Mehrere Zeichen: Zeichenketten
• Die Zeichenfolge wird in Anführungszeichen gesetzt PHP erlaubt als Anführungszeichen für Zeichenketten einfache und doppelte. Doppelte haben eine besondere Bedeutung: sie lösen in der Zeichenkette enthaltene Variablen auf, ersetzen den Namen also durch den Wert. Der Umwandlungsoperator für Zeichenketten kann ebenso wie der für Zahlen angewendet werden und hat folgendes Aussehen: • (string) Logische Werte Logische Werte werden auch als Boolesch bezeichnet. Diese haben zwei mögliche Zustände: Wahr und Falsch. PHP stellt sie allerdings intern als Ganzzahlen dar und »simuliert« praktisch nach außen die Verwendung spezieller Boolescher Werte. Dabei entspricht 0 oder Nichts dem Wert Falsch, 1 oder jede andere Ganzzahl Wahr. Der Umwandlungsoperator für Boolesche Werte kann ebenso wie der für Zahlen angewendet werden und hat folgendes Aussehen:
• (boolean) Es gibt zwei Konstanten, die die Verwendung vereinfachen, weil sie eindeutig erkennbar sind: • TRUE
• FALSE Woher kommt der Ausdruck Boolesch? George Boole (1815 – 1864) wurde 1815 im englischen Lincolnshire geboren. In einigen Biografien wird er als Ire bezeichnet, was genau genommen nicht korrekt ist. Allerdings verbrachte er den größten Teil seines Lebens in Irland. Er war ein englischer Mathematiker und Logiker, der einige grundlegende Regeln der Logik formulierte.
3.3
Variablen genauer betrachtet
Variablen sind in PHP weitaus flexibler, als die ersten Ausführungen vermuten ließen. Dieser Abschnitt zeigt weitere Eigenschaften, die elegante Lösungen für Programmierprobleme erlauben. Für die ersten Schritte ist es jedoch nicht notwendig, diesen Abschnitt zu lesen.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
52 _____________________________________________3 Variablen und Konstanten
3.3.1 Zwei Namen = Ein Wert
Listing 3.3: Einfache Zuweisungen (noreferences)
Verweise
Normalerweise werden Werte einer Variablen mit dem Zuweisungsoperator übertragen. Dabei werden, wenn man sich den Vorgang im Speicher vorstellt, tatsächlich Bytes von einer Speicherstelle zu einer anderen kopiert. Betrachten Sie den folgenden Code:
Es existieren nun zwei unabhängige Variablen, $zahl und $ziel, beide mit dem Wert 14. Änderungen an einer der Variablen wirken sich woanders nicht aus. Das Skript gibt die Zahl 14 aus. Verweise auf Variablen: Referenzen
Referenzen
Listing 3.4: Referenzen erzeugen (var_reference)
Mit Hilfe von Referenzen kann ein Verweis auf eine Variable erstellt werden. Dabei wird der Wert nicht kopiert, sondern bleibt nur an der ursprünglichen Stelle bestehen. Ändert sich später diese Quelle, wirkt sich das auf alle Referenzen aus. Der folgende Code zeigt dies:
Dieses Skript gibt die Zahl 15 aus. Die Änderung an $zahl wirkt sich direkt auf die Referenz $ziel aus. Sie können mit solchen Referenzen Skripte lesbarer gestalten und die Werteübergabe vereinfachen.
&
Referenzen entstehen, indem der Quellvariablen das Zeichen & vorangestellt wird.
3.4
Konstanten
Konstanten sind gegenüber Variablen deutlich einfacher zu verwenden. Sie können in Berechnungen ebenso eingesetzt werden, dürfen sich aber während der Abarbeitung eines Skripts nicht ändern. Eingesetzt werden Konstanten häufig, um bestimmte Werte zu setzen, die zur globalen Einstellung dienen. Wenn Sie beispielsweise an mehreren Stellen die Farbe eines Textes angeben und sich die Änderung vorbehalten, wäre der feste Einbau der Zeichenkette »red« nicht ratsam. Sie müssten bei Änderungen dann alle Vorkommen von »red« suchen und ersetzen. Das funktioniert unter Umständen nicht automatisch, wenn auch Variablen mit dem Namen $red vorkommen oder gar Wörter wie
3.4 Konstanten ________________________________________________________ 53 »reden«. Definieren Sie dann eine Konstante mit dem Namen FARBE, der Sie den Wert »red« zuweisen. Prinzipiell unterliegen Konstanten den gleichen Benennungsregeln wie Namen der Variablen. Der Name kann beliebig lang sein und besteht aus Buchstaben Konstanten und Zahlen und dem Unterstrich. Er darf jedoch nicht mit einer Zahl beginnen. Konstanten erhalten kein vorangestelltes $-Zeichen. Normalerweise werden Variablen in Zeichenketten, die von doppelten Anführungszeichen umschlossen sind, analysiert und durch den Wert ersetzt. Mit Konstanten funktioniert das nicht, da diese nicht erkannt werden können.
3.4.1
Definition von Konstanten
Um Konstanten zu definieren, wird eine spezielle Anweisung eingesetzt:
define("FARBE", 44); Daraus entsteht eine Konstante mit dem Namen FARBE, die den Wert 44 define enthält. Die Großschreibweise hat sich bewährt – Skripte werden besser lesbar, wenn bestimmte Eigenschaften von Elementen sofort erkennbar werden. Ein Zwang, die Benennung durchzuführen, gibt es natürlich nicht. Auch Konstanten haben einen Datentyp. Allerdings können Sie diesen Datentyp einer mit den bei Variablen zulässigen Operatoren nicht mehr ändern. Achten Konstanten Sie deshalb bei der Zuweisung auf die Mechanismen, die PHP selbst zur Feststellung des Typs verwendet: • Zeichenketten entstehen folgendermaßen: - PHP kann keine Zahl erkennen – dann wird immer eine Zeichen-
kette daraus - Die Zeichenfolge wird in Anführungszeichen gesetzt
• Gleitkommazahlen werden so erkannt: - Verwendung des Dezimalpunktes - Verwendung des Exponentialoperators »e« oder »E« - Verlassen des Wertebereiches der Ganzzahlen
• Ganzzahlen sind an diese Bedingungen gebunden: - Wertebereich wird eingehalten - Keine der Bedingungen für Gleitkommazahlen - Nur die Zahlzeichen und das Minuszeichen werden verwendet
Manchmal kann es notwendig sein, zu überprüfen, ob eine Konstante defined bereits definiert wurde. Dazu dient die Funktion defined. Noch eine constant weitere Funktion kann im Zusammenhang mit Konstanten sinnvoll sein. Manchmal verlangt es die Syntax an einer bestimmten Stelle, dass statt eines Literals (Konstantenname) eine Funktion aufgerufen werden muss. Dann nutzen Sie die Funktion constant zum Abruf der Konstanten:
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
54 _____________________________________________3 Variablen und Konstanten Abbildung 3.3: Umgang mit Konstanten (constant)
3.4.2
Vordefinierte Konstanten
PHP_VERSION PHP_OS
PHP kennt bereits einige vordefinierte Konstanten. PHP_VERSION enthält die Versionsnummer des PHP-Interpreters. Sie können damit Skripte schreiben, die sich an bestimmte Bedingungen der Versionen anpassen. PHP_OS berücksichtigt das Betriebssystem, was vor allem bei Operationen im Dateisystem von Bedeutung sein kann.
TRUE FALSE NULL
In den meisten Programmiersprachen kann der Zustand »Wahr« und »Falsch« durch eine Konstante repräsentiert werden. Aufgrund des schwachen Typkonzepts von PHP wird ersatzweise angenommen, Werte ungleich 0 oder »0« sind »Wahr«. Um trotzdem lesbare Skripte erzeugen zu können, werden die Konstanten TRUE und FALSE verwendet, die intern als 1 und 0 dargestellt werden. Auch die kleine Schreibweise true und false ist zulässig. Seit PHP4 gibt es die Konstante NULL (null), die einen nicht vorhandenen Wert darstellt – vor allem zum Vergleich mit anderen Werten, die auf den Zustand »nichts« untersucht werden sollen.
__FILE__ __LINE__
Für die Fehlersuche sind zwei Konstanten wichtig, die immer die aktuelle Datei (Skript) und die Zeilennummer enthalten, die gerade abgearbeitet wird. Haben Sie einen Fehler abgefangen, können Sie in einer Fehlerausgabe auf diese Konstanten verweisen und so die Quelle des Fehlers feststellen: • __FILE__ enthält den Dateinamen des Skripts. • __LINE__ die Zeilennummer. Beachten Sie, dass es sich um zwei Unterstriche vor und nach dem Namen handelt.
E_ERROR E_WARNING E_PARSE E_NOTICE
Bei der Entwicklung von fehlertoleranten Anwendungen sind möglicherweise noch zusätzliche Konstanten von Bedeutung. Die folgenden Konstanten steuern die Ausgabe der Fehlermeldungen zur Laufzeit des Skripts: • E_ERROR, E_WARNING, E_PARSE, E_NOTICE Die Konstanten werden zusammen mit der Funktion error_reporting eingesetzt. Mehr dazu finden Sie in Abschnitt 14.3 Tipps zur Fehlersuche ab Seite 361. Sowohl eigene als auch vordefinierte Konstanten get_defined_constants aus. Versuchen Sie mal folgendes Skript:
4 Rechnen und Verarbeiten Programmieren hat viel mit Berechnungen und Datenverarbeitung zu tun. PHP5 liefert das hierfür nötige Spektrum an Funktionen und Operatoren, nicht mehr, aber auch nicht weniger.
Als Zeichenketten werden zwei oder mehr zusammenhängende Zeichen bezeichnet. Im englischen werden sie »string« genannt, weshalb viele Funktionen, die sich darauf beziehen, mit »str« beginnen. Zeichenketten haben in PHP kein spezielles Endezeichen und können bis zu 2 GByte groß sein – auch wenn dies eine eher theoretische Annahme ist.
4.1.1
Zeichenkettenfunktionen
Viele Vorgänge, die mit PHP programmiert werden, drehen sich um die So verarbeiten Verarbeitung von Zeichenketten. Praktisch sind sehr viele Daten, die in Sie Zeichenketten Skripten manipuliert werden, Zeichenketten. Berechnet wird dagegen vergleichsweise wenig. Arbeiten mit HTML heißt Umgang mit Zeichen. Entsprechend üppig ist PHP mit Funktionen ausgestattet. Die Kunst besteht weniger im anwenden, sondern im auffinden der richtigen Funktion. Tabelle 4.1 zeigt hier eine durch Funktionsmerkmale bestimmte Einteilung. Funktionsübersicht Die Vielzahl von Zeichenkettenfunktionen macht eine ganzheitliche Darstellung zu einer fast unlösbaren Aufgabe. Wichtiger als die ausführliche Ausbreitung aller Funktionsmerkmale – Auswahl der merken kann sich kaum jemand alle Funktionen – ist die Auffindbarkeit passenden in der Referenz. Die folgende Tabelle hilft Ihnen bei der Auswahl der Funktion richtigen Gruppen von Zeichenkettenfunktionen anhand des Aufgabengebietes. Aufgabe
Funktion
Umwandeln, Modifizieren
addcslashes addslashes bin2hex chop convert_cyr_string ltrim rtrim stripcslashes stripslashes
str_repeat strtok strtolower strtoupper trim ucfirst ucwords wordwrap strpos
Verschlüsseln
crypt crc32 md5
md5_file sha1 sha1_file
Zeichenorientierte Verarbeitung
ord chr
Tabelle 4.1: Zeichenkettenfunktionen nach Aufgabengebieten
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
58
____________________________________________ 4 Rechnen und Verarbeiten Aufgabe
In diesem Abschnitt werden einige sehr häufig verwendete und sehr mächtige Funktionen vorgestellt. Lesen Sie anschließend auch den folgenden Abschnitt 4.1.3 Zeichenketten ab Seite 63, um etwas über trickreiche Konstruktionen zu erfahren. Besonderheiten einiger Funktionen Verhalten beim Suchen
Wenn Sie mit Zeichenketten arbeiten, sollten Sie einige Eigenschaften kennen. Zeichenketten besitzen einen Index, der die Elemente der Kette –
4.1 Zeichenketten______________________________________________________ 59 die Zeichen – adressiert. Das erste Zeichen der Zeichenkette hat den Index 0. Das ist wichtig, wenn Sie in Zeichenketten suchen. Wenn die Fundstelle nämlich das erste Zeichen ist, dann wird 0 zurückgegeben. 0 entspricht aber auch dem Booleschen Wert FALSE – und das ist eigentlich der Wert, der zurückgegeben wird, wenn nichts gefunden wurde. Der Ausweg aus dem Dilemma besteht in der Nutzung des Identitätsoperators ===. Hier wird neben dem Wert (0) auch der Datentyp überprüft. Der ist bei einem Rückgabewert 0 integer, bei einem Fehler jedoch boolean. Universelle Zeichenkettenformatierungen Es gibt zwei Funktionen, die die gleiche Art der Formatierung verwen- printf den: printf und sprintf. Erstere gibt die formatierte Zeichenkette sofort sprintf an den Browser aus, sprintf dagegen gibt sie als Zeichenkette zur weiteren Verarbeitung zurück. Eine interessante Anwendung finden Sie in Listing 6.27 auf Seite 129, wo Beispiel die Einrückungen bei der Ausgabe eines Verzeichnisbaumes mit printf erzeugt werden. Beide Funktionen sind in der Anwendung identisch, die folgende Beschreibung bezieht sich auf beide Funktionen. Angegeben werden müssen mindestens zwei Parameter. Der erste gibt eine Formatieranweisung an, der zweite (und alle folgenden) gibt einen Wert an, der in die Formatieranweisung eingesetzt wird. Hier ein Beispiel für sprintf:
Listing 4.1: Datumsformatierung mit sprintf (string_sprintf)
Im Skript werden drei Variablen Werte zugewiesen:
$year = 2004; $month = 5; $day = 26; Dann werden diesen Werte den Formatanweisungen in sprintf übergeben. Dabei ersetzt die Funktion »%04d« mit dem Inhalt von $year usw. Die Minuszeichen werden unverändert ausgegeben, denn sie stehen außerhalb der Formate.
$result = sprintf("%04d-%02d-%02d", $year, $month, $day); Am Ende wird die formatierte Zeichenkette ausgegeben:
echo $result; Sie erzeugt dabei folgende Ausgabe: Abbildung 4.1: Ausgabe mit sprintf Die Formatieranweisungen von printf, sprintf usw. Eine komplette Formatieranweisung besteht nach dem einleitenden %- Was heißt »%0d2«? Zeichen aus bis zu fünf Elementen mit folgender Bedeutung:
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
60
____________________________________________ 4 Rechnen und Verarbeiten 1.
Ein Füllzeichen Der Standardwert ist das Leerzeichen. Wenn Sie mehrere Stellen wünschen und der Zahlen- oder Zeichenkettenwert diese nicht erreicht, wird der Rest mit dem Füllzeichen aufgefüllt.
2.
Ausrichtung Standardmäßig werden alle Werte rechts ausgerichtet, die Füllzeichen also als führende Zeichen behandelt. Mit einem Minuszeichen wird diese Funktion umgedreht.
3.
Zeichenzahl Eine optionale Anzahl Zeichen, die für den Wert in der Ausgabe reserviert wird. Fehlende Zeichen werden mit Füllzeichen aufgefüllt.
4.
Dezimale Für Gleitkommazahlen kann die Anzahl der Dezimalstellen bestimmt werden. Auf andere Werte hat dies keinen Einfluss.
5.
Typ Der Typ gibt an, wie der Wert generell behandelt wird. Folgende Typen sind möglich: - % Gibt ein Prozentzeichen aus. - b Ganze Zahl in binärer Form. - c Ganze Zahl als Zeichen aus dem ASCII-Zeichensatz. - d Ganze Zahl als Dezimalzahl. - f Gleitkommazahl in Exponentialform. - o Ganze Zahl in oktaler Form. - s Zeichenkette. - x Ganze Zahl in hexadezimaler Form. Die hexadezimalen Ziffern
werden als Kleinbuchstaben »a« bis »f« dargestellt. - X Ganze Zahl in hexadezimaler Form. Die hexadezimalen Ziffern
werden als Großbuchstaben »A« bis »F« dargestellt. Diese Art der Formatangabe setzt voraus, dass die Parameter immer in der richtigen Reihenfolge stehen. Das kann sich jedoch ändern, was zu praktischen Problemen führt. Betrachten Sie folgende Formatanweisung: »%s erscheint in Version %s« Wenn der erste Parameter »PHP« enthält und der zweiten die Zahl »5«, ergibt sich ein sinnvoller Text. Werden die Texte in einem komplexen Programm von jemand anderem erstellt, schreibt dieser später vielleicht folgendes: »Version %s der Sprache %s« Ändert man nun die Parameter nicht, erscheint ein sinnloser Satz: »Version PHP der Sprache 5«
4.1 Zeichenketten______________________________________________________ 61 Alle printf-Formatanweisungen können deshalb modifiziert werden:
%X\$F
Nummerierte Formatanweisungen
Dabei definiert X die Nummer des Parameters, von 1 beginnend. F steht für die ursprüngliche Formatanweisung (ohne %-Zeichen). Der Text im letzten Beispiel würde also besser folgendermaßen geschrieben werden: »Version %1\s der Sprache %2\s« Der Texter muss nun nur wissen, welche Bedeutung %1 und %2 haben. Ändert er den Text, kann er die Zuordnung frei wählen und der Quellcode mit dem Parametern muss nicht parallel dazu geändert werden. Sie können in der Formatierungszeichenkette beliebige Kombinationen Währungen aus solchen Zeichen darstellen, beispielsweise für die Ausgabe von Währungen.
Gelegentlich wird zur Formatierung von Währungen auf die Funktion money_format verwiesen. Dies ist jedoch gefährlich, weil diese unter Windows nicht definiert ist. Damit ist die Plattformunabhängigkeit nicht gewährleistet. Dies hat sich mit PHP5 nicht verändert, leider. Vergessen Sie money_format einfach, die Funktion ist nutzlos.
Alle Zeichen, die nicht zur Formatieranweisung gehören, werden unver- Führende Nullen ändert ausgegeben, wie im letzten Beispiel der Präfix &euro. Für die Ausgabe von führenden Nullen ist die Funktion ebenfalls geeignet:
Listing 4.3: Formatierung mit führenden Nullen Abbildung 4.3: Ganzzahlformate
Wenn Sie Prozent- oder Zahlwerte ausgeben müssen, eignet sich folgen- Prozent- oder de Schreibweise: Zahlwerte
Listing 4.4: Verschiedene Formen der printf-Funktion für Prozent- und Zahlenwerte (string_format)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
62
____________________________________________ 4 Rechnen und Verarbeiten Die Zuordnung der Variablen zu den Formatanweisungen erfolgt in der Reihenfolge des Auftretens. Stimmen die Datentypen nicht überein, wird er implizit entsprechend dem Format geändert. printf rundet Zahlenwerte außerdem mathematisch korrekt, wenn es durch die Angabe der Anzahl der Nachkommastellen dazu gezwungen wird. Betrachten Sie als Beispiel die Ausgabe des Prozentwertes aus Listing 4.4 in Abbildung 4.4.
Abbildung 4.4: Ausgabe des Skripts aus Listing 4.4 ReferenzWegweiser
Auf der hier anhand sprintf und printf gezeigten Vorgehensweise bauen weitere Funktionen auf: • vprintf • vsprintf Diese Funktionen arbeiten wie printf bzw. sprintf, akzeptieren aber als Argument keine Variablenliste, sondern ein Array, dessen Elemente als Argumente dienen (mehr zu Arrays finden Sie in Abschnitt 5.1 Wie Arrays funktionieren ab Seite 85).
sscanf
Mit einem vergleichbaren Satz von Formatierungsanweisungen arbeitet auch die Funktion sscanf. Hier wird eine unformatierte Zeichenkette auf die Existenz bestimmter Formen hin untersucht und die gefundenen Werte werden in einem Array abgelegt. Diese Funktion eignet sich, um Felder aus HTML-Formularen auszuwerten oder auch zur Umwandlung von fremden Formaten. Das folgende Beispiel zeigt die Aufbereitung eines amerikanischen Datumsformates und die Ausgabe in der in Deutschland üblichen Form mit sscanf und printf.
Listing 4.5: Daten mit sscanf scannen (string_sscanf)
Das von sscanf erzeugte Array %d hat so viele Elemente, wie gültige Formatierungen entdeckt werden konnten. Dies muss nicht der Anzahl der Formatanweisungen entsprechen. Mehr Informationen über Arrays finden Sie in Kapitel 6 Komplexe Datenstrukturen: Arrays ab Seite 83.
Abbildung 4.5: sscanf zur Ausgabe number_format
Geht es bei der Ausgabe jedoch nur um Zahlen, werden Sie vielleicht mehr mit number_format anfangen können. Diese Funktion eignet sich vor allem, um das intern verwendete englische Zahlenformat in das in Deutschland übliche umzuwandeln.
Auch diese Funktion ist in der Lage, korrekt zu runden, wenn es die Anzahl der Dezimalstellen erfordert. Übergeben werden neben der Zahl (erster Parameter) die Anzahl der Dezimalstellen (im Beispiel »2«), das als Komma verwendete Zeichen und optional ein Zeichen, das als Tausender-Trennzeichen dient. Abbildung 4.6: Formatierung von Zahlen Die Bedeutung der Parameter der Funktion number_format entnehmen Sie bitte der folgenden Tabelle: Parameter
Bedeutung
1
Wert, der formatiert werden soll
2
Anzahl der Dezimalstellen
3
Zeichen für die Dezimaltrennung (bei uns: Komma)
4
Zeichen für Gruppierung (bei uns: Punkt)
Die Angaben sind – bis auf den Wert – optional.
4.1.3
Zeichenketten in der Praxis
Zeichen treten vor allem bei der Ausgabe auf. Auch Zahlen sind, wenn Sie in HTML erscheinen, nur Zeichen, die ansprechend formatiert werden müssen. Solche Ausgaben sollen zuerst behandelt werden. Ausgaben formatieren Beim Formatieren denken viele Programmierer gleich an komplexe Zeichenkette Funktionen wie printf oder number_format. Oft geht es jedoch viel einfa- gruppieren cher. So können Telefonnummern leicht in Zweiergruppen mit zwischengesetzten Leerzeichen zerlegt werden:
Die Variable $tel enthält nach dem Ausführen des folgenden Codes die Zeichenkette »56 30 10 32«. Die Funktion chunk_split zerlegt die Zeichenkette in Gruppen, wobei die Anzahl der Zeichen pro Gruppe durch den zweiten Parameter bestimmt wird. Dann werden die Gruppen mit dem Trennzeichen (dritter Parameter) verbunden.
Listing 4.7: Zeichenkette in Zweiergruppen (chunk_split)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
64
____________________________________________ 4 Rechnen und Verarbeiten
Abbildung 4.7: Ausgabe des Skripts aus Listing 4.7 Einen Index erstellen Bei der Ausgabe von längeren Listen ist es oft gewünscht, einen Index in der Kopfzeile zu haben, mit dem bestimmte Punkte direkt erreicht werden können. Dabei wird eine Abfrage aus einer Datenbank sortiert und als Liste ausgegeben. Die einzelnen Buchstaben sind per seiteninternem Link erreichbar, wie hier für den Buchstaben »Z« gezeigt:
Z Im Kopf der Seite steht dann ein Link, der etwa folgendermaßen aussieht:
Z Natürlich sollen nur die Links aktiv sein, die auch zu Inhalten führen. Die folgende Abbildung zu Listing 4.8 zeigt das Ergebnis des Skripts: Abbildung 4.8: Index in HTML In der Lösung werden die Funktionen strtoupper, strncasecmp und substr verwendet, um das gewünschte Ergebnis zu erzielen. Listing 4.8: Erzeugen eines Index (siteindex)
4.1 Zeichenketten______________________________________________________ 65 Das Skript startet mit der Definition eines Musterarrays. Mehr zu Arrays finden Sie in Abschnitt 5.1 Wie Arrays funktionieren ab Seite 85. Die Daten können natürlich ebenso einer Datenbankabfrage entstammen. Nehmen Sie das Array einstweilen als Liste mehrerer Variablen an:
$wordlist = array("Anton", "Berlin", "Cäsar", "Karl", "Ludwig", "Thodor", "Xara"); Dann wird das Array sortiert. Die Sortierrichtung bestimmt die Anzeigereihenfolge:
sort($wordlist); Zuerst wird das erste Element des Index ausgewählt:
$oldword = "A"; Dann werden alle übergebenen Wörter durchlaufen:
foreach($wordlist as $word) { Mehr zur Anweisung foreach finden Sie in bestellen: foreach ab Seite 116.
Abschnitt 6.3.4 Felder
Vom aktuellen Wort wird der erste Buchstabe selektiert:
$word = strtoupper(substr($word, 0, 1)); Jetzt wird verglichen, ob der aktuelle Anfangsbuchstabe unmittelbar dem letzten Buchstaben folgt, also beispielsweise das »F« hinter »E« steht oder hinter »B«. Falls eine Lücke entstanden ist, wird diese mit der Funktion fillin ausgefüllt:
$next = strncasecmp($word, $oldword, 1); if ($next > 0) fillin($oldword, --$next); Das gefundene Wort wird unterstrichen dargestellt (die inzwischen von fillin geschriebenen Buchstaben sind nicht unterstrichen). Dies deutet den Einbau eines Links an, Sie müssten hier die passenden -Tags einsetzen:
Eine andere Funktion widmet sich dem häufig erforderlichen Zeilenumbruch: wordwrap. Es gibt im Web unzählige Lösungen, die einen Text in Zeilen fester Länge zerlegen und dabei Wortgrenzen beachten. Viel einfacher geht es im folgenden Beispiel. Dabei wird berücksichtigt, dass HTML-Umlaute aus mehreren Zeichen bestehen – entsprechend werden diese mit dem in Listing 4.13 gezeigten Verfahren behandelt. Damit neben den HTML-Codes nicht auch die -Tags verschwinden, wird der Umbruch zwischenzeitlich mit »\n«-Codes erhalten.
Die Umwandlung der Umlaute ist notwendig, weil die Funktion wordwrap sonst im Wort »Jörg« nicht vier, sondern neun Zeichen liest – entsprechend der HTML-Darstellung »Jörg«. nl2br wandelt »normale« Zeilenumbrüche in -Tags um und löst damit eines der häufigsten HTML-Ausgabeprobleme. Die überflüssigen normalen Zeilenumbrüche werden mit str_replace wieder entfernt. Sie können sie aber auch drin lassen, um die Anzeige des Quelltextes im Browser besser lesbar zu gestalten. Der Ausgleich verhindert, dass die Umwandlung der Zeichen die -Tags mit vernichtet.
4.2 Praktische Mathematik ______________________________________________ 71 Abbildung 4.12: Ausgabe des Skripts aus Listing 4.15
4.2
Praktische Mathematik
Über die mathematischen Funktionen in PHP gibt es nicht viel zu berichten. Praktisch ist alles verfügbar, was im Programmiereralltag benötigt wird. Noch häufiger werden Sie einige Operatoren zur Konstruktion von Ausdrücken verwenden. Dieser Abschnitt gibt zu beiden Themen einen groben Überblick.
4.2.1
Operatoren
Viele Operatoren haben Sie bei den vorangegangenen Beispielen bereits unbewusst verwendet. Die vielfältigen Schreibweisen sind jedoch eine nähere Betrachtung wert. Arithmetische Operatoren PHP kennt die elementaren arithmetischen Operatoren:
$x $x $x $x $x
+ * / %
$y; $y; $y; $y; $y;
// // // // //
+-*/%
Addition Subtraktion Multiplikation Division Modulus (Rest der Ganzzahldivision)
Bei der Division wird immer dann eine Fließkommadivision durchgeführt, wenn einer der beiden Werte vom Typ double ist. Für eine Ganzzahldivision müssen beide Operanden integer sein. Um Werte um eins erhöhen oder verringern zu können, verwenden Sie die Inkrement- und Dekrementoperatoren:
$zahl++ $zahl--
++ --
Im Zusammenhang mit Zuweisungen ist interessant, ob Sie die Erhöhung (Verringerung) vor oder nach der Zuweisung vornehmen. Entsprechend schreiben Sie den Operator vor oder hinter die Variable:
$x = $y++ $x = ++$y
// x wird y, dann wird y erhöht // y wird erhöht und dann x zugewiesen
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
72
____________________________________________ 4 Rechnen und Verarbeiten $x = $y-$x = --$y
Anwendungsmöglichkeiten
Listing 4.16: Modulus für wechselnd farbige Tabellenreihen (modulus)
// x wird y, dann wird y verringert // y wird verringert und dann zugewiesen
In der Praxis können Sie die Operatoren für interessante Effekte einsetzen. So eignet sich der Modulus-Operator, um bei Tabellen die Zeilen abwechselnd farblich unterschiedlich darzustellen.
'; ?> Die Berechnung basiert auf folgender Definition: Der Modulus ist 0, wenn Dividend und Divisor ohne Rest teilbar sind. Wenn Sie also jede zweite Reihe anders behandeln möchten, dividieren Sie den Zähler durch zwei, für jede dritte Reihe durch drei usw. Konkret ausgeführt wird diese Berechnung in der folgenden Zeile:
if ($i % 2 == 0) Die if-Anweisung, die hier benutzt wird, entscheidet ob ein nachfolgender Block (in geschweiften Klammern) ausgeführt werden soll. Abschnitt 6.2 Verzweigungen ab Seite 104. InMehr dazu finden Sie formationen zu Schleifen enthält Abschnitt 6.3.3 Abzählbare Schleifen: for(;;) ab Seite 114. Das Verfahren eignet sich auch hervorragend für die Begrenzung der Spaltenanzahl. In Listing 4.14 auf Seite 69 finden Sie die folgende Zeile:
if ($x++ % 8 == 7) Hier erfolgt die Trennung nach der achten Spalte. Der Vergleichswert ist 7, da wegen »0 % Wert == 0« die Bedingung bereits im ersten Durchlauf erfüllt ist. Dies würde zur Ausgabe einer neunten Spalte führen. Wenn Sie dies vermeiden möchten, beginnen Sie entweder mit 1 oder setzen den Endwert anders. Generell ist der Rest der Division die Anzahl der bereits absolvierten Schritte. Die folgenden Formeln helfen, das zu verstehen. Dabei ist x die Laufvariable, y der Trennwert, ab dem eine Aktion ausgelöst werden soll: • Berechnung des Schrittes innerhalb eines Zyklus, beginnt mit z = 0:
z = x % y
4.2 Praktische Mathematik ______________________________________________ 73 • Berechnung der Anzahl Schritte s bis zum Endes des Zyklus, beginnend mit s = y:
s = (y – (x % y)) Das Ergebnis des Skripts zeigt die folgende Abbildung: Abbildung 4.13: Ausgabe des Skripts aus Listing 4.16
Zuweisungsoperatoren Der einfachste Operator ist der Zuweisungsoperator, der beispielsweise Zuweisungen: für die Übertragung von Werten in eine Variable Verwendung findet. Sie = können die grundlegenden arithmetischen Operatoren mit diesem Operator verbinden:
$zahl = 45; // weist einer Variablen einen Wert zu $zahl = $andere_zahl; Das sieht sehr einfach aus. Sie können aber mit Hilfe von Klammern Zuweisungskomplexere Konstruktionen schaffen: operatoren: += -= /= Anschließend enthält die Variable $faktor den Wert 2, $zahl den Wert 8 %= .=
und alle Variablen $zX enthalten alle den Wert 0. Die arithmetischen Operatoren können damit kombiniert werden:
$zahl $zahl $zahl $zahl $zahl
+= -= *= /= %=
$zahl2; $zahl2; $zahl2; $zahl2; $zahl2;
Auch der Zeichenkettenoperator ».« kann mit der Zuweisung kombiniert werden:
$zeichen .= " ";
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
74
____________________________________________ 4 Rechnen und Verarbeiten Bitoperatoren Wenn Variablen Werte enthalten, die sich in Byte- oder Bitform auffassen lassen, können Manipulationen mit Bitoperatoren sinnvoll sein. Denken Sie daran, dass ein spezieller binärer Datentyp nicht existiert. Die Anwendung auf normale Variablen führt manchmal zu unerklärlichen Effekten.
& | ~
Tabelle 4.2: Verhalten der Bitoperatoren
Der Operator & führt eine binäre UND-Verknüpfung durch, | steht für eine ODER-Verknüpfung, während ~ den Bitwert negiert. Die Operatoren entsprechen Boolescher Algebra. Das Verhalten kann der Auflistung in Tabelle 4.2 entnommen werden. Operand $x
Operand $y
$x & $y
$x | $y
~$x
~$y
0
0
0
0
1
1
0
1
0
1
1
0
1
0
0
1
0
1
1
1
1
1
0
0
Bedenken Sie, dass das Ergebnis alle Bitstellen beinhaltet, also nicht einer dezimalen 0 oder 1 entspricht. Tabelle 4.2 gibt jedoch zur Vereinfachung nur eine Bitstelle wieder. Bits verschieben
Zwei weitere Operatoren arbeiten auf Bit-Ebene: >> und <<. Beide sind mit dem Zuweisungsoperator = verknüpfbar. Mit >> werden Bits nach rechts verschoben, bei << nach links. Bedenken Sie, dass Sie Bitwerte verarbeiten. Es ist also nicht möglich, Zeichenketten der Art "0100" zu verarbeiten:
$bit = "0100"; $result = $bit >> 1; Dies ergibt 50, binär 110010. Was ist passiert? Eigentlich hätte die Rechtsverschiebung zu "0010" führen müssen. Tatsächlich hat PHP die Verschiebung korrekt ausgeführt – in beiden Fällen entspricht dies einer Division durch 2. Allerdings ist die Zeichenkette intern in eine Dezimalzahl gewandelt worden – in die 100. Versuchen Sie es besser folgendermaßen: Listing 4.17: Bitoperatoren anwenden (bitops)
> 1; printf("%04b", $result); ?> Jetzt wird erwartungsgemäß "0010" zurückgegeben. Einmal bitweise nach rechts verschieben entspricht einer Division durch 2, entsprechend ist eine Linksverschiebung eine Multiplikation mit 2.
bindec decbin
Elegant ist natürlich auch die Verwendung entsprechender Umwandlungsfunktionen. Speziell für Binärzahlen gibt es bindec (Binärzahl in Dezimalzahl) und decbin (Dezimalzahl in Binärzahl). Die Darstellung der Binärform ist jedoch in PHP eine Zeichenkette – keine echte Binärzahl.
4.2 Praktische Mathematik ______________________________________________ 75 Logische Operatoren Logische Operatoren dienen der Konstruktion logischer Ausdrücke und werden vor allem zusammen mit if, while oder do verwendet. Wichtig ist bei diesen Operatoren die Assoziativität – also die Rangfolge. PHP nutzt diese Vorgaben, wenn die Reihenfolge der Abarbeitung nicht durch Klammern exakt vorgegeben ist. Sie kennen sicher die Regel »Punkt- vor Strichrechnung« aus der Schule. Die Assoziativität dehnt das Regelwerk auf alle in der Programmiersprache definierten Operatoren aus. Die folgende Tabelle zeigt die nötige Übersicht: Assoziativität
Bei Operatoren wie or oder and kann diese Vorrangsteuerung ausgenutzt werden, um bestimmte Effekte zu erzielen. Im folgenden Beispiel werden die Funktionen f1() bis f4() nur solange ausgeführt, bis eine der Funktionen FALSE zurückgibt.
if (f1() and f2() and f3() and f4()) Der logische Ausdruck verknüpft die vier Rückgabewerte der Funktionen f1() bis f4() mit and. Damit der Ausdruck Wahr wird, müssen alle vier Werte Wahr sein. Ist auch nur einer der Werte Falsch, kann das Ergebnis nur noch FALSE sein. Hier arbeitet PHP sehr effizient – der Rest des Ausdrucks wird einfach nicht mehr bearbeitet, die Funktionen f3() und f4() werden also nicht aufgerufen, wenn f2() FALSE zurückgibt. Ob das im Sinne des Programmierers ist, hängt vom konkreten Fall ab. Auch mit dem or-Operator kann man sinnvoll diese Effekte ausnutzen. Wenn Sie damit rechnen, dass eine Funktion bei Fehlern FALSE zurückgibt, schreiben Sie Folgendes:
myfunction() or print "Fehler in 'myfunction()'"; Der Ausdruck ist durch ein logisches ODER verknüpft. Zuerst wird die linke Funktion ausgeführt. Gibt diese TRUE zurück, ist der gesamte Ausdruck – unabhängig von der rechte Hälfte, auch TRUE. Durch die Optimierung wird die weitere Verarbeitung abgebrochen und das Skript läuft
Tabelle 4.3: Assoziativität der Operatoren
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
76
____________________________________________ 4 Rechnen und Verarbeiten weiter. Gibt die Funktion myfunction() jedoch FALSE zurück, muss auch der rechte Teil ausgeführt werden. Der bringt zwar keine neuen Erkenntnisse, aber die Fehlerausgabe erscheint. Sehr oft ist hier auch die Anweisung die zu sehen, die das Skript an dieser Stelle abbricht.
Praktisch verwendbar
Diese Art der Programmierung ist weit verbreitet, kompakt und verhältnismäßig gut lesbar. Auch wenn es den Anschein eines Tricks hat – diese Technik ist sinnvoll und praktikabel. Welcher Teil zuerst ausgeführt wird, können Sie Tabelle 4.3 entnehmen. Abschnitt 6.4 Ordnung ins Chaos: Funktionen ab Seite 118 enthält Informationen über den Umgang mit eigenen Funktionsdefinitionen.
4.2.2
Mathematische Funktionen
Die folgende Tabelle zeigt alle Funktionen auf einen Blick. Die trigonometrischen Funktionen erwarten Argument im Bogenmaß (Radiant), mit deg2rad und rad2deg können solche Werte von und in Winkel umgerechnet werden. Tabelle 4.4: Mathematische Funktionen
Die Funktionen min und max mögen auf den ersten Blick trivial erscheinen, werden jedoch seltener eingesetzt als eigentlich möglich. Betrachten Sie den folgenden Ausdruck:
if ($a > 3) { $b = $a; } else { $b = 3; } Solche Gebilde sind sehr häufig anzutreffen. Besser könnte man hier folgendes schreiben:
$b = max($a, 3); Oft dürfen Werte nicht kleiner als 0 werden. Relativ elegant ist dagegen folgendes Konstruktion:
$a < 0 ? $a = 0 : $a;
4.2 Praktische Mathematik ______________________________________________ 77 Noch kürzer kann man dafür max einsetzen:
$a = max(0, $a); Umgekehrt gehen Sie vor, wenn ein Wert ein bestimmtes Limit nicht überschreiten soll. Praktisch ist dies bei der Analyse von Benutzereingaben. Das folgende Beispiel zwingt den Wert zwischen zwei festgelegten Grenzen, wobei kleinere Zahlen auf den kleinsten und größere auf den größten zulässigen Wert korrigiert werden:
$month = max(1, min($month, 12)); Nach der Ausführung ist $month auf jeden Fall zwischen 1 und 12. Durch Messung der Abarbeitungszeiten kann man feststellen, dass der AbarbeitungsFunktionsaufruf nach max oder min geringfügig schneller ist, als die ver- zeiten gleichbaren mehrzeiligen Konstruktionen aus if und else. Mehr Informationen zu diesen Anweisungen enthält Abschnitt 6.2 Verzweigungen ab Seite 104. Funktion
Beschreibung
abs(zahl)
Absoluter Betrag
floor(zahl)
Ganzzahliger Teil einer Zahl
ceil(zahl)
Nächsthöhere Ganzzahl
round(zahl [,dezimalstelle])
Rundung auf die durch dezimalstelle angegebene Stellenzahl
base_convert (nummer, quellbasis, zielbasis)
Wandelt von einem beliebigen Zahlensystem der Basis 2 bis 36 in ein anderes um
Wenn Sie Logarithmen mit einer beliebigen Basis berechnen müssen, verwenden Sie folgende Definition. Das erste Argument ist die zu berechnende Mantisse, das zweite die Basis des Logarithmus:
$mant = 3.14; $raise = 2; echo log($mant)/log($raise); Eine Besonderheit ist bei round zu beachten. Die Rundung erfolgt nicht in Probleme mit allen Fällen korrekt. Das liegt an der internen Darstellung der Zahlen. dem Runden: Einige einfache Werte, wie beispielsweise 11,5, sind nicht exakt im Binär- round format darstellbar. Statt dessen speichert der Computer 11,49999999999.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
78
____________________________________________ 4 Rechnen und Verarbeiten Die Rundungsfunktion rundet dann auf 11 ab. Eine Alternative ist die Anwendung der Funktionen ceil und floor, wie das folgende Beispiel zeigt:
= 0.5) { echo (ceil($v) / $r); } else { echo (floor($v) / $r); } ?> Die Funktion ermittelt zuerst die Anzahl der Dezimalstellen – dies entspricht der Zehnerpotenz:
$r = pow(10, $d); Dann wird der Wert damit multipliziert – jetzt gibt es keine Nachkommstellen mehr.
$v *= $r; Jetzt wird der so ermittelte Wert mit seinem ganzzahligen Teil verglichen. Tritt ein Fehler auf, ist die Differenz größer als 0,5, wird der nächst höhere Ganzzahlwert mit ceil ermittelt, andernfalls bleibt die bestehende Ganzzahl erhalten. Mit der abschließenden Division durch die Anzahl der Dezimalstellen entstehen wieder die Nachkommstellen. Tipp!
Eine andere Anwendung von ceil ist die Ermittlung einer Anzahl Seiten bei der seitenweisen Ausgabe von Ergebnissen. Wenn Sie beispielsweise 22 Ergebnisse in Gruppen zu sieben ausgeben, benötigen Sie dafür vier Seiten. 22/7 liefert 3.142. Eine Rundung würde 3 zurückgeben, was falsch ist; ceil liefert dagegen die korrekte Zahl 4. Mathematische Konstanten In PHP5 gibt es eine ganze Palette häufiger benötigter Konstanten, die in der folgenden Tabelle zusammengefasst sind.
Tabelle 4.6: Mathematische Konstanten und deren Definition
Die in der Dokumentation ausgewiesenen Konstanten M_EULER und M_LNPI, die mit PHP4.0.2 eingeführt wurden, waren bei der ersten Version PHP5.0.0 nicht verfügbar. In künftigen Versionen kann dies wieder geändert werden. Zufallszahlen Zufallszahlen werden häufig benötigt, um Vorgänge zu steuern oder beispielsweise Kennwörter zu erzeugen. Zufallsfolgen beruhen auf mathematischen Funktionen, die zwar einen chaotischen Verlauf haben, aber dennoch einer strengen Folge gehorchen, sie sind pseudozufällig. Echte Zufälligkeit wird erst erzeugt, wenn der Startwert variiert. Die folgende Tabelle zeigt Funktionen zum Abruf der Zufallswerte und zum Setzen des Startwertes. Funktion
Beschreibung
mt_srand()
Setzt den Startwert für den Zufallsgenerator.
mt_rand([min] [, max])
Gibt eine Zufallszahl zwischen 0 und 1 oder zwischen min und max zurück1.
mt_getrandmax()
Gibt die höchstmöglich Zahl an, die rand zurückgeben kann.
Tabelle 4.7: Funktionen, um Zufallszahlen zu erzeugen
Es existieren noch ältere Zufallszahlenfunktionen ohne den Präfix mt_. mt-Funktionen Die Funktionen mit dem Präfix mt_ sind neuer und sollten grundsätzlich bevorzugt werden. Die in den ursprünglichen PHP-Bibliotheken verwendeten Zufallsgeneratoren sind relativ unbekannt und langsam. Vor allem beim Einsatz in der Kryptografie ist die Charakteristik eines Zufallsgenerators wichtig. Die mt-Funktionen wurden von Mersenne Twister (mt) entwickelt und sind gut dokumentiert. Urspünglich muss bei allen Zufallsgeneratoren der Startwert explizit gesetzt werden, um nicht immer wieder dieselbe Folge von pseudozufäl-
1 Die eckigen Klammern gehören nicht zur Funktion, sondern deuten an, dass es sich hier um optionale Parameter handelt.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
80
____________________________________________ 4 Rechnen und Verarbeiten ligen Zahlen zu produzieren. PHP5 kann dies allein, es nimmt den aktuellen Zeitstempel inklusive des Mikrosekundenanteils als Startwert. Nur wenn das nicht reicht ist der Einsatz von mt_srand erforderlich.
4.2.3
Funktionen beliebiger Genauigkeit
Die internen mathematischen Funktionen von PHP – und übrigens auch aller anderen Programmiersprachen – arbeiten mit binären Gleitkommazahlen. Es liegt in der Natur dieser Zahlen, dass sie bestimmte reelle Zahlen nicht abbilden können. Letztlich sind Sie auch bei der dezimalen Mathematik auf eine bestimmte Anzahl Dezimalstellen beschränkt. Die alternative Schreibweise, die eine beliebige Genauigkeit erlaubt, sind Brüche. In der Computerwelt stehen für die Ablage und Verarbeitung von Zahlen im Prozessor auch nur eine bestimmte Anzahl von Bits zur Verfügung. Zahlen, die nicht in diesen Zahlenraum passen, werden mit einem bestimmten Fehler weiterverarbeitet. In der Praxis macht sich das nur selten bemerkbar, weil die Genauigkeit relativ hoch ist. Reicht es jedoch nicht, müssen andere Maßnahmen her. Ein wichtiges Beispiel sind Währungen. Während die Anzahl der Nachkommastellen gering ist, sind die Ansprüche an korrekte Berechnungen oder Rundungen extrem hoch. PHP unterstützt Berechnungen dieser Art mit einem Satz von Funktionen, die mit einer beliebigen Genauigkeit rechnen können. Wann es sich bemerkbar macht Rundungsprobleme
Um die Problematik noch etwas transparenter werden zu lassen, soll ein Beispiel gezeigt werden. Ab einer bestimmten Anzahl Dezimalstellen wird die verfügbare Genauigkeit überschritten:
$z = 4.4999999999999999; echo round($z, 0); Dieses Skript gibt »5« aus, was offensichtlich falsch ist. Berechnungen mit beliebiger Genauigkeit Das verwendete Modul trägt den Namen »bc« und liefert einige elementare Funktionen. Am Beginn steht die Festlegung der Anzahl Dezimalstellen, mit denen gerechnet werden soll. Die Anwendung zeigt das folgende Skript: Listing 4.19: Funktionen mit beliebiger Genauigkeit (math_bctest)
"; echo ""; echo "$x "; ?>
4.2 Praktische Mathematik ______________________________________________ 81 Das Skript führt eine Division mit einer Genauigkeit von 20 Stellen aus. Durch die provozierte Wahl der Operanden kann man den Effekt gut beobachten: Abbildung 4.14: Ausgabe des Skripts aus Listing 4.19 Sicher sind solche Fälle eher extreme Zustände. Wenn Sie jedoch den Verdacht haben, dass die interne Berechnung nicht ausreichend genau ist, beschäftigen Sie sich mit den Funktionen beliebiger Genauigkeit.
5 Komplexe Datenstrukturen: Arrays Zwischen Daten, wie sie in Variablen gespeichert werden, bestehen oft komplexe Beziehungen. Manchmal wäre es einfach nur praktisch, zusammengehörende Informationen auch gemeinsam ablegen zu können. Zu diesem Zweck sind Datenfelder entworfen worden – so genannte Arrays.
5
Komplexe Datenstrukturen: Arrays
5.1 Wie Arrays funktionieren ____________________________________________ 85
5.1
Wie Arrays funktionieren
Der Umgang mit Arrays erscheint, betrachtet man sich einfache Beispiele, unproblematisch. Trotzdem bergen sie einige Tücken, nicht zuletzt wegen der flexiblen Unterstützung in PHP5, die zu unsauberer Programmierung verleitet. Arrays sind Sammlungen von Daten unter einem gemeinsamen Namen. Ein leeres Array kann man in PHP folgendermaßen erzeugen:
$meinarray = array(); array ist eine Anweisung, die das erledigt. Arrays entstehen aber auch implizit, wenn man nicht existenten Elementen Werte zuweist. Die folgende Grafik zeigt, wie ein Array im einfachsten Fall aufgebaut ist. Jedes Element besteht aus einem Index (linke Spalte) und einem Datenwert (rechte Spalte). Abbildung 5.1: Aufbau eines einfaches Arrays
5.1.1
Eindimensionale Arrays
Im einfachsten Fall werden eindimensionale Arrays verwendet. Diese bestehen aus einer beliebigen Anzahl Elementen, die über einen numerischen Index angesprochen werden können. Der Index wird in eckigen Klammern angegeben, der Zugriff auf Elemente erfolgt ebenso:
Diese Skript erzeugt ein Array $plz mit drei Elementen. Das zweite Element wird anschließend ausgegeben: 12683. Der Datentyp der einzelnen Elemente muss dabei nicht identisch sein. Sie müssen sich über die Verwendung auch vorher keine Gedanken machen – jedenfalls ist das aus Sicht von PHP nicht notwendig. Betrachtet man dagegen Aspekte sauberer Programmierung, ist eine allzu verworrene innere Struktur eines Arrays nicht zu empfehlen. Prinzipiell kann ein Array aber alles enthalten, was auch eine normale Variable enthalten darf – einschließlich weiterer Arrays.
Listing 5.1: Ein einfaches Array anlegen (array_onedim)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
86 _____________________________________ 5 Komplexe Datenstrukturen: Arrays Variablenauflösung mit Arrays Komplizierte Konstruktionen mit Array-Indizes kann PHP5 nicht immer korrekt innerhalb der automatischen Variablenerkennung auflösen. Gewöhnen Sie sich deshalb am besten gleich die Schreibweise mit Variablenkennzeichnung an, bei der der Ausdruck in geschweifte Klammern gesetzt wird: "Meine Postleitzahl lautet: {$plz[1]}". Solange Sie numerische Indizes verwenden, gibt es keine Besonderheiten. Folgendes funktioniert also problemlos:
echo “$arr[3]“; Mit assoziativen Arrays sieht es anders aus. Falsch ist diese Schreibweise:
echo “$arr[’test’]”; PHP erkennt den Indexwert nicht. Um das Problem zu lösen, gibt es eine besondere Syntax, die eine weitere Klammerform verwendet, um dem Parser den zur Variablen gehörenden Umfang mitzuteilen:
echo “{$arr[’test’]}”; Die geschweiften Klammern selbst werden nicht ausgeben. Sie sind nur zur Unterstützung des Parsers gedacht.
Der Wertebereich für Indizes Wenn kein Index angegeben wird, nimmt PHP den nächsten freien Wert. Der Wertebereich, der dafür zur Verfügung steht, reicht von 0 bis 232-1, das sind alle positiven Ganzzahlen. Nullbasierte Arrays
Arrays sind standardmäßig nullbasiert, das erste Element hat also den Index 0.
Andere Werte für die Index
Andere Werte werden als Schlüssel eines assoziativen Arrays interpretiert, was im nächsten Absatz erläutert wird. Das folgende Beispiel ist dem in Listing 5.1 gezeigten völlig gleichgestellt:
Listing 5.2: Array ohne Indexangabe erzeugen (array_onedim2)
Wenn Sie zwischendurch einen Indexwert selbst festlegen, wird dieser als Ausgangspunkt für die automatische Vergabe benutzt:
5.1 Wie Arrays funktionieren ____________________________________________ 87 Das Array $plz besteht nun aus drei Elementen mit den Indizes 0, 4 und 5. Wenn ein Index gezielt gesetzt wird, zählt PHP intern ab diesem Wert weiter. Mit jeder Zuweisung wird der Index um 1 erhöht. Die Ausgabe des Skripts aus Listing 5.3 lautet: 12459. Es ist generell eine gute Idee, die Indizes selbst zu vergeben und damit festzusetzen, wenn darauf später direkt Bezug genommen wird. Die folgenden Grafik zeigt den inneren Aufbau eines Arrays ohne fortlaufenden Index. In der linken Spalte stehen die Indizes. Zu sehen ist, dass PHP weder die fehlenden Werte auffüllt noch die Reihenfolge verändert. Abbildung 5.2: Arrays ohne fortlaufenden Index
Arrays wieder ausgeben Der Zugriff auf Elemente erfolgt mit Hilfe der Notation [index] hinter dem Variablennamen. Für die Ausgabe ganzer Arrays dagegen bieten sich verschiedene Schleifenanweisungen an. Das Ausgaben von Arrays ist indes nicht so schwer. Es gibt in PHP eine Ganze Arrays sehr gute Unterstützung. Am häufigsten wird die Schleifenanweisung ausgeben foreach verwendet. Diese durchläuft immer alle Elemente eines Arrays und gibt die Element in einer Variablen zurück:
Grundsätzliche Aussagen zu foreach finden Sie in Abschnitt 6.3.4 Felder bestellen: foreach ab Seite 116. Hier wird die Sprachanweisung schon ohne weitere Erläuterung benutzt, um die Ausgabe der Arrays einfacher gestalten zu können. Die Schleife durchläuft alle Elemente und gibt diese untereinander aus. Die Anzahl spielt keine Rolle. Nur wenn die Variable $plz kein Array ist, wird ein Fehler ausgegeben. Falls der Index nicht der natürlichen Zahlenfolge entspricht, wären natürlich auch diese Angaben interessant. Erweitern Sie dann foreach folgendermaßen:
Listing 5.4: Ausgabe eines kompletten Arrays (array_foreach)
Der Operator => ist Bestandteil der foreach-Anweisung. Er dient der Trennung von Schlüssel und Werten eines assoziativen oder der Indizes eines numerischen Arrays. Abbildung 5.3: Ausgabe des Skripts aus Listing 5.5 Mehrere Werte zuweisen Umgang mit array Am Anfang wurde bereits die Erzeugung eines leeren Feldes mit array
angesprochen. Das ist sicher nur selten wirklich notwendig. Häufiger wird array eingesetzt, um gleich mehrere Werte zuzuweisen: Listing 5.6: Werte mit array zuweisen (array_array)
Wenn Sie nun, wie in Listing 5.7 gezeigt, andere Indizes wünschen, werden diese mit dem =>-Operator zugewiesen. Das sieht dann folgendermaßen aus:
Listing 5.7: Eigene Indizes mit array angeben (array_arrayi)
10999, 3 => 12683, 9 => 12459); echo $plz[3]; ?> Beide Skripte geben »12683« aus.
5.1.2
Sprechende Indizes: Assoziative Arrays
Assoziative Arrays verwenden keinen numerischen Index, sondern Schlüssel. Dies sind Zeichenketten, die ein Element benennen. Manchmal wird ein solches Array auch als Hash bezeichnet. Abbildung 5.4: Aufbau eines assoziativen Arrays
5.1 Wie Arrays funktionieren ____________________________________________ 89
Jedem das Seine: Hashes und Arrays In PHP wird der Begriff »Hash« nicht explizit definiert, in anderen Programmiersprachen ist dies jedoch durchaus üblich. In PHP spricht man von assoziativen Arrays. In VBScript oder Visual Basic dagegen werden solche Felder als »Dictionaries« bezeichnet. Wie auch die Benennung erfolgt, das Prinzip ist dasselbe. Leider ist die Begriffsbestimmung offensichtlich nicht einfach, denn assoziative Arrays werden häufig mit mehrdimensionalen verwechselt – leider auch in der Fachliteratur zu PHP. Um es ganz klar herauszustellen: Assoziative Arrays sind nicht mehrdi- Assoziativ und mensional. Sehr wohl kann man aber beide Konzepte kombinieren. Dazu Mehrdimensional später mehr. Wenn Sie mehr Daten in einem Array speichern möchten, sind assoziative Arrays hilfreich. Das folgende Skript zeigt, wie die Daten eines Buches abgelegt werden.
Listing 5.8: Assoziatives Array erzeugen und ausgeben (array_assoziativ)
Die Indizes sind hier Zeichenketten, die den Zweck des Feldes klar hervorheben. Wenn Sie solche Arrays in Skripten sehen, sind diese bedeutend leichter zu lesen und zu warten. Abbildung 5.5: Ausgabe des Skripts aus Listing 5.8 Ebenso wie bei den vorangegangenen Beispielen können Sie statt der Anweisung array auch die Klammerschreibweise verwenden:
$wert) { echo "$feld = $wert "; } ?>
Listing 5.9: Verwendung der Klammerschreibweise (array_asso2)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
90 _____________________________________ 5 Komplexe Datenstrukturen: Arrays Das Skript entspricht im Prinzip dem vorhergehenden. Die Lesbarkeit ist etwas besser, dafür ist der Schreibaufwand höher. Ansonsten entspricht die Ausgabe den bereits gezeigten Varianten. In den meisten Fällen dürfte die Benutzung von array sinnvoller und kompakter sein. Typische Fehlerquellen Häufig finden man auch die folgenden Schreibweisen:
$buch[titel] = 'PHP 5. Grundlagen und Profiwissen'; $buch = array(titel => 'PHP 5. Grundlagen und Profiwissen'); Das funktioniert zwar meistens, ist aber nicht korrekt. Denn hier wird eine Eigenschaft von PHP ausgenutzt, die Gefahren in sich birgt. Wenn PHP einen Ausdruck nicht auflösen kann, wie beispielsweise »titel«, dann wird dieser intern in eine Zeichenkette umgewandelt. Deshalb ist dies der Schreibweise aus Listing 5.9 äquivalent. Wenn Sie jedoch einen Namen als Index verwenden, den PHP kennt, wird ein Laufzeitfehler erzeugt – das Skript funktioniert plötzlich nicht mehr: Laufzeitfehler
Parse error: parse error, expecting `'('' in ... Schreiben Sie Indizes immer in Anführungszeichen, auch wenn Sie sicher sind, dass es ohne ebenso funktionieren würde. Wenn PHP in späteren Versionen die vermeintlich freien Namen als reserviert einführt, laufen Ihre Skripte sonst nicht mehr.
5.1.3
Mehrdimensionale Arrays
Wenn Sie mit einer Tabellenkalkulation wie Excel arbeiten, kennen Sie schon die Struktur eines zweidimensionalen Arrays. Eine Dimension erstreckt sich über die Spalten, die andere über die Zeilen. Jede Zelle enthält aber mehrere Informationen: Formel, aktueller Wert, Name für Bezüge und Formatierung für die Darstellung. Das kann man mit einer dritten Dimension abbilden. Stellen Sie sich jetzt vor, Sie haben mehrere Arbeitsblätter, die alle dieselbe Struktur aufweisen – das wäre dann schon die vierte Dimension. Der Zugriff auf mehrere Dimensionen erfolgt mit Hilfe von mehreren eckigen Klammern: Text in Zelle 0,0; Blatt 0
$tabelle[0][0][0][0] = 'Text in Zelle 0,0' $tabelle[0][0][0][1] = 'text' $tabelle[0][0][0][2] = 'Text in Zelle 0,0'
Zahl in Zelle 1,3; Blatt 1
$tabelle[1][1][3][0] = '34.5896999' $tabelle[1][1][3][1] = 'Währung' $tabelle[1][1][3][2] = 'DM 34.59' Wenn Sie jetzt entsetzt auf das Klammergrab schauen, müssen Sie nicht in Panik verfallen. Mehr als zwei Dimensionen benötigen Sie nur sehr, sehr selten. Wenn Sie keine passable Lösung mit zwei Dimensionen finden, ist vermutlich etwas an Ihrem Datenmodell falsch oder unglücklich formuliert. Denken Sie auch daran, dass solche Gebilde gigantische Datenmengen aufnehmen können. Wenn Sie sich 1 024 Zellen in Spalten-
5.1 Wie Arrays funktionieren ____________________________________________ 91 und Zeilenrichtung vorstellen, die jeweils 4 Eigenschaften haben und sich auf 8 Blätter verteilen können, stehen 1 024 x 1 024 x 4 x 8 Elemente zur Verfügung. Das sind schon 33 554 432 Einträge. Bei 10 Byte pro Eintrag benötigt dieses Array über 300 MByte. Haben Sie wirklich so viele Daten? Der gezeigte Fall ist extrem konstruiert – Tabellenkalkulationen baut man zum einen nicht mit PHP und zum anderen nicht mit Arrays. Aber denken Sie über die Datenmenge und die Verteilung in einem Array nach und entscheiden Sie dann, ob wirklich eine weitere Dimension nötig ist.
5.1.4
Mehrdimensionale Arrays verarbeiten
Für die Verarbeitung können Sie die bereits gezeigten Techniken verwenden. Lediglich die Anzahl der Klammerpaare erhöht sich mit jeder Dimension.
"; } echo ''; } ?>
Listing 5.10: Erzeugen und ausgeben eines zweidimensionalen Arrays (array_multidim)
Hier wird ein Array $buch mit zwei Dimensionen erzeugt. Es enthält in der ersten Dimension zwei Elemente, in der zweiten dagegen drei (insgesamt also sechs). Die äußere Schleife durchläuft mit der Laufvariablen $k die erste Dimension. Die innere Schleife wird insgesamt zwei Mal angestoßen und arbeitet die jeweils die drei Werte der zweiten Dimension ab. Als Laufvariable kommt hier $i zum Einsatz. Beide Laufvariablen dienen als Index für die Ausgabe der Elemente:
echo "{$buch[$k][$i]} "; Beachten Sie hier die Klammerung der Variablen! Hilfreich ist hier, wie auch bei den einfachen Arrays, die Funktion count, count mit der die Anzahl der Elemente ermittelt werden kann. Prinzipiell spricht aber auch hier nichts dagegen, mit foreach anstatt mit for zu arbeiten. Das folgende Skript zeigt dies:
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
92 _____________________________________ 5 Komplexe Datenstrukturen: Arrays Listing 5.11: Ausgabe des zweidimensionalen Arrays mit foreach (array_multidim2)
"; } echo ''; } ?> Beide Skripte erzeugen eine identische Ausgabe, wobei letzteres kompakter wirkt, weil auf die Ermittlung der Arraygrenzen mit count verzichtet werden konnte. Allerdings erlaubt der Zugriff auf die Laufvariablen manchmal elegante Lösungen.
Abbildung 5.6: Ausgabe der Skripte aus Listing 5.10 und Listing 5.11
Die folgende Abbildung zeigt den Aufbau des zuletzt verwendeten zweidimensionalen Arrays. Abbildung 5.7: Aufbau eines zweidimensionalen Arrays
Mehrdimensionalität und Assoziativität Statt der gezeigten numerischen Indizes können natürlich wieder Zeichenketten eingesetzt werden – so entstehen mehrdimensionale, assoziative Arrays. Das ist einer der häufigeren Einsatzfälle, auch wenn es kompliziert klingt. Das letzte Beispiel zeigt eine solche Kombination aus einem numerischen Array (1. Dimension) und einem assoziativen Array (2. Dimension). Neben der Klammerschreibweise eignet sich hier wieder die Anweisung array zum Erzeugen der Daten.
Listing 5.12: Zweidimensionales Array mit assoziativen Indizes (array_2dim)
Das Skript verwendet in der ersten Dimension des Arrays die intern generierten automatischen Indizes. In der zweiten wird dagegen auf eigene Indizes zurückgegriffen. Bildlich ist dies ein eindimensionales, numerisch indiziertes Array, dessen Elemente assoziative Arrays sind. Abbildung 5.8: Ausgabe des Skripts aus Listing 5.12
In PHP5 sind assoziative und mehrdimensionale Arrays praktisch äquivalent.
5.2
Die Arrayfunktionen
Die Arrayfunktionen in PHP sind sehr üppig und für nahezu jedes Problem existiert eine elegante Lösung. Mit jeder Version, auch mit PHP5, fanden neue Funktionen den Weg in die Sprache. Die folgende Tabelle gibt einen Überblick, sortiert nach Aufgabengebieten. Aufgabenstellung
Informationen zu den einzelnen Funktionen finden Sie in der Referenz. Zu fast allen Arrayfunktionen sind interessante Beispielskripte zu finden. Die wichtigsten Methoden der Zugriffe werden in den folgenden Abschnitten dieses Kapitels noch exemplarisch vorgestellt. Probieren Sie die Beispiele aus, um ein Gefühl für den Umgang mit Arrays zu bekommen.
5.3
Zugriff auf Array-Elemente
Wenn Sie mit Arrays programmieren, ist der Zugriff auf bestimmte Elemente von großer Bedeutung. Die bisherigen Beispiele zeigten immer die Ausgabe des gesamten Arrays mit for oder foreach. Für den Zugriff auf bestimmte Teile eines Arrays oder die fallweise Abarbeitung stehen in PHP sehr viele Funktionen zur Verfügung.
5.3 Zugriff auf Array-Elemente __________________________________________ 95
5.3.1
Arrays durchlaufen
Wenn ein Array mit foreach durchlaufen wird, kümmert sich die An- Gezielt Elemente weisung selbst um das Auffinden des nächsten Elements. Wollen Sie eine auswählen andere Reihenfolge, hilft foreach nicht weiter – Änderungen der Arbeitsweise sind nicht möglich. PHP kennt aber andere Funktionen, die flexibler arbeiten. Dazu verwaltet PHP einen internen Zeiger auf jedes Array, der jeweils current auf ein Element zeigt – das aktuelle Element. Der Zugriff auf dieses aktuelle Element erfolgt mit current. Bewegen können Sie den Zeiger vom ersten Element aus gesehen nach next vorn mit next. Zurück geht es mit prev. Beide Funktionen reagieren prev nicht, wenn die Grenzen des Array überschritten werden. Erst ein nachfolgender Zugriff mit current führt zu einem Laufzeitfehler – der Zugriff geht dann ins Leere. Um den Zeiger wieder an den Anfang zu setzen, verwenden Sie reset. reset Analog existiert auch eine Funktion end. end Abbildung 5.9: Arbeitsweise der Arrayfunktionen
Die Grafik zeigt, wie die Funktionen wirken. Ausgehend vom aktuellen Element mit dem Index 3 stehen die Funktionsnamen neben dem Element, auf das der Arrayzeiger nach seiner Ausführung zeigt.
5.3.2
Mit Arrays spielen
Wenn die Daten erst mal im Array sind, können Sie diese vielfältig ma- array_walk nipulieren. Eine erste Funktion, die PHP-Neulinge dabei entdecken ist array_walk. Diese Funktion durchläuft jedes Element eines Arrays und führt mit diesem eine benutzerdefinierte Funktion aus. Damit lassen sich natürlich alle Arrayprobleme lösen. Es ist jedoch ein typischer Designfeh-
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
96 _____________________________________ 5 Komplexe Datenstrukturen: Arrays ler von PHP-Skripten, bereits vorhandene Funktionen »neu zu erfinden«. Das wichtigste Element in der Hand des Entwicklers ist dann auch nicht das Regal voller Bücher, sondern eine gute Referenz. Eigenschaften ermitteln Wenn Sie dazu die Anzahl der Elemente bestimmen müssen, setzen Sie die Funktion count ein. Beim Umgang mit Arrays ist auch is_array von Bedeutung. Mit dieser Funktion kann überprüft werden, ob eine Variable ein Array ist. unset eignet sich, um gezielt ein Element eines Arrays zu entfernen. Alle Elemente dahinter rutschen eine Position nach unten, sodass count nun ein Element weniger zählt. Daten wieder herausgeben list each
Für die Ausgabe eines Array bietet PHP5 ein spezielles Sprachelement, das bereits an einigen Stellen im Buch verwendet wurde: foreach. Es geht aber auch anders. Möglicherweise verleiht Ihnen diese Methode eine größere Flexibilität. Verwendet wird ein while-Schleife:
"; } ?> Die Funktion list überführt eine beliebige Anzahl Elemente eines Arrays in Variablen. each liest das aktuelle Element eines Arrays und gibt es zurück, danach wird der interne Zeiger des Arrays weiter gesetzt. Wenn keine Element mehr verfügbar sind, gibt die Funktion und damit der gesamte Ausdruck FALSE zurück. Die while-Schleife bricht dann ab. Mehr Informationen zu while bietet 110.
Abschnitt 6.3 Schleifen ab Seite
Nicht konsequent genug: zum Umgang mit dem Array-Zeiger Dieser interne Zeiger ist eine tückische Erfindung, denn nur die klassischen Funktionen aus PHP3 nutzen ihn. Die neueren, mit PHP4 eingeführten und mit PHP5 erweiterten Arrayfunktionen mit dem Präfix »array_« greifen nicht darauf zurück. Wenn Sie Arrays auslesen, sollten Sie sich für die eine oder andere Variante entscheiden.
5.3.3
Interessante Arrayeffekte
Mit Arrayfunktionen kann man vielfältige Effekte erzielen, auch wenn es dabei nicht immer um Arrays geht. Angenommen, Sie wollen eine Datei in eine Zeichenkette einlesen. Die direkteste Funktion zum Lesen einer Datei ist file. Leider legt file die Datei in einem Array ab, als Trennzeichen wird »\n« verwendet. implode fasst ein Array zu einer Zeichenkette
5.3 Zugriff auf Array-Elemente __________________________________________ 97 zusammen, wobei ein Trennzeichen angeben werden kann. Dieses Trennzeichen kann auch eine leere Zeichenkette sein. Das folgende Skript zeigt, wie eine Datei in einer Zeichenkettenvariablen abgelegt wird:
$strFile = implode(file('dateiname.ext'), ''); Das nächste Beispiel zeigt, wie ein Array mit array_walk und array_count_values untersucht wird. Listing 5.14 zeigt den Code. Dabei werden die Anzahl Artikel, die von einem Verlag im Programm sind, gezählt. Die Verlagsnummer ist der zweite Teil einer ISBN.
$c1 Verlage liefern $c2 Artikel: "; foreach($count as $publisher => $number) { echo "Verlag $publisher ($number) Bücher "; } ?> Im Skript wird die Funktion $isbn_select für jedes Element des Arrays $book einmal aufgerufen. Informationen darüber, wie Sie eigene Funktionen anlegen und nutzen bietet Abschnitt 6.4 Ordnung ins Chaos: Funktionen ab Seite 118. Hier wird der zweite Teil (Index 1) der ISBN extrahiert:
$a = $arrA[1]; Das so reduzierte Element wird zurückgeben. Daraus entsteht ein Array, das nur Verlagsnummern enthält. Dann wird die Arrayfunktion array_count_values zur Ermittlung der Anzahl gleicher Elemente genutzt. Diese Funktion gibt ein Array zurück, dass zu jedem Element die Anzahl enthält. Dieses Array ($count) wird dann ausgegeben. Dabei wird folgende Ausgabe erzeugt:
Listing 5.14: Analysieren von ISBN-Nummern in einem Array (array_walk)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
98 _____________________________________ 5 Komplexe Datenstrukturen: Arrays Abbildung 5.10: Arrays analysieren: Listing 5.14 Listing 5.15 zeigt, wie mit wenigen Zeilen eine Funktion definiert werden kann, die mehrere Elemente aus einem Array entfernt. Listing 5.15: Element in einem Array entfernen (array_remove)
"; $arr = remove_element($arr, 3, 4); foreach($arr as $e) echo "$e "; ?> Die eigentliche Arbeit wird in der Funktion remove_element erledigt, die hier benutzt wird, um den Ablauf etwas übersichtlicher zu gestalten. Die Parameter $from und $to geben die Elementindizes an, an denen getrennt werden soll (von, bis). Der vordere und hintere Teil wird mit array_slice abgetrennt und danach mit array_merge wieder zusammengefügt. Weil die abgetrennten Einzelteile nicht mehr benötigt werden, wird die Entfernung mit unset forciert. In großen Projekten spart dies Speicher, weil nicht bis zum Ende des Skripts gewartet werden muss, bis PHP den Speicher aufräumt. Die Ausgabe sieht folgendermaßen aus:
Abbildung 5.11: Ausgabe des Skripts aus Listing 5.15 So sollten Sie weiter machen Arrays sind ein umfassendes und leistungsfähiges Gebiet der Programmierung. Sie sollten sich damit intensiv auseinander setzen und immer dann in die näheren Überlegungen einbeziehen, wenn mehr als zwei gleichartige Daten zu verarbeiten sind. Im Referenzbuch finden Sie weitere Beispiele. Achten Sie auch auf die Verwendung von Arrays in fremden Skripten.
5.3 Zugriff auf Array-Elemente __________________________________________ 99
6 Das Steuerrad für PHP: Kontrollstrukturen Nahezu jedes Skript benötigt Anweisungen, die den Programmfluss steuern. In diesem Kapitel wird gezeigt, wie diese Flüsse entstehen, welche Anweisungen dazu eingesetzt werden und wie sie funktionieren.
PHP kennt alle für Programmiersprachen typischen Anweisungen, mit denen Kontrollstrukturen gebildet werden. Dabei kann man zwei Arten unterscheiden: Schleifen und Verzweigungen. Schleifen definieren einen Bereich, der in Abhängigkeit von Bedingungen mehrfach abgearbeitet wird. Verzweigungen testen Bedingungen und führen dann einen bestimmten Programmzweig einmal aus.
6.1.1
Strukturiertes Programmieren
Zur Darstellung eignen sich bestimmte Diagramme, die in Programmablaufplänen verwendet werden. Das ist jedoch schon sehr theoretisch und soll hier nicht tiefgehend behandelt werden. Nur soviel: PHP erzwingt eine strukturierte Programmierung. Anweisungen zum »wilden« springen in andere Strukturen, wie »goto«, fehlen völlig. Programme bestehen aus einer Folge von einigen oder allen Strukturelementen aus folgender Liste: • Folgen (Sequenzen) • Verzweigungen (Selektionen) • Schleifen (Iterationen) In Klammern finden Sie die in der Informatik üblichen Fachbegriffe. Folgen Folgen bilden Sie einfach durch Aufschreiben von Anweisungen. Der PHP-Interpreter führt diese dann in dieser Reihenfolge aus. In manchen Fällen beziehen sich Anweisungen auf Gruppen solcher Anweisungen ebenso wie auf eine einzige. Dann werden Blöcke gebildet. Verzweigungen Für Verzweigungen werden folgende Anweisungen eingesetzt: • if
• switch if kennt eine Einfachauswahl und eine Zweifachauswahl. Beides wird im folgenden Struktogramm gezeigt.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
102 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Abbildung 6.1: Struktogramme einer Verzweigung
switch erlaubt dagegen ganze Serien solcher Verzweigungen. Auch hierfür gibt es eine entsprechende Darstellung. Abbildung 6.2: Struktogramm für switch
Auch switch kennt zwei Varianten: mit und ohne zusätzlichem Standardzweig. Die Schleifen Schleifen können mit folgenden Anweisungen gebildet werden: • for • foreach • while • do Normalerweise gelten die Anweisungen immer für den folgenden Befehl. Um Befehlsfolgen zu verarbeiten – was der Regelfall ist – werden Blöcke gebildet. Diese sollen deshalb gleich am Anfang des Kapitels vorgestellt werden.
6.1 Übersicht__________________________________________________________ 103 Abbildung 6.3: Struktogramm while
Abbildung 6.4: Struktogramm do
Abbildung 6.5: Struktogramm for
Die Struktogramme ergeben, als Bestandteile eines Programmflussplans aufgezeichnet, eine eindeutige Anleitung der tatsächlichen Flussrichtung des Programms. Was sich damit nicht zeichnerisch darstellen lässt, funktioniert auch nicht.
6.1.2
Blöcke
PHP verwendet Blöcke, um mehrere Befehle zusammenzufassen und { } damit als Einheit zu betrachten. Blöcke werden durch Paare geschweifter Klammern gebildet:
Kontrollanweisung { // Blockanweisung // Blockanweisung } PHP kann so erkennen, welche Befehle zusammengehören. Vor allem bei Blöcke und der Vermischung von PHP und HTML müssen Sie Blöcke bilden, denn HTML PHP beendet einen nicht explizit markierten Block am Ende des Skriptfragments. Der folgende Code funktioniert nicht:
HTML ist toll!
Sie müssen hier kennzeichnen, dass der HTML-Code zur if-Anweisung gehört:
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
104 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen
HTML ist toll!
6.2
Verzweigungen
Verzweigungen sind ein elementarer Bestandteil auch kleiner Programme. Dabei wird der ein oder andere Programmteil in Abhängigkeit von einer Bedingung ausgeführt.
6.2.1
Bedingungen
Bedingungen sind das bestimmende Element zur Steuerung von Verzweigungen. Es gibt praktisch kaum ein Programm, das völlig ohne die Steuerung mit Bedingungen auskommt. Das Programm fällt mit Hilfe der Bedingung eine einfache Entscheidung: Ja oder Nein. Entsprechend müssen Ausdrücke, die in den Bedingungsbefehlen eingesetzt werden, logische Ausdrücke sein. Das Ergebnis muss für PHP als Wahr (TRUE) oder Falsch (FALSE) interpretierbar sein. Ausdrücke konstruieren
Um Ausdrücke zu konstruieren, werden Operatoren benötigt. Man kann dabei logische Operatoren und Vergleichsoperatoren unterscheiden. Den eigentlichen Programmablauf steuern dann Anweisungen wie if, die nachfolgend vorgestellt werden. Auch die Befehle zur Schleifensteuerung nutzen logische Ausdrücke. Logische Operatoren
and or xor ! && ||
In vielen Abfragen wird eine logische Entscheidung (Ja/Nein) verlangt. Mit speziellen Operatoren können Sie Ausdrücke verknüpfen. Das Ergebnis eines korrekten logischen Ausdrucks ist immer 0 (FALSE, Falsch) oder 1 (TRUE, Wahr). Die folgenden Ausdrücke zeigen die Anwendung:
$x and $y $x or $y $x xor $y $x && $y $x || $y !$x
// // // // // //
ist wahr, wenn $x UND $y wahr ist ist wahr, wenn $x ODER $y wahr ist falsch, wenn beide gleich sind entspricht and entspricht or negiert den Wert FALSE=TRUE, TRUE=FALSE
Zwischen der Form && und and gibt es keinen Unterschied in der Ausführung der Operation. Bei der Notation sieht es anders aus. Die Darstellung mit Schlüsselworten (and, or) statt Operatorsymbolen (&&, ||) spart Klammern. Der folgende Ausdruck muss mit dem Schlüsselwort geschrieben werden:
if ($a == 45 and $b == 23) Wenn Sie die Symbole verwenden, sind zusätzliche Klammern nötig:
if (($a == 45) && ($b == 23))
6.2 Verzweigungen ____________________________________________________ 105 Vergleichsoperatoren Um komplexe logische Ausdrücke erstellen zu können, benötigt man ==, !=, === auch Vergleichsoperatoren. Die folgende Tabelle gibt einen Überblick. <, >, <=, >= Operatorsymbol
Bedeutung
==
Gleichheit
===
Identität (wert- und typgleich)
!=
Ungleichheit
!==
Ungleichheit bei Wert oder Typ
>
Größer als
<
Kleiner als
>=
Größer als oder gleich
<=
Kleiner als oder gleich
Tabelle 6.1: Vergleichsoperatoren
Vergessen Sie nie, dass der Gleichheitsoperator == nicht dem Zuwei- Typische sungsoperator = entspricht – ein wesentlicher Unterschied zu einigen Probleme anderen Sprachen, mit dem vor allem Umsteiger zu kämpfen haben. Der folgende Ausdruck ist syntaktisch völlig korrekt, aber trotzdem falsch und vor allem sinnlos:
if ($var = 17) { echo $var; } Gemeint war sicher, dass die Variable ausgegeben wird, wenn der Inhalt gleich 17 ist. Praktisch passiert aber Folgendes: Die Variable wird durch den Zuweisungsausdruck gleich 17 gesetzt – unabhängig von ihrem vorherigen Inhalt. Dann wird die 17 ausgegeben. Die Zuweisung selbst, als Ausdruck betrachtet, wird korrekt abgearbeitet und von if als Wahr erkannt. Wenn Sie so einen bestimmten Zustand im Programm erkennen möchten, wird Ihnen dies vielleicht nicht einmal auffallen. Es gibt eine einfache Lösung zum Verhindern solcher Fallen. Vertauschen Trickreiche Sie einfach die Operanden: Lösung
if (17 = $var) Dieser Ausdruck ist syntaktisch definitiv falsch. Zuweisungen können nicht »rückwärts« arbeiten. Der Fehler wird vom Parser sofort erkannt. Schreiben Sie dagegen den Gleichheitsoperator korrekt, ergibt sich ein gültiger Ausdruck:
if (17 == $var) Dies sieht zwar ungewöhnlich aus, ist aber sehr programmierfreundlich.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
106 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen
6.2.2 if
Syntax
Bedingungen mit if auswerten
if (dt. falls oder wenn) testet eine Bedingung und führt die dahinter stehenden Anweisung aus, wenn die Bedingung Wahr ist, ansonsten wird sie übergangen: if (Bedingung) Anweisung; Wenn mehrere Anweisungen von der Bedingung abhängig sind, muss ein Block gebildet werden:
Syntax
if (Bedingung) { Anweisung-1; Anweisung-2; Anweisung-3; } Anwendung der if-Anweisung Das folgende Beispiel zeigt, wie if verwendet wird:
if ($tag == "Montag") echo "Heute ist Montag"; Die if-Anweisung besteht aus dem Schlüsselwort und dem in runden Klammern stehenden logischen Ausdruck. Wenn der Ausdruck Wahr ist, wird die nachfolgende Anweisung oder der Block (in geschweiften Klammern) ausgeführt. Ist der Ausdruck Falsch, wird die Programmausführung mit der nächsten Anweisung oder dem nachfolgenden Block fortgesetzt. Folgen mehrere abhängige Anweisungen, ist also folgendermaßen zu schreiben:
if ($tag == "Montag") { echo "Heute ist Montag"; echo "
«; } Das folgende Beispiel nutzt die schon bekannten Datumsfunktionen, um eine entsprechende Begrüßung zu erzeugen: Listing 6.1: Steuerung mit if (if_simple)
Das Beispiel ist sicher primitiv. Praktisch kann immer nur eine der Alternativen erfüllt sein, denn $tag enthält einen ganz bestimmten Wert. Allerdings besteht auch die – zumindest theoretische Chance – dass keine
6.2 Verzweigungen ____________________________________________________ 107 der Bedingungen zutrifft. Diesen Zustand abzufangen, ist mit ifAnweisungen dieser Art schon schwieriger. Im Abschnitt 6.2.3 Mehrfachauswertungen mit switch ab Seite 108 finden Sie eine elegantere Alternative. Mehr zu date finden Sie in Abschnitt 13.4 Zeitwächter: Datumsfunktionen ab Seite 336. Wenn man weiter zu komplexeren Entscheidungsbäumen vordringt, sind if ist unbegrenzt Verschachtelungen unvermeidlich. Prinzipiell kann die if-Anweisung verschachtelbar unbegrenzt verschachtelt werden, das heißt, in jedem Block kann sich wiederum eine if-Anweisung verstecken. Das führt in der Regel nicht zu besonders gut lesbarem Code und sollte vermieden werden. Es gibt jedoch elegantere Lösungsmöglichkeiten. Oft ist es notwendig, nicht nur auf das Eintreten eines Ereignisses zu reagieren, sondern auch die negative Entscheidung zu behandeln. In solchen Fällen wird die if-Anweisung um else (dt. sonst) ergänzt. Die Anweisung oder der Block hinter else wird ausgeführt, wenn die Bedingung nicht zutrifft. Das folgende Beispiel führt eine solche zweistufige Entscheidung durch; else egal wie das Datum ist, in jedem Fall muss eine Ausgabe erfolgen. elseif
Listing 6.2: if in Verbindung mit else (if_else)
Das Skript verwendet else, um eine Alternative zum Zustand 6 oder 0 der Variablen $tag zu erkennen. Auch hier sind Fehler möglich – auch 99 erfüllt diese Bedingung und führt zu der Ausgabe »Arbeitszeit«. Mehr zu date finden Sie in Abschnitt 13.4 Zeitwächter: Datumsfunktionen ab Seite 336. An dieser Stelle eine kurze Erklärung zur hier eingesetzten Funktion: Der Parameter »w« gibt die Nummer des Wochentages zurück, wobei die Zählung mit dem Sonntag (= 0) beginnt. Folglich ist das Wochenende am Tag 6 (Samstag) oder am Tag 0 (Sonntag). Das Beispiel aus Listing 6.1 könnte man auch mit elseif schreiben. Praktisch wird dabei in dem else-Zweig noch eine weitere if-Anweisung eingebaut. elseif spart also im Wesentlichen nur Schreibarbeit. Das folgende Beispiel zeigt eine mögliche Anwendung:
Listing 6.3: Beispiel mit elseif (if_elseif)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
108 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen
} ?>
echo "Arbeitszeit";
Die Erweiterung mit elseif verbessert die letzte Konstruktion weiter. Konsequent wäre die Formulierung der Bedingungen auch für den letzten else-Zweig – dann ist das Skript gegen Eingabefehler sicher. Versuchen Sie, dies selbst zu formulieren. Der Bedingungsoperator ... ? ... : ...;
PHP kennt eine Kurzschreibweise für einen einfachen Bedingungsbefehl, auch Bedingungsoperator genannt:
$var == "test" ? $result = true : $result = false; Damit lassen sich Abfragen oft kürzer und lesbarer gestalten. Die drei Elemente haben folgende Bedeutung:
Bedingung ? Wenn TRUE : Wenn FALSE Typische Probleme
Wichtig ist, daran zu denken, dass Ausdrücke erwartet werden, die etwas zurück geben. Operatoren benötigen zum Arbeiten Operanden. Der folgende Ausdruck funktioniert deshalb nicht:
$a == 66 ? echo "A ist 66" : echo "A ist nicht 66"; Woran liegt das? Die Anweisung echo gibt nichts zurück, sie ist keine Funktion. In diesem speziellen Fall müssen Sie die Funktion print verwenden. Sie gibt das, was zum Browser ausgegeben wurde, auch zurück. Das wird zwar im weiteren Verlauf der Abarbeitung ignoriert, aber die Operatoren bekommen ihre Operanden, um der Semantik zu genügen.
6.2.3 switch() { case: break; }
Listing 6.4: Entscheidungsbäume mit switch aufbauen (switch)
Mehrfachauswertungen mit switch
Wenn Sie wie in Listing 6.1 mehrere aufeinanderfolgende Bedingungen gegen ein und dieselbe Variable testen möchten, ist die if-Anweisung sehr aufwändig. Mit switch steht eine weitere Anweisung zur Verfügung, die solche Listen eleganter aufbaut:
Den prinzipiellen Aufbau zeigt Listing 6.4. In der switch-Anweisung selbst steht die zu testende Variable, in den case-Abschnitten jeweils der Testwert, der auf Gleichheit getestet wird. case 8 entspricht dabei dem Ausdruck $stunde == 8. Zweige mit break verlassen Wichtig ist die break-Anweisung, die die Arbeitsweise von switch we- break sentlich beeinflusst. Wenn switch eine zutreffende Bedingung findet, wird nach der case-Anweisung mit der Ausführung des Codes begonnen. Weitere case-Anweisungen werden nicht ausgewertet; die enthaltenen Befehle werden aber ausgeführt. Der switch-Block muss also mit break explizit verlassen werden. Einen Zwang zur Anwendung gibt es natürlich nicht; manchmal ist der Effekt ja auch beabsichtigt. Die Ausgabe einer definierten Anzahl von Zeichen ist natürlich mit entsprechenden Funktionen oder Schleifen einfacher und eleganter lösbar. Durch den gezielten Einsatz (oder Nicht-Einsatz) von break können raffinierte Konstruktionen geschaffen werden. In Listing 6.4 wurde bereits der Befehlsbestandteil default genutzt. Die- default: ser Zweig der switch-Anweisung wird ausgeführt, wenn keine andere Bedingung zutrifft. Allerdings wird default erreicht, wenn hinter der zutreffenden Bedingung kein break erfolgt. Die Beschränkung des Bedingungstests auf Gleichheit mag als ernsthafte Erweiterte Behinderung erscheinen. Glücklicherweise kennt PHP eine erweiterte Syntax: Notation der Syntax, die das Problem zumindest teilweise lindert. Dabei case...: case... können mehrere Vergleiche hintereinander ausgeführt werden, die Operanden sind quasi Oder-Verknüpft:
Listing 6.5: switch mit case und break (switch_case)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
110 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Hier wird der Vergleich solange ausgeführt, bis ein break erreicht wurde. Zuerst erfolgt der Vergleich auf 1. Ist er erfolgreich, wird dieser Zweig ausgeführt. Nach dem break wird am Ende der switch-Anweisung fortgesetzt. Andernfalls erfolgt der Vergleich auf 2. Ist dieser erfolgreich, wird derselbe Zweig ausgeführt:
case 1: case 2: Waren beide Vergleiche nicht zutreffend, wird mit dem nächsten case fortgefahren:
case 7: case 0: case "six": Wurden überhaupt keine Übereinstimmungen gefunden, wird der alternative Zweig ausgeführt:
default: echo "In der Woche"; Das »Durchfall-Problem« Falle!
Wenn Sie zwei Vergleiche nacheinander durchführen und dazwischen Anweisungen platzieren, verhält sich switch anders. Im folgenden Code werden beide Ausgaben erzeugt, wenn die erste Bedingung zutreffend ist:
$a = 17; switch ($a) { case 17: echo “Siebzehn”; case 19: echo “Neunzehn”; } Hier fehlt offensichtlich break. In den meisten Fällen ist break keine nette Option, sondern unbedingt erforderlich.
6.3
Schleifen
Schleifen benötigen Sie, um Programmteile mehrfach durchlaufen zu lassen. Neben der Einsparung an Tipparbeit ist vor allem die variable Festlegung der Schleifendurchläufe interessant. Schleifen ohne feste Laufvariable werden durch eine Bedingung gesteuert. Der Zustand des logischen Ausdrucks bestimmt, ob die Schleife weiter durchlaufen wird oder nicht.
6.3.1 while
Standardschleifen mit while
Die häufigste Schleifenart ist die while-Schleife, die in fast jeder Programmiersprache zu finden ist. Die Bedingung wird mit jedem Eintritt in die Schleife getestet. Solange der Ausdruck TRUE zurückgibt, wird die
6.3 Schleifen __________________________________________________________ 111 Schleife durchlaufen. Wenn der Ausdruck also schon beim Eintritt in die Schleife FALSE ergibt, wird die Schleife überhaupt nicht durchlaufen.
Listing 6.6: Einfachste Form einer whileSchleife (while)
In diesem Skript werden zuerst zwei Variablen definiert. Eine wird als Laufvariable mit wechselnden Werten eingesetzt, die andere zur Steuerung der Abbruchbedingung.
$counter = 0; $test = 6; Mit dem ersten Aufruf der while-Anweisung wird die Bedingung geprüft – im Extremfall kann es vorkommen, dass der Körper der Schleife überhaupt nicht ausgeführt wird.
while ($test > $counter) Innerhalb der Schleife wird die Zählvariable ausgegeben und anschließend um eins erhöht.
{ }
echo "Aktueller Zähler: $counter "; $counter++;
Achten Sie unbedingt darauf, dass einfache Schleifen eine klare Abbruchbedingung haben. Endlosschleifen können das System durchaus zum Stillstand bringen. Die Problematik der Abbruchbedingung kann oft umgangen werden, break indem zusätzlich ein Notausstieg eingebaut wird. Das folgende Beispiel zeigt eine fehlerhaft programmierte Schleife – die Abbruchbedingung wird regulär nie erfüllt. Der Notausstieg verwendet die schon bekannten break-Anweisung, die die Ausführung an die nächsthöhere Programmebene zurückgibt; dies ist normalerweise der Befehl, der auf die schließende Klammer folgt.
112 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Die Schleife entspricht der Vorhergehenden. Wenn jedoch wegen falscher Startwerte $counter bis auf 50 erhöht wird, unterbricht break die Ausführung der Schleife:
if ($counter == 50) break; Anschließend setzt das Skript nach der schließenden Klammer der whileAnweisung fort. Der Begriff Notausstieg sollte hier nicht überbewertet werden. Die Anwendung der break-Anweisung ist eine reguläre Programmiertechnik. Als Anfänger sollten Sie sich aber vielleicht die eine oder andere breakAnweisung einbauen, um sicher durch alle Schleifen zu kommen. Profis werden besser einschätzen können, ob die gewählten Schleifenbedingungen allen Programmsituationen genügen werden.
6.3.2
Die »Mindest-Schleife« mit do ... while
Der Test der Bedingung am Anfang hat einen wesentlichen Nachteil, wenn der Inhalt des Blocks für die weitere Programmfortsetzung unbedingt erforderlich ist. Es ist möglich, dass die Bedingung so wirkt, dass der Inhalt nie durchlaufen wird. Um das sicherzustellen, gibt es die folgende Konstruktion:
do { // block } while (Bedingung); Der einzige Unterschied ist die Reihenfolge der Abarbeitung. Zuerst wird die Schleife einmal durchlaufen und am Ende die Abbruchbedingung getestet. Auch dann, wenn die Abbruchbedingung beim Schleifeneintritt FALSE ist, wird der Block mindestens einmal ausgeführt. Listing 6.8: "; $counter++; } while ($counter <= $test) ?> Hier werden zuerst der Startwert der Laufvariablen $counter und der Variablen für den Abbruchwert zugewiesen:
$counter = 0; $test = 6; Dann wird die Schleife mindestens einmal ausgeführt:
do { echo "Aktueller Zähler: $counter "; In der Schleife wird die Laufvariable erhöht (Inkrementoperator):
$counter++;
6.3 Schleifen __________________________________________________________ 113 Dann wird am Ende der Schleife eine Bedingungen getestet. Solange der Ausdruck Wahr ist, wird an den Anfang gesprungen und der Block erneut ausgeführt.
} while ($counter <= $test) Der Einsatz von break ist natürlich genauso und aus demselben Grund möglich wie bei while. Wenn Schleifen aus dem Block heraus beendet werden können, wäre continue auch ein forciertes Auslösen des Bedingungstests sinnvoll. Dies können Sie mit der Anweisung continue erreichen. Diese setzt die Ausführung sofort mit dem Test der Schleifenbedingung fort. Ist die Schleifenbedingung in diesem Augenblick FALSE, wird die Schleife mit der Anweisung continue verlassen.
"; $counter++; } ?> Auch hier werden zuerst die Kontrollvariablen initialisiert:
$counter = 0; $test = 10; Dann beginnt die Schleife mit der ersten Prüfbedingung:
while ($counter < $test) Falls $counter eine gerade Zahl enthält (der Modulus-Operator gibt als Rest der Division durch Zwei die Zahl 0 zurück) wird die Verarbeitung des Anweisungsblocks unterbrochen und sofort mit der nächsten Prüfbedingung am Schleifenanfang fortgesetzt:
if ($counter % 2) { $counter++; continue; } Andernfalls läuft die Schleife normal weiter und eine Ausgabe erfolgt:
echo "Aktueller Zähler: $counter "; Den Abbruch durch die Prüfbedingung steuert die Laufvariable, die auch hier inkrementiert wird:
Listing 6.9: Umgang mit continue (while_continue)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
114 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen $counter++; Das Beispiel in Listing 6.9 zeigt eine Anwendung, in der mit Hilfe einer weiteren if-Bedingung ein Teil des Schleifenblocks ausgeblendet wird. Die Schleife zeigt hier nur gerade Zahlen an. Es ist durchaus möglich und oft sinnvoll, break und continue gemeinsam zu verwenden.
6.3.3
Abzählbare Schleifen: for(;;)
Die vorangegangenen Beispiele dienten vor allem der Erläuterung der Syntax der Befehle, die feste Vorgabe von unteren und oberen Grenzen ist keine typische Anwendung der while-Schleifen. In solchen Fällen setzen Sie besser for-Schleifen ein. Die Abbruchbedingung ist allerdings auch hier ein normaler logischer Ausdruck. Zusätzlich kann eine numerische Variable mitgeführt werden – die Zählvariable.
Wie Sie Ihre Schleifenvariablen benennen sollten Als Variablennamen kommen typischerweise einzelne Buchstaben wie $j, $k oder $i zum Einsatz. Dies ist keine Vorschrift oder gar Eigenheit von PHP, sondern dient lesbaren Skripten. Alle Parameter dieser Anweisung sind optional. Bei vollständiger Angabe ist die for-Schleife jedoch komplexer als die bisher behandelten Schleifentypen:
for(start, bedingung, iteration); Dies ist die einfachste Form der Anwendung. Die folgende Schleife zeigt Schrift in verschiedenen Größen an: Listing 6.10: Einfach forSchleife (loop_for)
for-Schleife'; } ?> Die Schleife arbeitet mit der Laufvariablen $i. Der Startwert ist 10. Die Schleife wird solange durchlaufen, wie $i kleiner oder maximal gleich 24 ist. Nach jedem Durchlauf wird $i um 2 erhöht. Dabei wird die Zählvariable bei jedem Durchlauf um 2 erhöht.
6.3 Schleifen __________________________________________________________ 115 Abbildung 6.6: Ausgabe des Skripts aus Listing 6.10
Die Variable in den drei Elementen der for-Schleife muss nicht durchgehend verwendet werden. Dies ist zwar im Hinblick auf lesbare Skripte zu empfehlen, notwendig ist es jedoch nicht, wie das folgende Beispiel zeigt:
for-Schleife'; $k += 2; } ?>
Listing 6.11: Abbruchbedingung und Iteration nutzen verschiedene Variablen (for.2varloop)
In diesem Skript wird $i wieder als Laufvariable eingesetzt. Allerdings findet sie im Block überhaupt keine Verwendung. Das ist auch nicht notwendig. Es geht hier nur darum, sicher eine bestimmte Anzahl von Durchläufen des Anweisungsblocks auszuführen. Zusätzlich wird die zur Anzeige benötigte Variable $k in der for-Anweisung initialisiert. Das wirkt sich aber auf den Verlauf der Schleife nicht aus. Alle drei Parameter der for-Schleife sind optional. Ohne Iterationsvariab- for(;;) le wird die Schleife endlos durchlaufen. Sie können in diesem Fall wieder continue auf break zurückgreifen, um die Schleife mit einer zusätzlichen Bedin- break gung zu verlassen. Ebenso kann der Test der Abbruchbedingung und die Ausführung des Iterationsschrittes mit continue erzwungen werden.
"; if ($i > 5) break; } ?>
Listing 6.12: for ohne Parameter (for_endless)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
116 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Die leere for-Anweisung führt eine Endlosschleife aus. Die Steuerung des Abbruchs übernimmt nun break:
if ($i > 5) break; Eine andere Schreibweise für eine Endlosschleife ist übrigens while(TRUE). Der flexible Umgang mit den Schleifenvariablen kennt praktisch keine Grenzen. Es spielt offensichtlich keine Rolle, ob hier Variablen zum Einsatz kommen oder nicht. Wenn Sie nur einfach eine Liste ausgeben möchten, kann der entsprechende Befehl sehr knapp ausfallen. Mehrere Anweisungen im Parameterbereich werden durch Kommata getrennt: Listing 6.13: Mehrere Ausdrücke in for (for_multi)
"); ?> Im Beispiel wird nicht zufällig die Anweisung print anstatt des flexibleren echo verwendet. echo ist ein Sprachkonstrukt, keine Funktion, und gibt deshalb nichts zurück. print dagegen wird die Parameter zurückgegeben. Normalerweise wird dieser Rückgabewert weggeworfen. Die forSchleife erwartet jedoch von jedem direkt eingegebenen Wert, dass er sich als Ausdruck verhält – Ausdrücke geben immer etwas zurück. An dieser Stelle kann echo deshalb nicht funktionieren. Versuchen Sie es dennoch, erhalten Sie einen Laufzeitfehler.
6.3.4
Felder bestellen: foreach
Manchmal werden die Inhalte von Arrays ausgegeben. Wenn sich der Inhalt ändert, ist die für eine for-Schleife nötige Größenangabe ein zusätzlicher Schritt. Den können Sie sich sparen, wenn Sie foreach verwenden. Wörtlich übersetzt heißt diese Anweisung: »Für jeden«. Die Schleife wird also für jedes Element eines Arrays durchlaufen. Gegenüber der while-Schleife mit list und each vereinfacht sich die Syntax deutlich. Hier die einfachste Syntax für eindimensionale Arrays:
foreach($array as $element) { ... } Dabei wird das Array $array von Anfang bis Ende durchlaufen und bei jedem Schleifendurchlauf wird das aktuelle Element der Variablen $element zugewiesen. Das Schlüsselwort as ist fester Bestandteil der Anweisung und niemals optional. Zuerst ein einfaches Beispiel mit einem assoziativen Array: Listing 6.14: Ausgabe eines einfachen Arrays (foreach)
Im Skript wird zuerst ein Array aufgebaut. Die Daten können ebenso auch einer Datenbank entstammen:
$plz = array("12683","12459","10999","13055"); Dann überträgt foreach das erste Element in die Laufvariable $postleitzahl.
foreach($plz as $postleitzahl) { Dieser Wert wird ausgegeben.
echo "$postleitzahl "; Am Ende wird wieder zum Schleifenanfang gesprungen und das nächste Element des Arrays ausgelesen. Sind keine Elemente mehr vorhanden, bricht die Schleife ab und das Programm wird nach der schließenden Klammer fortgesetzt.
} Abbildung 6.7: Ausgabe des Skripts aus Listing 6.14 Wenn Sie Arrays mit Schlüssel-/Wertepaaren bauen, kann foreach mit foreach für einer erweiterten Syntax diese Paare direkt auslesen. Die grundlegende Arrays mit Schlüsseln Syntax lautet:
foreach($array as $schluessel => $wert) { # auszuführende Befehle } Hier wird der Operator => eingesetzt, der schon bei der Konstruktion des => Arrays verwendet wurde.
"Krause", "Ort" => "Berlin", "Website" => "www.joerg.krause.net"); foreach($myarray as $key => $val) { echo "$key: $val "; } ?> Diese Schleife arbeitet wie die zuerst vorgestellte, nur werden hier sowohl Schlüssel als auch Werte ausgelesen. Wenn Sie ein eindimensionales Array auslesen werden als Schlüssel die internen Indizes ausgegeben – fortlaufenden Zahlen, beginnend mit 0 für das erste Element.
Listing 6.15: Assoziative Arrays mit Schlüssel/Wertpaaren (foreach_hash)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
118 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Mehr Informationen zu Arrays und Anwendungsbeispiele für foreach finden Sie im Abschnitt 5.3 Zugriff auf Array-Elemente ab Seite 94. Abbildung 6.8: Ausgabe des Skripts aus Listing 6.15
6.4
Ordnung ins Chaos: Funktionen
Funktionen sind kleine Programmabschnitte oder Module, die PHPCodes enthalten. Sie werden, ebenso wie die bereits erläuterten eingebauten Funktionen, aus anderen Funktionen oder der »Stammebene« des Skripts heraus aufgerufen. An Funktionen können Sie Parameter übergeben und diese im Original ändern lassen oder eine Kopie des Wertes verwenden. Funktionen können Werte zurückgeben, beispielsweise das Ergebnis einer Berechnung.
6.4.1 function
Die Welt erschaffen: Funktionsdefinition
Nutzerdefinierte Funktionen werden durch das Schlüsselwort function eingeleitet. Die Syntax kann folgendermaßen aussehen:
function myfunction($param, $param2, ...) { // Hier werden Befehle ausgeführt return $rueckgabe; } Der Funktionskopf besteht aus dem Namen der Funktion (hier: myfunction) und in runden Klammern einer Auflistung der erwarteten Parameter. Diese Werte werden der Funktion beim Aufruf übergeben. Ein Zwang zur Übergabe von Werten besteht nicht, schreiben Sie einfach ein leeres Klammernpaar. return
Die Anweisung return enthält als Parameter den Rückgabewert. Dies kann ein Ausdruck oder eine einzelne Variable sein. An welcher Stelle innerhalb der Funktion Sie return einsetzen, spielt keine Rolle. Auch die mehrfache Notation ist zulässig – hier wird nach dem Motto »Wer zuerst kommt, siegt« verfahren und die Funktion wird sofort verlassen. Aus Gründen sauberer Programmierung sollten Sie aber return trotzdem nur einmal und nur am Ende einsetzen.
Späte Bindung
Wo im Skript die Funktion definiert wird, spielt bei PHP5 keine Rolle. Man spricht in diesem Fall von einer späten Bindung (engl. late binding). Hintergrundinformationen dazu finden Sie im nächsten Kasten.
6.4 Ordnung ins Chaos: Funktionen ______________________________________ 119
Niemals zu spät: Späte Bindung in PHP5 In PHP3 mussten Funktionen vor ihrer ersten Verwendung im Skript geschrieben werden. Der PHP-Interpreter arbeitete das Skript von oben nach unten durch. Fand er einen Funktionsaufruf und die Definition der Funktion stand dahinter, passierte nichts. In PHP4 und PHP5 werden die Funktionen erst nach dem Abarbeiten des Skripts im Parser gebunden. Dadurch spielt es keine Rolle, wann sie definiert werden. »Bindung« ist ein Fachausdruck, der aussagt, wann die Adresse der übersetzten Funktion im Speicher ermittelt und zum Aufruf bereitgestellt wird.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
Richtig argumentieren: Funktionsargumente Die Übergabe von Argumenten an die Funktion (die dann dort Parametern entsprechen) kann auf vielfältige Art und Weise erfolgen. Im einfachsten Fall geben Sie Variablennamen an:
function nameausgeben($name, $ort) { echo "$name wohnt in $ort."; } Der Aufruf der Funktion kann nun erfolgen, indem Werte eingesetzt werden:
nameausgeben("Max Müller", "Berlin"); Der Rückgabewert interessiert hier nicht, also wird auch kein return benötigt. Beim Aufruf können natürlich auch Variablen eingesetzt werden. Das folgende Beispiel ist äquivalent zu dem vorherigen:
Die Variablennamen innerhalb einer Funktion müssen sich nicht von denen in anderen Funktionen oder außerhalb der Funktion unterscheiden, da sie per Definition lokal sind. Der Zugriff auf globale Variablen ist aber durch die zusätzliche Anweisung global möglich.
"; } $name = "Joerg Krause";
Listing 6.16: Verhalten lokaler und globaler Variablen beim Funktionsaufruf (function_global)
120 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen show_location("Berlin"); ?> In diesem Skript wird zuerst eine Funktion definiert. Diese erwartet einen Parameter, der in der lokalen Variablen $ort abgelegt wird.
function show_location($ort) Dann wird die globale Variable $name in der Funktion sichtbar gemacht.
global $name; Es folgt eine Ausgabe, die sowohl die globale Variable als auch den Parameter umfasst.
echo "$name wohnt in $ort. "; Der eigentliche Programmablauf beginnt mit der Zuweisung der globalen Variablen und setzt mit dem Funktionsaufruf fort:
$name = "Joerg Krause"; show_location("Berlin");
Von Geltungsbereichen und Variablen: »global« und »lokal« Variablen gelten immer in einen bestimmten Bereich. Wenn Sie das gesamte Skript betrachten, sind dort angelegte oder von PHP selbst initialisierte Variablen »global«. Innerhalb von Funktions- und Klassendefinition sind sie nicht sichtbar. Umgekehrt gelten dort angelegte Variablen als »lokal«. Sie sind wiederum nicht außerhalb des Bereiches sichtbar, wo sie definiert wurden. Die Ausgabe in der folgenden Abbildung zeigt das Verhalten der Funktionsvariablen: Abbildung 6.9: Ausgabe des Skripts aus Listing 6.16 Frontalangriff: Zugriff auf alle globalen Variablen Mit dem Schlüsselwort global teilen Sie PHP mit, dass die Variable in einem globalen Kontext bereits existiert. Der Inhalt der Variable wird dann übernommen. Umgekehrt werden sich Änderungen an der Variablen innerhalb des Blocks auch global widerspiegeln. Oft wird der Zugriff auf mehrere Werte notwendig sein. Sie können hinter global mehrere Variablen schreiben, durch Kommata getrennt:
global $x, $y, $zahl; GLOBALS[]
Reicht auch das nicht aus, machen Sie mit einem einzigen Befehl alle globale Variablen verfügbar. Intern verwaltet PHP die Variablen in einem Array. Hier ein kleines Beispiel:
Listing 6.17: Zugriff auf globale Variablen mit GLOBALS[] (function_globals)
Beachten Sie hier, dass der Schlüssel des Arrays der Name der Variablen ohne das führende $-Zeichen ist. Ansonsten arbeitet dieses Skript wie das aus Listing 6.16. Wenn Sie die konkreten Namen einer Variablen nicht kennen oder sehr viele globale verwendet werden, ist der bisherige Weg über global nicht erfolgversprechend. Naheliegend wäre folgende Variante:
global $GLOBALS; Leider funktioniert das so nicht. Sie müssen die Werte einzeln aus dem $GLOBALS-Array holen.
Listing 6.18: Alle globalen Variablen in einem Array sichtbar machen (function_glob2)
Das letzte Beispiel dürfte eine nähere Erklärung wert sein, weil es doch deutlich anspruchvoller ist, als die bisherigen Varianten. Verwendet werden die Variablennamen aus $GLOBALS, die mittels array_keys() extrahiert werden. Der Zugriff auf den Namen erfolgt mittels der dynamischen Zuweisung von Variablen mit ${$name}. Dabei ist $name nicht die Variable selbst, sondern der Name einer anderen Variablen, auf die verwiesen wird. Konstante Bedingungen: Variablen statisch machen Variablen innerhalb eines Gültigkeitsbereiches sind flüchtig – beim Ver- static lassen der Funktion werden sie gelöscht, beim erneuten Eintritt wieder angelegt. In solchen Fällen müssten Sie immer auf globale Variable zurückgreifen, was bei großen Projekten zu einem unüberschaubaren Chaos an Variablennamen führen würde. Mit dem Schlüsselwort static
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
122 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen können Sie Variablen innerhalb einer Funktion so definieren, dass sie erhalten bleiben, wie das folgende Beispiel zeigt: Listing 6.19: Statische Variablen (function_static)
"; $zahl++; } ausgabe(); ausgabe(); ausgabe(); ?> Der Operator ++ erhöht die Variable jedes Mal um eins. Die Zuweisung des Startwerts erfolgt nur beim ersten Aufruf der Funktion, die drei Aufrufe führen also tatsächlich zur Ausgabe von 22, 23 und 24.
Abbildung 6.10: Ausgabe des Skripts aus Listing 6.19 Übergabe von Werten: Per Referenz oder per Wert? Die Veränderung der übergebenen Variablen kann manchmal erwünscht sein. So könnte eine Funktion sämtliche übergebenen Werte in mit dem HTML-Tag fett ausgeben: Listing 6.20: Funktion mit referenzierten Argumenten (function_ref)
$html"; } $ausgabe = "Test"; echo $ausgabe; echo " "; machfett(&$ausgabe); echo $ausgabe; ?> Die Funktion machfett() nutzt einen referenzierte Parameter – anstatt des Wertes in $ausgabe wird ein Verweis auf diese Variable übergeben. Dass dieser Verweis innerhalb der Funktion einen anderen Namen trägt ($html), spielt keine Rolle. Tatsächlich wird nach der Ausführung der Inhalt von $ausgabe verändert sein.
Abbildung 6.11: Ausgabe des Skripts aus Listing 6.20 Der Name der Variablen spielt keine Rolle. Entscheidend ist die Kennzeichnung der Argumente mit &. In diesem Fall wird der globalen Vari-
6.4 Ordnung ins Chaos: Funktionen ______________________________________ 123 ablen der neue Wert zugewiesen. Dies ist auch ein Trick, um mehrere Werte aus einer Funktion heraus zu ändern. Bei der Rückgabe mit return sind Sie ja auf einen Wert beschränkt.
Von Referenzen und Werten Normalerweise werden die Parameterwerte, wenn sie als Variable angegeben werden, der Funktion in Kopie übermittelt. Änderungen an der Kopie wirken sich nicht auf die Quelle aus. Diese Art der Übergabe wird als »by value« bezeichnet und ist bei PHP – wie bei vielen anderen Sprachen auch – Standard. Im Gegensatz dazu wird bei der Übergabe per Referenz, »by reference«, ein Verweis auf die Quelle transportiert. Abgesehen davon, dass Änderungen sich nun auf die Quelle auswirken, ist diese Form auch etwas schneller.
Argumentationskunst: Probleme mit Argumenten Das Beispiel in Listing 6.20 mag Ihnen zu umständlich erscheinen. Die folgende Zeile ist kompakter, verständlicher und falsch:
echo machfett($ausgabe); Syntaktisch ist dies richtig, der PHP-Parser wird nicht mit einer Fehlermeldung reagieren. Das Problem ist eine falsche Parameterübergabe. Die Beispielfunktion machfett() gibt nichts zurück – sie ändert nur eine übergebene Variable. Die Anweisung echo erwartet aber Werte, die sie an den Browser senden kann. An die eingeschlossene Variable $ausgabe kommt sie nicht heran. Diese wird zwar geändert, das Ergebnis aber quasi verworfen. Wenn Sie die Anwendung dennoch in dieser Form benötigen, setzen Sie return ein:
Listing 6.21: Funktionsrückgabe mit echo ausgeben (function_echo)
Sie können generell einzelne Argumente beim Aufruf weglassen. In die- Optionale sem Fall wird die betreffende Variable nicht initialisiert. Um zusätzliche Parameter Tests zu vermeiden und Laufzeitfehler innerhalb der Funktion nicht zu provozieren, können Sie jedes Argument mit einem Standardwert belegen. Man spricht in solchen Fällen von optionalen Parametern. Die Syntax verlangt, dass der Funktionskopf um eine Zuweisung erweitert wird:
function nameausgeben($name = "Name", $ort = "Ort")
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
124 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Eleganter ist die Verwendung von Konstanten. Sie erreichen so mehr Ordnung und eine bessere Lesbarkeit Ihrer Skripte:
define("NONAME", "Kein Name"); ... function nameausgeben($name = NONAME, $ort = "Ort") Probleme mit optionalen Parametern
Anfängern bereiten optionale Parameter oft Schwierigkeiten. Der Umgang damit ist einfach, solange die Anwendung in der gezeigten Form erfolgt. Es ist jedoch auch möglich, einige Parameter mit Standardwerten zu belegen und andere nicht. Das folgende Beispiel funktioniert problemlos:
function nameausgeben($name, $ort = "Ort") Der umgekehrte Fall erscheint ebenso einfach, führt jedoch zu einem Laufzeitfehler:
function nameausgeben($name = "Name", $ort) Dieses Beispiel funktioniert nur, wenn tatsächlich beide Parameter übergeben werden. Der folgende Aufruf kann nicht funktionieren:
nameausgeben("Berlin"); Woher soll PHP wissen, dass Sie den zweiten Parameter meinen? Fazit: optionale Parameter müssen im Funktionskopf immer rechts stehen und von rechts auflösbar sein. Flexibel argumentieren: Variable Argumentelisten Manchmal müssen Sie Funktionen definieren, die mehrere Argumente haben. Es kann nun vorkommen, dass an der einen oder anderen Stelle die Anzahl der Argumente nicht bekannt ist. Das folgende Beispiel zeigt eine Funktion, die eine variable Anzahl Elemente übernimmt und formatiert zurückgibt. Wie viele Elemente es sind, spielt keine Rolle: Listing 6.22: Umgang mit variablen Argumentelisten (function_argv)
6.4 Ordnung ins Chaos: Funktionen ______________________________________ 125 Interessant ist vor allem der Umgang mit den Argumenten in der Funktion. Tatsächlich werden keine Parameter angegeben. Das ist logisch, weil diese ja nicht präzise beschrieben werden können. Diese Funktion akzeptiert hier eine beliebige Anzahl Parameter:
function html_fett() Die tatsächliche Anzahl wird dann als erstes ermittelt:
$argc = func_num_args(); Dann wird das erste Argument ausgelesen:
$tag = func_get_arg(0); Da die Anzahl der Argumente bekannt ist, kann diese Information für eine Schleife verwendet werden, die alle weitere Argumente ausliest und entsprechend behandelt.
for ($i = 1; $i < $argc; $i++) { $result .= "<$tag>".func_get_arg($i)."$tag>"; } Das Ergebnis wird zurückgegeben.
return $result; Variable Argumentlisten sind auch bei einigen internen Funktionen und Anweisungen zulässig, wie beispielsweise echo und max. Abbildung 6.12: Ausgabe des Skripts aus Listing 6.22 Die speziellen Funktionen, mit denen der Umgang mit variablen Parame- func_num_args terlisten möglich ist, noch einmal auf einen Blick: func_get_arg • func_num_args()
func_get_arg
Diese Funktion gibt die Anzahl der Parameter zurück. • func_get_arg(index) Hiermit ermitteln Sie einen konkreten Parameter, die Auswahl erfolgt durch den Index, der die Argumentliste von 0 beginnend durchzählt. • func_get_args() Diese Funktion gibt ein Array mit den Parametern zurück. Das folgende Beispiel implementiert zwei Funktionen, die beliebig viele Parameter akzeptieren. Realisiert wird eine mathematische Funktion zur Berechnung der Summe und des Durchschnitts.
Listing 6.23: Funktionen mit variablen Argumentelisten (function_argv2)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
126 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen
function avg() { $num = func_num_args(); $arr = func_get_args(); $sum = 0; for ($i = 0; $i < $num; $sum += $arr[$i++]); return $sum / $num; } printf("Summe: %0.2f%s", sum(13, 12, 7, 8, 23, 100), ' '); printf("Durchschnitt: %0.2f", avg(13, 12, 7, 8, 23, 100)); ?> Die Funktion sum nutzt die Parameterinformationen, um die Anzahl der Werte zu ermitteln. Die gesamte Berechnung findet auf dieser Basis in der for-Anweisung statt. Die Funktion avg berechnet den Durchschnittswert. Dazu wird die nach dem bei sum gezeigten Verfahren ermittelte Summe durch die Anzahl der Werte dividiert. Die Anzahl ermittelt func_num_args. Im Beispiel wurde gezeigt, dass keine Parameterangaben im Kopf der Funktionsdeklaration notwendig sind. Wenn Sie dennoch Parameter angeben, werden diesen Argumente vom Anfang der Liste zugewiesen. Unabhängig davon werden alle Werte von den Parameterfunktionen verwaltet. Das folgende Beispiel gibt »14« zurück: Listing 6.24: Zusätzliche Auswertung des ersten Parameters (function_argv3)
$first übernimmt hier den ersten Wert: 1. Die gesamte Argumentliste, die func_get_arc zurückgibt, steht dort als Array. Der erste Index ist 0, entsprechend zeigt der Index 3 auf den Wert 4 im Beispiel. Antwortverhalten: Rückgabe mehrerer Werte Die Rückgabe mehrerer Werte ist nicht vorgesehen. Da es aber sonst keine Beschränkung des Typs gibt – sowohl Arrays als auch Objekte sind erlaubt –, bietet sich ein einfacher Trick zur Übergabe mehrerer Werte an. Eine Variante kennen Sie bereits: die Übergabe von Variablen als Referenz mit vorangestelltem &-Zeichen. Daneben können Sie auch als Rückgabewert ein Array einsetzen:
Listing 6.25: Rückgabe eines Arrays (function_array)
Die Auflösung wird mit list vorgenommen. Oft ist diese Schreibweise offensichtlicher und leichter zu verstehen als der Umgang mit Referenzen. list liest so viele Werte aus einem Array aus, wie Variablen als Parameter angegeben wurden. Dies ist im übrigen eine eingebaute Funktion, die variable Argumentlisten versteht. Abbildung 6.13: Ausgabe des Skripts aus Listing 6.25 Selbstgespräche führen: Einführung in die Rekursion PHP erlaubt rekursive Funktionsaufrufe. Dabei ruft eine Funktion sich selbst auf. Das ist eine recht häufig verwendete Programmiertechnik. In der Mathematik gibt es ganze Reihe von Definitionen, bei denen sich die Funktion durch einen Verweis auf sich selbst erklärt wird. Am bekanntesten ist die Berechnung der Fakultät:
n! = n·(n-1)! Hinzu kommt eine Abbruchbedingung, die auf dem Sonderfall 1! = 1 basiert. Als Lösung kann nun eine Funktion dienen, die folgenden Aufbau hat:
Diese Funktion besteht aus zwei Zeilen: Zuerst wird der Sonderfall und damit die Abbruchbedingung behandelt, danach die Berechnung gemäß der Vorschrift. Am Anfang mag dieses Verfahren schwer vorstellbar sein. Vor allem das »Ende« ist nicht sofort erkennbar. Abbildung 6.14 zeigt den Weg, den das Programm nimmt. Wenn die Funktion sich selbst aufruft, startet sie wie jeder andere Funktionsaufruf auch. Ist die untergeordnete Funktion beendet, kehrt sie an die aufrufende Stelle zurück, sodass beim Auflösen der Kette praktisch nur noch Funktionsenden bearbeitet werden. Das
128 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen heißt: Bei dem gezeigten Wert 4 wird die Funktion nacheinander viermal aufgerufen und dann viermal verlassen. Abbildung 6.14: Prinzip der Rekursion anhand der Berechnung der Fakultät
Wozu man das braucht
Vermutlich kommen Sie selten in die Verlegenheit, die Fakultät berechnen zu müssen. Rekursive Funktionen sind dennoch ein fester Bestandteil der Programmierwelt und sehr oft anzutreffen. Sie werden beispielsweise benötigt, um verschachtelte Strukturen auszugeben. Dabei kann es sich um Verzeichnisse oder Menüführungen handeln. Eine Eigenschaft dieser Strukturen ist die unbekannte Tiefe, die sie annehmen können. Rekursive Funktionen sind – theoretisch – per Definition auf unbekannte Tiefe ausgelegt. In der Praxis sieht das anders aus.
Alles hat seine Grenze Jeder Aufruf einer rekursiven Funktion aus sich selbst heraus legt die benötigten Aufrufparameter in einem speziellen internen Speicher ab – dem Stapelspeicher (engl. stack). Dieser hat eine begrenzte Kapazität. Ist er erschöpft, bricht PHP mit dem Fehler »PHP has encountered a Stack overflow« ab. Experimentell konnte ich eine Grenze von etwa 220 rekursiven Aufrufen feststellen. Das mag aber von System zu System verschieden sein.
6.4 Ordnung ins Chaos: Funktionen ______________________________________ 129 Als praktischere Anwendung sei hier ein anderes Beispiel angeführt – die Ausgabe eines Verzeichnisbaumes. Auch wenn die nötigen Funktionen erst später behandelt werden, sollte das Prinzip dennoch klar werden.
<pre> read()) { if (!ereg('^[.]{1,2}', $entry)) { if (is_dir("$path/$entry")) { printf ("% ".$depth."s /%s\n", "+-", $entry); showdir($path.'/'.$entry, $depth + 3); } else { printf ("% ".$depth."s %s\n", "|-", $entry); } } } $folder->close(); } $start = "/inetpub/wwwroot"; showdir($start, 0); ?> Die Funktion showdir, die die gesamte Arbeit erledigt, ist sicher eine genaue Betrachtung wert. Zuerst wird mit der Funktion dir ein Verzeichnisobjekt erzeugt, am Anfang mit dem Pfad, der mit dem ersten Aufruf mitgegeben wird.
$folder = dir($path); Objekte dieser Art bringen verschiedene Methoden mit – das sind Funktionen, die auf Daten des Objekts angewendet werden. Im Beispiel werden nacheinander Verzeichniseinträge mit read() gelesen.
while ($entry = $folder->read()) Dann werden die beiden Verzeichnisse ».« und »..« ermittelt. Diese sollen nicht in die Anzeige mit einbezogen werden.
if (!ereg('^[.]{1,2}', $entry)) Für alle anderen Elemente, also Verzeichnisnamen und Dateien, wird eine Einrückung berechnet. Diese basiert auf der Variablen $depth, die mit jeder Ebene erhöht wird.
printf ("% ".$depth."s /%s\n", "+-", $entry);
Listing 6.27: Ausgabe von Verzeichnisbäumen (recursion_dir)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
130 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Handelt es sich bei dem Eintrag um ein Verzeichnis, wird die Ebene erhöht und die Funktion ruft sich selbst auf, um das Verzeichnis auszugeben. Findet sie dort weitere Verzeichnisse, wird der Prozess fortgesetzt.
if (is_dir("$path/$entry")) { showdir($path.'/'.$entry, $depth + 3); Falls es keine Verzeichnisse mehr gibt, wird der aktuelle Eintrag als Dateiname ausgeben. Dies ist zugleich auch die Abbruchbedingung für die Rekursion.
} else { printf ("% ".$depth."s %s\n", "|-", $entry); Zuletzt werden alle Schleifen geschlossen und das Verzeichnisobjekt vernichtet.
$folder->close(); Möglicherweise werden Sie erwarten, dass Verzeichnisse mit mehr als 200 Einträgen nicht funktionieren. Das ist aber nicht das Problem. Während der Abarbeitung wird nur dann ein rekursiver Aufruf erfolgen, wenn ein Unterverzeichnis entdeckt wurde – die Tiefe der Verzeichnisstruktur ist hier der kritische Wert. Allerdings dürften 200 Verzeichnisebenen in der Praxis mehr als ausreichend sein. Vorsicht Falle!
Ein ganz anderes Problem werden Sie bemerken, wenn Sie eine sehr tiefe Struktur analysieren oder versehntlich im Rootverzeichnis des Computers starten. Bei einigen 10 000 Verzeichnissen – das ist durchaus normal – dauert die Analyse spürbar. Auch schnelle Computer können damit einige Minuten beschäftigt sein. Fatal ist das ganze auf einem Webserver, wenn viele Nutzer parallel zugreifen. Spürbare Prozessorbelastung wirkt sich dann auf alle aus. Ist das System so eingerichtet, dass jedem Prozess nur eine begrenzte Prozessorleistung zur Verfügung steht, kann sich die Analyse ewig hinziehen. Insofern sind rekursive Lösungen generell mit Vorsicht zu genießen. Machen Sie sich ernsthaft darüber Gedanken, wie tief die Analyse einer Struktur erfolgt und wie schnell die Funktion den Stapel wieder auflöst. Vermeiden Sie unter allen Umständen zeitraubende Berechnungen in der unteren Ebene, wo der Aufruf sehr oft erfolgt.
6.5 Fehler einfangen: try@catch _________________________________________ 131 Abbildung 6.15: Ausgabe des Skripts aus Listing 6.27
6.5
Fehler einfangen: try@catch
In den bisherigen PHP-Versionen war keine wirklich brauchbare Fehlerbehandlung zu finden. Mit PHP5 fanden nicht nur zwei neue Schlüsselwörter zu Flusskontrolle im Fehlerfall Eingang, sondern gleich ganz neue Klassen zum Erkennen von spezifischen Fehlerzuständen. Die Integration gelang leider nicht vollständig, sodass interne Laufzeitfehler nach wie vor mit dem Fehleroperator @ abgefangen werden müssen.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
132 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen
6.5.1
Bei Fehler: Abbiegen
In diesem Abschnitt werden die beiden Anweisungen try und catch kurz vorgestellt. Ein genauerer Ausblick auf die Verwendung setzt ein Verständnis für objektorientierte Programmierung voraus. Im Zusammenhang mit der entsprechenden Einführung finden Sie eine Fortsetzung des Themas im Abschnitt 7.5 Eingebaut: Die Fehlerklassen ab Seite 155. Das Prinzip von try und catch ist einfach. Ein Programmzweig, indem
Fehler auftreten könnten, wird mit einem try-Block umschlossen:
try { // Hier können Fehler passieren } Tritt ein Fehler zur Laufzeit in diesem Bereich auf, erzeugt PHP5 keine Fehlerausschrift, sondern sucht zuerst das nächste catch. Wird ein solches Schlüsselwort gefunden, werden die darin befindlichen Anweisungen ausgeführt, vorzugsweise schreibt man dort die Fehlerbehandlung hinein. Es ist möglich, mehrere catch-Zweige hintereinander zu platzieren, um auf verschiedene Arten von Fehler zu reagieren. Abschnitt 7.5 Eingebaut: Die Fehlerklassen ab Seite 155 zeigt dies im Detail. Als Auslöser dient das Schlüsselwort throw, dass eine so genannte Fehlerklasse instantiiert und an den passenden catch-Zweig schickt. Fehlt da nicht was? Umsteiger aus anderen Sprache kennen try/catch vermutlich. Allerdings verhält sich PHP hier grundsätzlich anders. Zum einen ist der Programmierer selbst dafür verantwortlich, alle erdenklichen Fehler zu beachten und passende Abfragen zu erstellen. Echte Laufzeitfehler, wie eine Division durch Null, lassen sich nach wie vor nicht abfangen. Insofern ist das try/catch-Konzept in PHP5 sicher ein Fortschritt gegenüber den Vorgängerversionen, aber bei weitem nicht so komfortabel wie es Java- oder C++-Entwickler gewohnt sind. Technisch liegt das daran, dass PHP5 zwar ein paar objektorientierte Funktionen verpasst bekam, aber im Kern nicht damit arbeitet, Laufzeitfehler also nicht Klassen entstammen, die auf catch wirken könnten, sondern simple Textbotschaften erzeugen. Schade eigentlich! Konsequenterweise fehlt dann auch das vierte Schlüsselwort im Bunde: finally. Normalerweise fügt sich ein finally-Zweig an den letzten catchBlock an. Er enthält Anweisungen, die unbedingt ausgeführt werden müssen, unabhängig davon, ob ein Abbruch des try-Abschnitts erfolgte oder nicht. Dies passiert auch dann, wenn eine Funktion in catch oder try mit return verlassen wurde. Fehlerbehandlung ist kein Programmiermittel! Die Fehlerbehandlung mit try/catch ist kein Mittel, um den Programmfluss zu steuern. Es sollten tatsächlich nur dann Fehlerroutinen aufgebaut
6.5 Fehler einfangen: try@catch _________________________________________ 133 werden, wenn ungewöhnliche Abläufe zu behandlen sind. Wenn Sie auf Dateien prüfen, verwenden Sie zuerst eine Funktion wie file_exists, um festzustellen, ob eine Datei vorhanden ist. Erst wenn dies angezeigt wird und der Ladevorgang dann misslingt, sollte die Fehlerverwaltung greifen. Alle »Fehler«, die im normalen Betrieb auftreten können, sind deshalb durch normale Programmbefehle zu behandeln. Leider ist das try/catch-Modell von PHP5 dafür nicht ideal ausgebildet.
6.5.2
Einfache Fehlerbehandlungen
Die Fehlerbehandlung mit try/catch erlaubt einfacheren und besser lesbareren Quellcode. Statt die vielfältigen Fehlerausgaben innerhalb eines Skripts weit zu verstreuen, kann man sich auf wenige Punkte beschränken, was leichter zu pflegen ist. Insofern spricht einiges für die Anwendung, trotz der nach Meinung des Autors insgesamt misslungenen Umsetzung. Ein einfaches Beispiel soll die grundsätzliche Arbeitsweise erläutern:
$a = 45; $b = $a - 45; $c; try { if ($b == 0) { throw new Exception('Division durch Null'); } $c = $a / $b; } catch(Exception $exception) { echo "Ein Fehler trat auf: $exception"; } Die Klasse Exception bietet unter Umständen detaillierte Informationen. Abschnitt 7.5 Eingebaut: Die Eine nähere Behandlung erfolgt unter Fehlerklassen ab Seite 155 zeigt dies im Detail. Die Ausgabe dieses Skripts zeigt, wie die Fehlerbehandlung wirkt:
Ohne entsprechende Fehlerbehandlung erscheint ein Laufzeitfehler:
6.5.3
Fehlerunterdrückung mit @
Jeder Funktion, die PHP5 intern oder als Teil eines Moduls bietet, kann der Fehleroperator @direkt vorangestellt werden:
134 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Geht bei der Ausführung etwas schief, wird kein Fehler angezeigt. Sie sollten aber unbedingt dafür sorgen, dass auf mögliche Fehlerzustände im Code reagiert wird. Die meisten Funktionen geben bestimmte Werte zurück, beispielsweise das Ergebnis einer Berechnung, eine Referenz auf Daten usw. Im Fehlerfall wird meist FALSE zurückgegeben. Ein entsprechender Test sollte sich deshalb immer anschließen:
if ($fh === FALSE) echo “Es trat ein Fehler auf“; Es ist auch möglich, Fehler beim Zugriff auf Array-Elemente zu unterdrücken.
$result = @$array[$key]; Beachten Sie hier, dass das Ergebnis nicht FALSE, sondern NULL ist. Sie müssen eine explizite Prüfung deshalb mit isset vornehmen. Zum Unterdrücken von Fehlern bei Methodenaufrufen nutzen Sie folgende Syntax:
@$this->foo($bar); Das Voranstellen beim Methodennamen ist dagegen falsch:
$this->@foo($bar); Eine einfache Fehlerverzweigung Die können eine Fähigkeit von PHP5 nutzen, um auf Fehler sehr einfach zu reagieren. Wenn ein Boolescher Ausdruck ausgewertet wird, erkennt PHP5 mehrere Operanden. Bei einer OR-Verknüpfung kann der Ausdruck nur TRUE sein, wenn der erste Operand bereits TRUE ist. Es wäre dann nicht erforderlich, den Ausdruck weiter auszuwerten – am Ergebnis kann sich nichts mehr ändern. Diese Optimierung kann man ausnutzen:
$fh = @fopen(“datei“, “r“) or die(“Fehler“); In diesem Ausdruck wird die Funktion fopen ausgeführt. Normalerweise gibt diese eine Referenz auf eine Datei zurück, was PHP5 in einem Booleschen Ausdruck als TRUE wertet. Damit wird die weitere Verarbeitung der Zeile abgebrochen und der Befehl die niemals erreicht. Anders geht es, wenn ein Fehler auftritt. Nun ist der erste Teil FALSE und PHP wertet den zweiten Teil aus. die
Der Befehl die beendet das Programm mit einer selbst definierten Meldung. Den ursprünglichen Fehler erkennen Um den ursprünglich erzeugten Fehler zu sehen, können Sie die Variable $php_errormsg abfragen. Diese Variable wird allerdings standardmäßig nicht gesetzt. Sie können die entsprechende Konfiguration jedoch leicht per Skript erledigen:
Listing 6.29: Fehler erkennen und abfangen (errorcheck)
echo "Datei nicht gefunden: "; echo $php_errormsg;
Dieses Skript setzt den Parameter track_errors aus der Datei php.ini. Alternativ zum Aufruf per Skript wäre es also auch möglich, dies in der Konfigurationsdatei zu erledigen. Abschnitt 14.2 Konfiguration mit »php.ini« ab Seite 350 zeigt die entsprechenden Details. Fehlerreaktionen steuern Darüber hinaus werden interne Fehler erkannt und in verschiedene Feh- error_reporting lerklassen eingeteilt. Mit der Funktion error_reporting können Sie im Skript steuern, welche Fehler angezeigt und welche unterdrückt werden. Die folgende Tabelle zeigt alle Bitwerte und die Namen der Fehlerklassen: Bitwert
Fehlername
Beschreibung
1
E_ERROR
Fehler im Programmablauf
2
E_WARNING
Warnungen
4
E_PARSE
Fehler in der Syntax
8
E_NOTICE
Nachrichten
16
E_CORE_ERROR
Fehler des PHP-Interpreters
32
E_CORE_WARNING
Warnung des PHP-Interpreters
64
E_COMPILE_ERROR
Zend-Compiler-Fehler
128
E_COMPILE_WARNING
Zend-Compiler-Warnung
256
E_USER_ERROR
512
E_USER_WARNING
1024
E_USER_NOTICE
Diese Konstanten steuern das Verhalten der nutzerdefinierten Fehlerbehandlung mit der Funktion user_error
E_ALL
Alle Fehler
Tabelle 6.2: Konstanten der Fehlerklassen
Die Fehlernamen sind in PHP als Konstanten vordefiniert. Setzen Sie diese einfach als Parameter ein. Mehrere Werte können Sie mit dem Operator | (ODER) verbinden:
Mit Hilfe von E_ALL und der Negation ^ können gezielt bestimmte Warnungen ausgeschaltet werden. Eine eigene Fehlerbehandlung erstellen Als letzte Maßnahme zu einer ordentlichen Fehlerverarbeitung kann man eine Funktion definieren, die immer dann aufgerufen wird, wenn ein
Listing 6.30: Fehler und Warnungen unterdrücken
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
136 ______________________________ 6 Das Steuerrad für PHP: Kontrollstrukturen Laufzeitfehler auftritt. Es wäre zwar nahliegend, dafür throw zu verwenden, aber das funktioniert nur mit eigenen Klassen, nicht mit Laufzeitfehlern, die PHP5 selbst definiert. Will man – zwecks konsistenter Programmierung – nun den eigenen Fehlerbehandler nutzen, muss man eigene Fehler mit trigger_error auslösen. Das es keine Zusammenspiel mit try/catch gibt, ist mehr als nur ärgerlich. Es ist die Meinung des Autors, dass sich die PHP-Entwickler hier krass disqualifiziert haben. Die Definition einer eigenen Fehlerbehandlungsmethode erfolgt mit set_error_handler: Listing 6.31: Eigene Fehlerbehandlung (errorhandler)
function myhandler($errno, $errstr, $errfile, $errline) { switch ($errno) { case E_USER_ERROR: echo "Fehler: $errstr trat auf in $errfile auf Zeile $errline"; break; } } ini_set('track_errors', 1); set_error_handler('myhandler'); $fh = @fopen("unbekannte_datei", "r"); if ($fh === FALSE) { echo "Datei nicht gefunden: "; trigger_error($php_errormsg, E_USER_ERROR); } Die Funktion trigger_error löst den Fehler aus und übergibt eine Fehlermeldung nebst Fehlernummer. Der Handler erhält zusätzlich als dritten und vierten Parameter Dateinamen und Zeilennummer aus. Kombinationen aus trigger_error und throw
Eine Idee für Fortgeschrittene
Wer die Entwicklung von Bibliotheken tion der Fehlerbehandlung aus Fehlerbehandler und throw erstellen. Fehler vom Nutzer der Klasse mit können.
plant, könnte sich eine Kombinatrigger_error und eigenem Dabei würde dann der interne try/catch abgefangen werden
7 Sammeltransporte: Objekte In der Welt der Programmiersprachen haben sich zunehmend objektorientierte Modelle etabliert. PHP ist strenggenommen keine objektorientierte Sprache, wie C++, C# oder Java, sondern eine prozedurale (imperative) Sprache. Sie verfügt aber dennoch über einige objektorientierte Ansätze. In professionellen Skripten werden Sie immer wieder damit konfrontiert und oft sind mit diesen Techniken einfache und clevere Lösungen möglich.
7
Sammeltransporte: Objekte
7.1 Einführung in die Welt der Objekte ___________________________________ 139
7.1
Einführung in die Welt der Objekte
Objektorientiert programmieren setzt voraus, das die dahinter liegenden Was es mit Paradigmen verstanden wurden. Einführungen in diese Welt leiden Objekten auf sich meist unter einer Flut neuer Begriffe, die sich gegenseitig erklären – we- hat nig hilfreich, wenn ein Ansatzpunkt fehlt.
7.1.1
Das Konzept
Ein Blick in die reale Welt hilft vielleicht, das Konzept objektorientierter Programmierung besser aufzunehmen. In der realen Welt ist alles ein Objekt. Jedes Ding besteht aus einem Zustand, beschrieben durch Eigenschaften und zulässigen Verhaltensregeln. Dabei können Gegenstände sehr simpel sein, komplexere Objekte wie Tiere oder Menschen dagegen kaum noch erfassbar. Klassen als Abbild von realen Objektdefinitionen In der Programmierung werden solche zusammen agierenden Gebilde aus Eigenschaften und Ablaufregeln als Objekte bezeichnet. In klassischen Programmen speichern Sie Daten in Variablen und geben dann durch Programmanweisungen und Funktionen Regeln vor, nach denen das Programm abläuft. Interaktion mit Schnittstellen schafft dann die Vielfalt der Abläufe. Objektorientierte Programme bestehen aus Sammlungen von Objekten. Die Idee dahinter ist die Zusammenfassung gleichartiger oder ähnlicher Was sind Strukturen und zusammengehörender Elemente. Solche Gebilde werden Klassen? als Klassen bezeichnet. Es werden also nicht immer wieder Objekte definiert, wenn man sie benötigt, sondern nur ein Mal – als Klasse. Wenn der Programmierer dann ein Objekt benötigt, beispielsweise um eine Programmfunktion auszulösen, erzeugt er aus der Klasse ein Objekt ab und verwendet dies. Dieser Vorgang wird auch als »instanziieren« bezeichnet. Das Objekt ist eine »Instanz« der Klasse. Aus einer Klasse kann man beliebig viele Objekte erzeugen. Diese sind voneinander völlig getrennt und werden für sich wiederum in Variablen gespeichert. Umgang mit Klassenhierarchien In komplexeren Applikationen, wo die objektorientierten Modelle zur Höchstform auflaufen, schreibt man einfach Klassen und baut auf diesen immer komplexere auf. Oder man schreibt umfangreiche Klassen und stellt diese anderen Entwicklern als Bibliothek zur Verfügung. In jedem Fall gibt es Mitglieder (Variablen, Funktionen) die man intern benötigt und solche, die man dem verwendenden Programm bereitstellen will. Für diese bietet PHP5 entsprechende Schlüsselwörter wie private und protected an, die später noch genauer untersucht werden.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
140 __________________________________________ 7 Sammeltransporte: Objekte Eine andere Form der Kontrolle besteht in der Definition, wer von einer Klasse ableiten kann oder sogar muss (abstract) bzw. wo dies explizit untersagt ist (final). Schnittstellen zur Kommunikation Programme, die aus vielen Klassen bestehen, benötigen öffentliche Mitglieder, um Kommunikationswege zwischen den Objekten zu schaffen. Nach außen, zum späteren Nutzer beispielsweise einer Bibliothek, sind diese Darstellungen meist weniger gedacht. Um nun einen Teil eines Objekts zu definieren, der in der Verwendung völlig frei ist, dienen Schnittstellendefinitione (interface). Diese sehen aus wie Klassen, enthalten aber keinen Code, sondern nur die Definitionen der Methoden, also die Köpfe. Auch dies wird noch kurz betrachtet.
7.1.2 class
Listing 7.1: Eine Klasse definieren (class_basic)
Das erste Objekt
An dieser Stelle können Sie schon das erste Objekt ausprobieren. Dies beginnt natürlich mit der Definition einer Klasse. In PHP verwenden Sie dazu die Anweisung class.
7.1 Einführung in die Welt der Objekte ___________________________________ 141 $de->weekday(); ?> Das Skript zeigt sowohl die Klassendefinition als auch die Instanziierung der Objekte. Die Klasse printdate kennt zwei Eigenschaften und zwei Methoden. Sie soll Wochentage und Monatsnamen in verschiedenen Sprachen ausgeben. Die beiden öffentlichen Eigenschaften sind: • $language Eine Codierung, welche Sprache verwendet wird. • $datetime Das Datum, zu dem Wochentag oder Monat ausgegeben wird. Die öffentlichen Methoden sind die Handlungsanleitungen: • weekday() Hiermit erfolgt eine Ausgabe des Wochentags • month() Diese Methode gibt den Monatsnamen aus. Das Objekt besteht also aus zwei Eigenschaften und zwei Methoden. Die Das Gerüst einer Klasse wird in folgendem Mantel erstellt: Klasse
class printdate { } Innerhalb der geschweiften Klammern werden die Eigenschaften als Eigenschaften Variablendefiniert:
$language; $datetime; Methoden werden dagegen wie Funktionen erzeugt:
function weekday() { } function month() { } Parameter und Rückgabewerte können Sie ebenso verwenden, wie Sie dies von normalen Funktionen gewohnt sind. Zugriffsmodifikatoren Im Beispiel wurde bereits der Modifikator public verwendet, um anzuzeigen, dass der Zugriff ohne weiteres aus jedem Teil des Skripts möglich ist. Die Angabe kann auch entfallen. Aus Kompatibilitätsgründen mit PHP4 – wo es die Zugriffsmodifikatoren noch nicht gab – nimmt PHP5 public als Standardwert an. Soll ein Mitglied einer Klasse nur intern verwendet werden, beispielsweise als Hilfsvariable, dann wird es mit private gekennzeichnet. Ein Zugriff vom Objekt aus führt dann zu einem Laufzeitfehler.
Methoden
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
142 __________________________________________ 7 Sammeltransporte: Objekte Klassen können auch vererbt werden, dazu folgt später noch eine Einführung. Soll der Zugriff nur von der eigenen und der vererbten Klasse aus möglich sein, nicht jedoch vom Objekt aus, nutzt man das Schlüsselwort protected. Umgang mit den Definitionen in der Klasse: $this $this
Der Zugriff auf Eigenschaften und Methoden innerhalb der Klasse verwendet die spezielle Variable $this. Wenn Sie sich »virtuell« innerhalb der Klassendefinition befinden, existiert ja eigentlich noch kein Objekt. $this nimmt diese Instanziierung quasi voraus und verweist auf die Klasse selbst. Praktisch läuft dieser Vorgang natürlich erst dann ab, wenn ein Objekt erzeugt wurde. Wenn viele Objekte existieren, gibt es auch ebenso viele $this-Instanzen. Zur Trennung von Objekt und Eigenschaft oder Methode wird der Operator -> eingesetzt. Dies gilt für $this und jede andere Objektvariable. Ob es sich um eine Eigenschaft oder Methode handelt, erkennt PHP alleine anhand der runden Klammern. Achten Sie darauf, bei Methoden die runden Klammern nicht zu vergessen – auch wenn keine Parameter folgen. Wenn Sie diese weglassen, erkennt PHP eine Eigenschaft. Wie bei Variablen üblich, werden nicht vorhandene Variablen beim ersten Aufruf angelegt und bleiben leer, wenn sie nicht verwendet werden. Eine Fehlermeldung wird hier nicht erzeugt. Sie sollten dennoch explizit Startwerte vergeben, um immer mit einem definierten Zustand beginnen zu können. Das erste Beispiel könnte dann folgendermaßen aussehen:
public $language = ’de’; public $datetime; Allerdings muss der Wert, der hier zugewiesen wird, eine Konstante (oder ein Literal) sein. Funktionen wie time sind nicht möglich, weil die Auswertung bei der Übersetzung des Skripts erfolgt, nicht bei der Abarbeitung. Im Beispiel erfolgt der Zugriff auf die Eigenschaften innerhalb der Klasse mit Hilfe von $this.
$old = setlocale(LC_TIME, $this->language); echo strftime('%A', $this->datetime); setlocale(LC_TIME, $old); Das Objekt erzeugen Von der Klasse zum Objekt
Die bis hierher definierte Klasse ist noch völlig funktionslos. Sie nimmt nicht einmal Speicher in Anspruch – außer dem Platz für den Quelltext. Erst durch Instanziierung zur Laufzeit wird sie aktiv.
new
Dafür ist eine weitere Anweisung zuständig: new. Als Parameter wird der Name einer Klasse angegeben, als Ergebnis entsteht ein Objekt. Im Beispiel werden zwei davon erzeugt:
$dd = new printdate; $de = new printdate;
7.2 Erweiterte Techniken für Objekte _____________________________________ 143 Die Objekte – mit den Eigenschaften $language und $datetime und den Methoden weekday() und month() – $dd und $de sind völlig unabhängig voneinander. Erst jetzt wird auch tatsächlich Speicher in Anspruch genommen. Der Zugriff auf die Eigenschaften erfolgt wie bei Variablen. Anstatt der internen Klassenvariablen $this – die hier nicht mehr existiert – muss nun natürlich die Objektvariable verwendet werden.
$dd->language = 'de'; $de->language = 'fr'; $dd->datetime = $de->datetime = time(); Beachten Sie die Position des $-Zeichens – es wiederholt sich nicht hinter dem ->-Operator. Methoden werden ebenso wie Funktionen verwendet, nur steht auch hier wieder die Objektvariable davor. Damit ist klar, in welchem Objekt die entsprechende Aktion ausgelöst wird:
$dd->weekday(); echo ''; $de->weekday(); Denken Sie an die runden Klammern, damit PHP den Aufruf als den einer Methode erkennt. Abbildung 7.1: Datumsoperation mit Klassen (class_basic)
7.2
Erweiterte Techniken für Objekte
Mit der gezeigten Form der Definition einer Klasse und ihrer Eigenschaf- Zugriff auf ten und Methoden können Sie schon viel erreichen. Wenn Sie solche Klassen und Klassen häufig verwenden, müssen Sie sich über die Benennung der Vererbung Eigenschaften und Methoden weniger Gedanken machen. Die Klasse bildet einen abgeschlossenen Namensraum – Konflikte mit Definitionen gleichnamiger Funktionen im imperativen Teil des Skripts oder anderen Klassen können nicht auftreten.
7.2.1
Direkter Zugriff auf Klassen
Manchmal lohnt es sich, auch dann eine Klasse zu definieren, wenn die :: Existenz eines Objekts gar nicht notwendig wäre. Man spricht dann von so genannten statischen Mitgliedern. Die Kennzeichnung erfolgt mit dem Schlüsselwort static. Der Zugriff erfolgt in PHP5 mit dem Operator »::«.
'; echo calc::in2cm(4.5); ?> Das Beispiel zeigt eine Umrechnung von Zentimeter in Zoll (in steht für inches) und umgekehrt. Eine Zuweisung zu Eigenschaften und ein Erhalten eines Objektes allein deswegen wäre zu umständlich. Die direkte Verwendung bringt hier Vorteile. Vor allem müssen Sie sich keine Gedanken darüber machen, ob irgendwo anders eine Funktion cm2in() oder in2cm() existiert. Der Aufruf nennt sowohl die Klasse als auch die Funktion:
echo calc::cm2in(3); Nicht alles ist so machbar
Manche Probleme wären ohne diese Methode nicht lösbar. Wenn Sie zwei Klassendefinitionen haben, können Sie nicht auf Methoden oder Eigenschaften der anderen Definition zugreifen: new ist hier nicht zulässig und $this verweist immer nur auf die eigene Klasse. Mit dem ::Operator können Sie jedoch auch innerhalb einer Klassendefinition den Namen einer anderen Klasse verwenden.
Klassen verfügen über Konstruktoren und Destruktoren. Wird eine Klasse mit new erzeugt, befindet sich dass erzeugte Objekt erst mal in einem undefinierten Zustand, zumindest solange, bis den Eigenschaften Werte zugewiesen und dazu eventuell nötige Methoden aufgerufen wurden.
7.2 Erweiterte Techniken für Objekte _____________________________________ 145 Um diesen Weg zu verkürzen kann man einen so genannten Konstruktor definieren. Dies ist eine normale Methode mit dem vordefinierten Namen __construct2. Darin erfolgen alle Zuweisungen, die in irgendeiner Form erforderlich sind, um einen definierten Anfangszustand zu erreichen. Das folgende Beispiel nutzt einen Konstruktor:
class User { private $name; private $login; private $password; private $status; const USER = 'USER'; function __construct() { $this->status = USER; } public function SetName($n, $l, $p) { $this->name = $n; $this->login = $l; $this->password = $p; } public function GetName() { return $this->login; } public function GetPassword() { return $this->password; } public function GetStatus() { return $this->status; }
} $ma = new User(); echo "Mitarbeiter hat Status: " . $ma->GetStatus(); In diesem Fall wird der Status fest zugewiesen. Da die Eigenschaft $status privat ist, kann man den Wert später nicht wieder ändern. Im Gegensatz zum Konstruktor gibt es auch einen Destruktor. Dieser wird aufgerufen, wenn das Objekt zerstört wird. Das passiert entweder,
2
Achtung! Da sind zwei Unterstriche davor.
Listing 7.3: Klasse mit Konstruktor (class_construct)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
146 __________________________________________ 7 Sammeltransporte: Objekte wenn es gezielt mit unset vernichtet wird, oder am Ende des Skripts. Der Einsatz erfolgt, wenn bestimmte Vorgänge unbegingt ausgeführt werden müssen, unabhängig vom Verlauf oder Abbruch des Codes. Dies betrifft beispielsweise das Schließen von Datenbankverbindungen oder geöffneten Dateien.
7.2.3 Eigenschaften weitergeben
Vererbung und Schnittstellen
Wenn Sie über objektorientierte Programmierung lesen, werden Sie sehr bald auf das Wort Vererbung stoßen. Bislang spielte es hier keine Rolle. Sie müssen Vererbung nicht verwenden. Erst bei größeren Projekten wird es sinnvoll sein. Was aber hat es damit auf sich? Wenn Sie eine Klasse definiert haben, kommt vielleicht der Zeitpunkt, wo eine Erweiterung notwendig wird. Vielleicht fallen Ihnen aber auch mehrere Variationen dieser Klasse ein. Anstatt nun mehrere Klassen zu definieren, ist es besser, eine Basisklasse zu entwerfen und daraus Subklassen abzuleiten, die nur die geänderten Eigenschaften und Methoden enthalten. Damit die Subklassen überhaupt eine Funktionalität haben, müssen Sie von einer Mutterklasse dies erben. Dieser Vorgang wird Vererbung genannt.
PHP – überall Klassen? Wenn Sie sich professionelle Skripte aus dem Web anschauen, kommen dort fast ausschließlich Klassen zum Einsatz. Heißt das nun, dass wer professionell programmieren will, muss Klassen verwenden? Der Grund ist ein anderer: Klassen bieten Softwareentwicklern handsfeste Vorteile bei der Nutzung fremden Codes und der Verteilung des eigenen an Fremde. Durch die Kapselung der Variablennamen kommt es nicht mehr Konflikten mit dem globalen Adressraum der Zielapplikationen. Dabei spielt es oft keine Rolle, ob objektorientierte Ansätze darüber hinaus noch genutzt werden. In PHP wird die Vererbung mit der Anweisung extends gesteuert. Dabei definieren Sie eine Klasse und geben mit extends an, von welcher Klasse zusätzliche Eigenschaften und Methode geerbt werden sollen. extends
Aus Ketten solcher Vererbungen entsteht eine Hierarchie. Dies ist auch mit PHP möglich. Ebenso können Methoden überschrieben werden. Tritt in einer Klasse, die bereits Methoden geerbt hat, eine Methode gleichen Namens auf, überschreibt diese die ursprüngliche Methode. Vererbung mit extends Die Vererbung auf einfachster Ebene ist leicht zu verstehen. Stellen Sie sich nochmals die Klasse User aus dem letzten Abschnitt vor. Um andere Arten von Benutzern anzulegen, wäre eine neuen Definition wenig sinnvoll. Einfacher geht es folgendermaßen:
7.2 Erweiterte Techniken für Objekte _____________________________________ 147 class Admin extends User { } Die neue Klasse Admin kennt nun erstmal genau die Eigenschaften und Methode wie die Klasse User. Allerdings muss sie dafür sorgen, dass Eigenschaften, die mit der Kennzeichnung protected freigegeben wurden, erneut zu deklarieren sind. Das nächste Beispiel verwendet dieselbe Klasse User, jedoch mit einer veränderten Zeile für die Definition der Eigenschaft $status:
protected $status; In der abgeleiteten Klasse wird dann die Referenz auf diese Eigenschafte wiederholt und der veränderte Konstruktor überschreibt den vorhergehenden:
class Admin extends User { const ADMIN = 'ADMIN'; protected $status;
}
function __construct() { $this->status = ADMIN; }
Braucht man mehrere ähnliche Klassen, sind solche Ableitungen ein effizientes Arbeitsmittel. Änderungen an der Basisklasse wirken sich zwangsläufig auf alle Ableitungen aus, was die Pflege vereinfacht. Bevor Sie jedoch gleich superumfangreiche Hierarchien entwerfen seien Sie gewarnt: Dies ist eine Kunst, die man erst nach langer Erfahrung im Umgang mit objektorientierter Programmierung beherrschen kann. Schnittstellen Schnittstellen sind im Rahmen eines Einsteigerbuches kaum relevant. Sie sollen dennoch erwähnt werden. Zum einen, weil sie neu in PHP5 sind, zum anderen, weil es zeigt, dass es weit mehr gibt, als hier kurz behandelt werden kann. Immerhin ist damit auch eine satte Lernkurve verbunden, was bei kleineren Projekten nicht gerechtfertigt ist. Angenommen, Ihr System aus Mitarbeiterklassen dient als Basis für eine Software, die Mitarbeiter verwaltet. Andere Entwickler sollen Zusatzmodule schreiben und dafür gegebenenfalls weitere Klassen nutzen dürfen, die mehr oder weniger der vorgestellten Klasse User ähneln. Um diese mit umfassender Funktionalität ausstatten zu können, müssten Sie sehr weitreichende Strukturen der Basisklasse vorausdenken. Das funktioniert in der Praxis nicht. Deshalb einigt man sich darauf, nur den Aufbau der Klasse vorzugeben, nicht jedoch die Implementierung. Als Vereinbarung über den Aufbau dient die Schnittstelle. Für User sieht sie folgendermaßen aus:
Listing 7.4: Eine Klasse ableiten (class_extends)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
148 __________________________________________ 7 Sammeltransporte: Objekte Listing 7.5: Eine Schnittstelle wird definiert (interface)
interface Employees { public function public function public function public function }
Wenn nun eine neue Klasse erzeugt werden will, die zu den anderen kompatibel sein soll, muss sie lediglich diese Schnittstelle benutzen – man spricht dann von »implementieren«:
class User implements Employees Es ist dann Sache der Klasse, die Methoden zu definieren, also den Code mitzubringen. Dies ist ein wesentlicher Unterschied zu extends, wo Methode mitsamt dem Code übernommen wird. Ende der Vererbung Soll die Vererbung unterdrückt werden, stellt man einer Klasse das Schlüsselwort final davor.
final class Admin Damit wird verhindert, dass aus einer endgültig ausformulierten Klasse weitere Varianten entstehen, die sich möglicherweise widersinnig oder unlogisch im Sinne des ursprünglichen Entwicklers verhalten. Bitte vererben! Zu final gibt es auch ein Gegenstück: abstract. Dies dient Klassen als Definition dafür, dass der erbende Teil noch selbst Code aufbringen muss. Das ähnelt den Schnittstellen, dient jedoch nicht als Erweiterung einer bestehenden Hierarchie unabhängig von der bestehend, sondern in Abhängigkeit von dieser. Die Entscheidung darüber, ob Schnittstellen oder abstrakte Klassen genutzt werden, setzt wieder einige Erfahrung voraus. Wichtig zu wissen ist, dass auch einzelne Methoden als abstract bezeichnet werden können, während eine Schnittstelle immer das gesamte Gebilde betrifft.
7.3 Funktionen für Objekte
Spezielle Funktionen
Des weiteren sollten Sie sich mit einigen Funktionen auseinandersetzen, die den Umgang mit Klassen unterstützen. Eine vollständige Übersicht über Syntax und Anwendungsbeispiele finden Sie in der Referenz.
7.3.1
Methoden aufrufen
Der Aufruf von Methoden mit den Operatoren -> und :: ist manchmal nicht hinreichen flexibel. Sie können dies auch mit Hilfe einer Funktion initiieren:
Listing 7.6: Methoden per Funktion aufrufen (classdynamic)
Zoll // 4.5 Zoll cm
// 3 cm
Die Funktion call_user_method erwartet mindestens zwei Parameter: Den Namen einer Methode und einer Klasse. Der dritte und eine beliebige Anzahl weiterer Parameter werden an die Methode beim Aufruf übergeben. Ergänzend sei auf call_user_method_array hingewiesen, wo die Parameter als Array übergeben werden. Das ist interessant, wenn sehr viele Parameter erforderlich sind.
7.3.2
Eigenschaften von Klassen ermitteln
Wenn Sie große Projekte in Planung haben, helfen Ihnen einige Funktionen, flexibel auf Klassen zu reagieren. So ermitteln die folgenden Funktionen Eigenschaften von Klassen durch Zugriff auf bereits abgeleitete Objekte: • method_exists($object, "method_name") Gibt TRUE zurück, wenn die Methode in dem angesprochenen Objekt existiert. • is_subclass_of($object, "superclass") Gibt TRUE zurück, wenn das Objekt $object von der Klasse superclass abgeleitet wurde, beispielsweise durch extends. • get_class_methods("classname") Diese Funktion gibt ein Array mit allen Methoden der Klasse zurück. Es spielt keine Rolle, ob ein Objekt dieser Klasse erzeugt wurde. Weitere Funktionen finden Sie in der Referenz. Ein Anwendungsbeispiel zeigt das folgende Skript:
Listing 7.7: Ermitteln von definierten Methoden (calls_methods)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
150 __________________________________________ 7 Sammeltransporte: Objekte } $arr = get_class_methods('calc'); foreach($arr as $method) echo "$method "; ?> Die Klasse wird hier wie üblich definiert. Objekte werden nicht instanziiert. Mit der foreach-Schleife werden die Methodennamen ausgegeben. Der Einsatz bietet sich vor allem zur Fehlersuche an, aber auch zum Abfangen von Fehlern bei der Verwendung fremder Klassen. Abbildung 7.3: Ausgabe von Listing 7.7
7.4
Klassenpraxis
Ein Anwendungs- In der Praxis werden Klassen überwiegend eingesetzt, um in vielen Programmen wiederverwendet werden zu können. Solche Definitionen beispiel
zeichnen sich deshalb durch eine gewisse Universalität aus. Das folgende Projekt zeigt die Entwicklung einer einfachen Klasse und eine Strategie, wie davon andere abgeleitet werden können.
7.4.1
Herausforderung: Grafik selbstgebaut
Häufig benötigt man beim Aufbau von Webseiten kleine Grafiken, die als Abstandhalter, Linien oder Graphen verwendet werden. Dies sind in der Regel kleine GIF-Bildchen, einfarbige, im Idealfall nur 1x1 Pixel große Elemente, die erst im Browser ihre endgültige Größe erreichen. Nun können Sie sich Dutzende Varianten vorher in Adobe Photoshop anfertigen – oder diese von PHP generieren lassen. Ohne GD2Bibliothek
Für die automatische Generierung von Grafiken gibt es die GD2Bibliothek. Der Einstieg ist aber nicht einfach und der Umgang damit nicht unproblematisch. In Abschnitt 13.2 Hilfsdesigner: Bildfunktionen ab Seite 315 finden Sie dazu eine praktische Einführung.
Bilder selbstgemacht
Hier geht es nur um die Erzeugung eines kleinen Standard-GIFs. Die innere Struktur eines solchen Bildes ist klar festgelegt. Entsprechend bietet es sich an, dies in der Klasse bei Bedarf zusammenzusetzen. GIF ist – wie jedes andere Bildformat auch – sehr genau definiert. Praktisch setzt sich ein Bild aus Basisinformationen und Bilddaten zusammen. Bei GIF beginnt die Bilddatei mit den Zeichen »GIF« (3 Bytes), dann folgt die Version »89a« (3 Bytes) und der Bildgröße (4 Bytes sowie Informationen über den Aufbau der Farbtabellen.
7.4 Klassenpraxis______________________________________________________ 151 Abbildung 7.4: Aufbau des Kopfes einer GIFDatei
Abbildung 7.4 zeigt den Aufbau des Kopfes, wie sie in der Klassendefinition verwendet wird. Das Bild soll 1x1 Pixel groß werden. Anschließend folgt die Farbtabelle und verschiedene weitere Bildinformationen, unter anderem für Transparenz. Dazu gibt es eine Reihe Spezialliteratur; der genaue Aufbau soll hier nicht weiter betrachtet werden. GIF erlaubt auch die Definition von Sequenzen (animierte GIFs und die Kompression der Bilddaten). Beides wird hier nicht benötigt. Der Code 0x3B schließt das Bild übrigens ab und muss immer am Ende stehen. Die erste Klasse Die erste Klasse erzeugt lediglich ein schwarzes Pixel. Sie wird später um eine Farbkomponente erweitert.
end = str_repeat('f', 1530); } private function hex2bin($s) { $t = pack("H*", $s); return $t; } public function Create ($incolor = 0) { $pix = Giffy::ini . $this->color . $this->end; if ($incolor == 1) { $pix .= Giffy::inc; } else {
Listing 7.8: Klasse zum Erzeugen eines 1x1-Pixel-GIF (giffy)
} } $gifPix = new Giffy(); header ("Content-type: image/gif"); echo $gifPix->Create(); ?> Wenn Sie die Klasse abschreiben, achten Sie unbedingt auf die exakte Schreibweise der Zahlenkolonnen am Anfang – diese müssen auch in einer Zeile stehen, der Umbruch ist nur für den Druck eingebaut worden. Die Klasse definiert folgende intern verwendete Eigenschaften: • $ini Startsequenz der GIF-Datei • $end Endsequenz der Farbtabelle der GIF-Datei • $nor Endsequenz für transparente Bilder • $inc Endsequenz für nichttransparente Bilder Die Variable $end besteht nur aus FF-Bytes und wird mit dem Konstruktor giffy() aufgefüllt.
$this->end = str_repeat('f', 1530); Das Bild selbst setzt die öffentliche Methode Create zusammen. Da mit Hexadezimalzahlen gearbeitet wird, die in Form von Zeichenketten existieren, müssen diese am Ende noch in eine binäre Form gebracht werden. In PHP5 verwenden Sie für binäre Operationen am besten die Funktion pack. Sie »verpackt« Zeichenketten und Zahlen in binäre Formate. Das funktioniert so ähnlich wie bei printf, mit einer Formatanweisung:
$t = pack("H*", $s); Der Buchstabe »H« steht dabei für hexadezimale Zahlen, das Sternchen wiederholt diese Formatierung solange wie Eingabedaten in der Variablen $s zu finden sind. Verwendung der Klasse
Das reihenweise Erzeugen und Speichern von Bildern ist sicher nicht besonders hilfreich. Einfacher ist es, die generierten Bilder direkt zum Browser zu senden. Die Klasse wird deshalb um Code erweitert, der das Bild erzeugt und direkt an den Browser sendet. Dazu wird das Objekt erzeugt und mit echo die Ausgabe der Methode Create an den Browser gesendet. Damit dieser die Angaben bearbeiten kann, werden entsprechende Kopfdaten mitgegeben:
header("Content-type: image/gif"); Wichtig ist, dass vor der Ausgabe von Headern keine weiteren Daten gesendet werden – auch keine Leerzeichen. Natürlich muss der Aufruf des Skripts aus Listing 7.8 irgendwie initiiert werden. Dazu dient eine kleine HTML-Datei als Test:
7.4 Klassenpraxis______________________________________________________ 153 Braucht man einzelnen GIF-Punkte?
JA
NEIN
Listing 7.9: Nutzung der dynamischen GIF-Erzeugung (GiffyTest1)
Das Skript besteht nur aus reinem HTML. Beachten Sie den Aufbau der -Tags. Hier wird als Ziel kein Bild angegeben, sondern das zuvor definierte PHP-Skript.
Das Ergebnis ist schon recht ansehnlich. Ein schwarzes GIF abzulegen wäre jedoch einfacher gewesen; Farbe muss also her! Abbildung 7.5: Test der Klasse »Giffy«
Eine Erweiterung der Klasse um eine Farbfunktion ist deshalb der nächste Schritt. Erweiterung einer Klasse Die Erweiterung erfolgt mit Hilfe der Anweisung extends. Dabei kann eine Erweiterung der Klasse nicht nur die Eigenschaften und Methoden der Mutterklasse erben, sondern auch überschreiben. Beides wird in der folgenden Definition ausgenutzt.
color = $color; $gif = parent::Create($incolor); return $gif; }
Nach der Einleitung mit extends wird eine neue Methode Create definiert. Diese überschreibt die bereits in der Mutterklasse vorhandene Funktion gleichen Namens:
function Create($color, $incolor = 0) Neu an der Definition ist ein weiterer Parameter: $color. Dieser wird verwendet, um die entsprechende Eigenschaft in der Mutterklasse zu setzen. Der Zugriff gelingt, weil diese Eigenschaft in der Klasse als protected gekennzeichnet wurde.
$this->color = $color; Dann wird die alte Funktion create der Mutterklasse aufgerufen. Die »Eltern«-Klasse wird hier mit parent direkt adressiert und mit dem ::Operator angesprochen, weil die Verwendung von $this zu einem Namenskonflikt führt. Die Parameter entsprechen natürlich der alten Definition:
$gif = parent::Create($incolor); Die Klasse verwenden
Die Verwendung unterscheidet sich nur durch den zusätzlichen Parameter $color und natürlich durch Aufruf der neuen Klasse:
$gifPix = new GiffyColor(); header("Content-type: image/gif"); echo $gifpix->create(isset($_GET['color']) ? $_GET['color'] : '000000'); Der Zugriff auf das Array $_GET erfasst per HTTP-GET übertragene Parameter. Diese Technik wird ausführlich in Abschnitt 9.2 Daten per URL weiterreichen ab Seite 213 erläutert. Damit das funktioniert, wird die Testdatei ebenfalls erweitert. Dazu wird der Parameter color an den Aufruf des Skripts angehängt: Listing 7.11: Aufruf für GiffyColor (GiffyTest2)
7.5 Eingebaut: Die Fehlerklassen ________________________________________ 155
Auch wenn es der Druck nicht erlaubt: Abbildung 7.6 zeigt farbige Bilder. Von Vorteil ist nun, dass Sie die Farben dynamisch erzeugen können, beispielsweise für eine personalisierte Seite, bei der sich die Benutzer aus einer Vielzahl von Farben eigene aussuchen können. Sie sparen sich die prophylaktische Anfertigung hunderter Minibildchen. Abbildung 7.6: Tatsächlich farbig: Bunte GIFs dynamisch erzeugt
Fehlersuche Nachteilig bei diesem Verfahren ist die mangelnde Testmöglichkeit. Feh- Wenn es nicht ler im Skript werden nicht angezeigt, weil der Browser fehlerhafte Bild- funktioniert daten – die eine Fehlerausgabe für ihn ist – einfach ignoriert oder ein Standardbild darstellt. Rufen Sie die Klassendefinition deshalb zuerst direkt auf. Dann sehen Sie zwar kein Bild, dafür aber eventuell auftretende Warnungen oder Hinweise. Wenn nichts mehr angezeigt wird, handelt es sich um Bilddaten – dann steht dem regulären Aufruf nichts mehr im Wege.
7.5
Eingebaut: Die Fehlerklassen
Im letzten Kapitel wurde bereits kurz die Fehlerbehandlung mit try/catch vorgestellt. Für den praktischen Einsatz ist etwas mehr Aufwand nötig, der sich aber lohnt, wenn größere Programme geschrieben werden.
7.5.1
Die Klasse Exception
PHP5 verfügt über eine eingebaute Klasse, die die Fehlerbehandlung kontrolliert: Exception (dt. Ausnahme). Diese Klasse ist folgendermaßen definiert:
class Exception { function __construct(string $message=NULL, int code=0) { if (func_num_args()) { $this->message = $message; }
$message = 'Unknown exception'; // Text $code = 0; // User Code $file; // Dateiname $line; // Quellzeile
private $trace; private $string;
}
// Trace
final function getMessage() { return $this->message; } final function getCode() { return $this->code; } final function getFile() { return $this->file; } final function getTrace() { return $this->trace; } final function getTraceAsString() { return self::TraceFormat($this); } function _toString() { return $this->string; } static private function StringFormat(Exception $exception) { } static private function TraceFormat(Exception $exception) { }
Wenn man nun eigene Ausnahmen definiert, wird sinnvollerweise von dieser Klasse abgeleitet. Das folgende Beispiel zeigt, wie dies prinzipiell aussieht: Listing 7.12: Eigene Ausnahmeklasse (try_numexc)
7.5 Eingebaut: Die Fehlerklassen ________________________________________ 157 try {
if(!is_int($i)) { throw new NumberFormatException('$i ist keine Zahl.'); } else { echo 'OK'; }
} catch (NumberFormatException $exception) { echo $exception->toString(); } ?> Die Klasse NumberFormatException wird direkt von Exception abgeleitet. Sie verfügt deshalb über die passenden Methoden zum Aufnehmen und Ausgaben einer Fehlermeldung. Damit diese auch benutzt werden können, ist der Konstruktor der Stammklasse aufzurufen:
parent::__construct($exception); Im try-Block des Testskripts erfolgt dann eine entsprechende Fehlerprüfung und bei Bedarf die Auslösung des Fehlers:
throw new NumberFormatException("$i ist keine Zahl."); Im catch-Zweig wird dann darauf reagiert. Welche konkrete Reaktion ein Skript verlangt, hängt von der Schwere des Fehlers ab. Entweder erfolgt eine entsprechende Fehlerausgabe mit nachfolgendem Programmabbruch oder beispielsweise ein »stiller« Neustart. Zum unmittelbaren Beenden des Programms bietet sich die Anweisung die an:
die($exception->getMessage());
7.5.2
Praktischer Einsatz der Fehlerbehandlung
Fehlerklassen sind normalerweise Teil der Konstruktion von Bibliotheken. Als Entwickler einer solchen Funktionssammlung definieren Sie nicht nur die Verwendung, sondern auch die Fehlerbehandlung. Der Nutzer der Klasse ist dafür zuständig, dann auf die Fehler in entsprechender Weise zu reagieren. Erst bei diesem Szenario wird deutlich, wozu das try/catch-Konzept tatsächlich in der Lage ist. Es degradiert sich selbst lediglich deshalb, weil die interne Laufzeitfehlbehandlung in das Konzept nicht eingeschlossen ist, was den praktischen Nutzen mehr oder weniger auf die Bibliotheksprogrammierung beschränkt. Das Verständnis dafür ist dennoch für jeden PHP-Entwickler notwendig, weil die meisten früher oder später fremde Bibliotheken nutzen.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
158 __________________________________________ 7 Sammeltransporte: Objekte Um die Sache etwas zu verdeutlichen, soll das letzte Beispiel nochmals leicht verändert benutzt werden. Funktion und Fehlerklasse sind nun beides Klassen und stehen als externe Bibliothek zur Verfügung. Sie sind folgendermaßen definiert: Listing 7.13: Eine kleine Musterbibliothek (try_numexclib)
$i ist keine Zahl."); } else { echo 'OK'; } } } ?> Das typische Szenarion sieht nun folgendermaßen aus: Diese Bibliothek wird von einem Entwickler erstellt und der Allgemeinheit verfügbar gemacht. Ein anderer Programmierer möchte die Klasse nun nutzen. Er weiß, welche Klassen enthalten sind und welche Fehler ausgelöst werden, wenn bestimmte Bedingungen nicht erfüllt sind. Er nutzt die Klasse folgendermaßen:
Listing 7.14: Nutzung einer Bibliothek (try_numexc2)
getMessage(); } ?> Hier sieht die Sache nun deutlich einfacher und konsistenter aus. Das ist der Sinn der Fehlerbehandlung. Denn der Anwender der Bibliothek kann entscheiden, ob und wie er Fehler abfängt und wie er mit ihnen im
7.5 Eingebaut: Die Fehlerklassen ________________________________________ 159 catch-Zweig verfährt. Um die eigentliche Fehlerprüfung muss er sich dagegen nicht kümmern. Wie im realen Leben tritt natürlich nicht nur ein einziger Fehler auf. Ein und derselbe Prozess kann verschiedene Fehler erzeugen. So könnte sich an die Zahlenprüfung noch eine solche auf negative Zahlen anschließen. Durch die Aneinanderreihung von catch-Zweigen kann auf jeden Fehler einzeln reagiert werden.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
7.5 Eingebaut: Die Fehlerklassen ________________________________________ 161
8 Daten bewegen: Dateisystem und FTP Mit PHP können Sie komfortabel auf das Dateisystem zugreifen. Damit ergeben sich umfangreiche Anwendungsmöglichkeiten. Der Zugriff ist dabei nicht nur auf das lokale Dateisystem des Servers beschränkt, sondern kann sich auch auf andere Computer im Internet erstrecken – per FTP (File Transfer Protocol) oder HTTP. Umgekehrt können Sie auch zulassen, dass Benutzer Dateien auf den Server hochladen – auch dafür stellt PHP eine sehr einfach zu programmierende Technik zur Verfügung.
Der Zugriff auf das Dateisystem ist eine enorme Erleichterung bei der Umsetzung vieler Projekte. Sie können mit Hilfe von Dateien Daten dauerhaft speichern, Grundeinstellungen und Stammdaten lesen. Viele praktische Anwendungen gibt es darüber hinaus, beispielsweise: • Protokolle schreiben Protokollieren Sie Nutzerbewegungen in eigenen Dateien. • Formulardaten ablegen
Wozu Sie Dateifunktionen verwenden können
Speichern Sie Formulare als Dateien ab. • News des Tages Bauen Sie dynamische Daten in Ihre Websites ein, die Sie oder andere als Textdatei abgelegt haben, beispielsweise für Tagestipps. • Datenspeicher Legen Sie Daten, die sich nur selten ändern, in Textdateien ab. Die Arbeit mit Dateien erfolgt nach einem einfachen Schema. Zuerst wird die Datei geöffnet, dann werden Daten herausgelesen oder hineingeschrieben und dann wird die Datei wieder geschlossen. Wenn eine Datei nicht existiert, kann sie beim Öffnen automatisch angelegt werden. Wenn eine Datei geöffnet ist, kann der gesamte Inhalt in eine Variable transportiert werden. Besser ist die Verwendung eines Dateizeigers. Dieser markiert eine bestimmte Position in der Datei und steuert die Ausgabe der enthaltenen Daten. PHP kann zwischen Binär- und Textdateien unterscheiden. Textdateien Binär oder Text? werden im ASCII-Modus geöffnet und es bieten sich spezielle Bearbeitungsfunktionen dafür an. Um mit Dateien arbeiten zu können, werden so genannte Datei-IDs oder Handles Handles erzeugt. Ein Handle ist ein Verweis auf eine Datei. Funktionen zur Dateimanipulation nutzen Handles zur Zugriffssteuerung. Dies ist einfacher, als immer wieder den kompletten Pfad zu einer Datei anzugeben.
Handlungsanleitungen in Form von Handles Im Buch wird sehr oft der Begriff »Handles« genutzt. Generell handelt es sich dabei um eine Referenz (Verweis) auf eine bestimmte Verbindung. Der Begriff ist üblich und wurde deshalb nicht übersetzt.
Zugriff auf entfernte Server Als Dateiname kann, außer einem physischen Pfad auf einem Unix- oder Windows-System, auch eine HTTP- oder FTP-Adresse genutzt werden. Dann wird eine Verbindung zu einem Webserver (http:) oder einem FTP-
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
164 ________________________________ 8 Daten bewegen: Dateisystem und FTP Server aufgebaut. FTP-Verbindungen können lesen und schreiben, Daten aus HTTP-Verbindungen können Sie natürlich nur lesen. Für FTP und auch für HTTP stehen neben den Standardfunktionen für die Dateieinund -ausgabe auch spezielle Module zur Verfügung.
8.1.2
Zugriff auf Dateien
Dateien lesen ist der elementarste Schritt. Der Webserver muss dazu keine besonderen Voraussetzungen erfüllen – im Gegensatz zum Schreiben, wo bestimmte Rechte existieren müssen. Sie können also sofort loslegen. Dateien ins Skript einbinden Mit der Anweisung include wird eine PHP- oder HTML-Datei eingeschlossen und so ausgeführt, als wäre sie alleine aufgerufen worden. Sie können dies auch in Schleifen einsetzen. Hier ein Beispiel für die einfachste Anwendung:
"; include("kopf.inc.php"); echo ""; ?> Das Skript führt zuerst die erste echo-Anweisung aus. Dann wird die Datei kopf.inc.php geladen und komplett verarbeitet. Anschließend wird die zweite echo-Anweisung ausgeführt. In PHP ist es möglich, dass die Befehlsstruktur in einer mit include eingebundenen Datei als Block aufgefasst wird. Damit ist auch die Anwendung der Anweisung return möglich. Der Rückgabewert wird der Variablen des aufrufenden Skripts übergeben: Listing 8.1: Rückgabewerte eines Moduls (include1)
"; $value = include("modul.inc.php"); echo $value, ' = ', $path; echo ""; ?> Das Skript bindet die Datei modul.inc.php normal ein, als wäre der Code an dieser Stelle geschrieben worden.
$value = include("modul.inc.php"); Wenn in diesem Skript eine return-Anweisung steht, wird deren Wert an die Variable $value übergeben.
Die Verwendung von return ist allerdings tückisch. Ähnlich wie bei Probleme mit Funktionen wird der nach dem return liegende Teil nicht mehr verarbei- return tet. Das gilt auch für dahinter liegende HTML-Tags, die vielleicht nicht direkt im Zusammenhang mit dem PHP-Code stehen. Listing 8.3: Ausgabe des Skripts aus Listing 8.2 Wenn Sie mit include und return arbeiten, sollten Sie sich darüber klar sein, dass es einen wesentlichen Unterschied zu Funktionen gibt: Die Variablen des eingeschlossenen Blockes sind im gleichen Sichtbereich wie der umschließende Bereich – außerhalb einer Funktion also global sichtbar. Umgekehrt sind vor dem Aufruf von include bereits definietrte Variablen auch im Block sichtbar. Wenn Sie ein Modul fest in die Seite einbinden möchten, so als wäre es require an dieser Stelle direkt untergebracht, nutzen Sie dagegen die Anweisung require.
"; require("header.inc.php"); ?>
Listing 8.4: Modul mit require einbinden
In Dateien, die mit require eingebunden werden, muss der PHP-Code in den Begrenzungszeichen stehen.
Die Unterschiede zwischen include und require Der Unterschied zwischen den beiden Befehlen liegt im Zeitpunkt der Verarbeitung der einzuschließenden Datei. require wird zuerst ausgeführt – vor dem Parsen des gesamten Skriptes. Bei include dagegen erfolgt die Integration erst zu dem Zeitpunkt, wenn die Abarbeitung an dieser Stelle angelangt ist. Wird die Stelle interpretiert, kann sie natürlich auch in einer Schleife stehen. Allerdings sollte include dann als Block gekennzeichnet sein und in geschweiften Klammern stehen. Unterschiedlich ist auch das Fehlerverhalten: Bei include wird lediglich eine Warnung erzeugt, wenn die Datei nicht gefunden werden kann, require bricht dagegen mit einem Fehler ab.
Informationen zur Verwendung von $_SERVER finden Sie im Daten über den Server ermitteln ab Seite 219.
3
Abschnitt 9.3
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
166 ________________________________ 8 Daten bewegen: Dateisystem und FTP Tipps im Umgang mit eingeschlossenen Dateien Sicherheitsprobleme
Normalerweise können Nutzer den Inhalt der Skripte nicht sehen. Jede Datei mit der Endung PHP oder PHP5 wird vom Webserver an PHP gesendet. Es ist natürlich auch möglich, jede andere Endung anzugeben, auch HTML. Das geht bei Dateien, die mit Sicherheit keinen PHP-Code enthalten, aber zu Lasten der Leistung. Oft werden Dateien, die mit include eingeschlossen werden sollen, mit .INC bezeichnet. Auch diese Endung wird nicht verarbeitet. Das ist für den Ablauf des Skripts egal – die Verarbeitung erfolgt im Rahmen des »umgebenden« Skripts und damit unter dessen Regie. Wenn nun aber ein Nutzer den Pfad zu den Include-Dateien herausbekommt, kann er deren Namen in der Adresszeile des Browsers direkt eingeben. Der Webserver wird die Endung nicht kennen und dem Browser die Datei direkt anbieten. Der erkennt einfachen Text und stellt ihn dar. Da in Include-Dateien auch Kennwörter für Datenbanken stehen können, entsteht so eine erhebliche Sicherheitslücke.
Abhilfe
Abhilfe schaffen Sie sehr einfach. Benennen Sie einfach alle IncludeDateien mit .INC.PHP. So haben Sie eine eindeutige Kennzeichnung und erzwingen im Notfall das Parsen des Codes. Das mag zwar zu einer Fehlermeldung führen – an den Inhalt gelangt der Nutzer dennoch nicht. Mehrfacheinbindung und verschachtelte Module
Probleme mit verschachtelten Dateien include_once require_once
Normalerweise ist es kein Problem, wenn Sie innerhalb einer mit include oder require eingeschlossenen Datei weitere Einschlüsse vornehmen. Handelt es sich jedoch um dieselbe Datei, wird ein Laufzeitfehler erzeugt, wenn dort beispielsweise Funktionen definiert werden. Der mehrfache Einschluss ist zwar möglich – nicht aber die mehrfache Definition von Funktionen oder Klassen, die daraus möglicherweise resultiert. Um dieses Problem zu vermeiden, können Sie die Anweisungen include_once und require_once verwenden. Damit erfolgt die Aktivierung des Inhalts garantiert nur einmal. Daten aus Textdateien holen
readfile
Abbildung 8.1: News aus einer Textdatei
Die Funktion readfile liest eine Datei und sendet deren gesamten Inhalt ohne weitere Bearbeitung an den Browser. Als Parameter wird der Dateiname mit den nötigen Pfadangaben übergeben. Hier sollte kein ausführbarer PHP-Code stehen, sonst wird dieser im Browser angezeigt und nicht ausgeführt. Zuerst das Ergebnis:
Listing 8.5: Datei einlesen und ausgeben (readfile)
Das Skript wird bis readfile normal abgearbeitet. Mit Aufruf der folgenden Zeile, wird die Datei gelesen und sofort ausgegeben:
readfile($datei); Die abschließende Ausgabe einer Linie erfolgt nach dem Text der Datei NEWS2.TXT, zur Kennzeichnung der Grenzen:
echo ""; Analog funktioniert auch file, die gelesene Datei wird dabei in einem Array abgelegt. Jede Zeile wird zu einem Element eines eindimensionalen Arrays. Der Zeilenumbruch verbleibt am Ende des Elements. Das ist natürlich nur sinnvoll, wenn es sich um Textdateien handelt, die zeilenweise aufgebaut sind. Im folgenden Beispiel liest ein Skript »sich selbst« sein. Anschließend wird es zeilenweise mit Zeilennummern ausgegeben.
%4d: %s ", $line, htmlspecialchars($data)); } ?> Hier wird zuerst der Name des Skripts ermittelt, das gerade läuft (das Skript gibt sich also selbst aus): basename($_SERVER['PHP_SELF']). Dieser Name wird als Parameter für file verwendet:
$filearray = file(basename($_SERVER['PHP_SELF'])); Jetzt steht das Skript zeilenweise im Array $filearray. Die Schleife durchläuft nun das Array Zeile für Zeile:
while(list($line, $data) = each($filearray)) Jede Zeile wird so formatiert, dass der Browser die HTML-Tags nicht ausführt, sondern anzeigt.
printf("%4d: %s ", $line, htmlspecialchars($data)); Der Weg über file ist meist der einfachste. Für den Umgang mit dem Inhalt stehen Ihnen danach alle Arrayfunktionen zur Verfügung. In der Kombination von Dateizugriff und Arrayoperation ist der Funktionsvorrat in PHP5 sehr leistungsfähig. Die folgende Abbildung zeigt, wie die Ausgabe des Skripts erfolgt:
Listing 8.6: Datei in Array einlesen und verarbeiten (fileread)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
168 ________________________________ 8 Daten bewegen: Dateisystem und FTP Abbildung 8.2: Ausgabe mit Zeilennummern
Direkt mit Textdateien arbeiten Um direkt mit Dateien arbeiten zu können, dient ein so genanntes DateiHandle4. Dies ist eine Referenz auf die geöffnete Datei. fopen
Die für die Erzeugung des Handles zuständige Funktion heißt fopen. Sie können den Dateinamen und ein Attribut angeben. Das Attribut bestimmt, wie die Datei geöffnet wird: • r Öffnet eine Datei zum Lesen und setzt den Dateizeiger auf den Anfang (das erste Zeichen in der Datei). • r+ Öffnet eine Datei zum Lesen und Schreiben und setzt den Dateizeiger auf den Anfang (das erste Zeichen in der Datei).
• w Öffnet eine Datei zum Schreiben. Wenn die Datei nicht existiert, wird sie angelegt. Wenn die Datei vorhanden ist und Daten enthält, werden diese gelöscht und die Länge wird auf 0 gesetzt. • w+ Öffnet eine Datei zum Schreiben und Lesen. Wenn die Datei nicht existiert, wird sie angelegt. Wenn die Datei vorhanden ist und Daten enthält, werden diese gelöscht und die Länge wird auf 0 gesetzt.
• a Öffnet die Datei zum Schreiben. Wenn die Datei nicht existiert, wird sie neu angelegt. Vorhandene Daten bleiben erhalten. Der Dateizeiger steht am Ende der Datei (a steht für append, dt. anhängen).
4
Dies ist ein feststehender Begriff, der üblicherweise nicht übersetzt wird.
• a+ Öffnet die Datei zum Schreiben und Lesen. Wenn die Datei nicht existiert, wird sie neu angelegt. Vorhandene Daten bleiben erhalten. Der Dateizeiger steht am Ende der Datei. Jedes der Attribute kann mit einem vorangestellten b kombiniert werden, Binärdateien um den Zugriff auf Binärdateien zu ermöglichen. Daraus ergeben sich dann folgende Kombinationen:
• br, br+ • bw, bw+ • ba, ba+ Geöffnete Dateien müssen wieder geschlossen werden, um Systemres- fclose sourcen zu schonen und anderen Prozessen den Zugriff zu ermöglichen.
fclose($handle); Der Zugriff auf die geöffnete Datei erfolgt mit einer ganzen Palette von Dateifunktionen, denen alle ein Parameter gemeinsam ist: Das bereits erwähnte Datei-Handle. Zeilen- und Zeichenweise aus einer Textdatei lesen Die Funktion fgets liest aus einer Datei von der Position des Dateizei- fgets gers.
$bytestream = fgets($handle, 2048); Dies erfolgt, bis mindestens eines der drei folgenden Ereignisse eintritt: • Die Anzahl Bytes, die angegeben wurde, ist erreicht • Ein Zeilenende wurde erreicht. Dies wird anhand des Zeilenumbruchs \n erkannt. Das Zeilenumbruch-Tag , das in HTML benutzt wird, erkennt die Funktion nicht. • Das Dateiende wurde erreicht. Die Funktion fread entspricht fast der Funktion fgets, interpretiert aber fread nicht den Zeilenumbruch. Damit wird tatsächlich bis zum Dateiende oder der angegebenen Anzahl Bytes gelesen – die Funktion kann daher auch für Binärdateien genutzt werden:
$bytestream = fread($handle, 4096); Das Beispiel liest 4 KByte aus einer Datei, die vom Handle $handle adressiert wird. Das Einblenden eines Tipps des Tages ist eine typische Anwendung. AnwendungsSenden Sie jeden Tag per FTP eine Textdatei an Ihren Webserver, die beispiel automatisch in eine HTML-Seite eingebettet werden soll. Das folgende Skript liest die Datei zeilenweise und gibt nur die Zeile aus, die zu einem bestimmten Tag passt. Den Aufbau der Nachrichtendatei finden Sie in Listing Listing 8.8:
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
170 ________________________________ 8 Daten bewegen: Dateisystem und FTP Listing 8.7: Zeilenweises Lesen einer Datei (file_fgets)
"; } } fclose($fp); ?> Das Beispiel in Listing 8.7 enthält keine Besonderheiten, wendet aber mehrere übliche Techniken an, die Sie beherrschen sollten. Zuerst wird ein Handle auf die Datei erstellt und in der Variablen $fp gespeichert:
$fp = fopen("news.txt", "r"); Die Zuweisung in der Schleife liest die nächste Zeile aus der angegebenen Datei, immer zu Stücken von 1 024 Byte (1 KB):
$line = fgets($fp, 1024) Der gesamte Ausdruck wird FALSE, wenn der Zugriff misslingt. Dies ist normalerweise nur am Ende der Datei der Fall. Hier werden also Zugriff, Auslesen der Daten und Prüfung auf Dateiende in einem einzigen Ausdruck zusammengefasst. Der Dateizeiger wird automatisch weiter gesetzt, sodass die Ausgabe mit die nächste Zeile fortgesetzt wird:
echo fgets($fp, 1024)." " Dies funktioniert beim Ablauf nun so: Jede Zeile wird gelesen und mit Hilfe der Funktion preg_match analysiert:
preg_match("/^\[[$select]+\]/", $line) Die Funktion preg_match nutzt einen einfachen regulären Ausdruck zur Prüfung. Mehr zu regulären Ausdrücken finden Sie in Abschnitt 13.3 Suchexperten: Reguläre Ausdrücke ab Seite 323. Solche Ausdrücke beschreiben Suchmuster und dienen hier der Erkennung der Wochentage in eckigen Klammern. Wenn die Prüfung erfolgreich, in der Datei der Wochentagscode vorhanden war, dann wird die darauf folgende Zeile ausgeführt:
echo fgets($fp, 1024) . " "; Diese liest nun die unmittelbar nach dem Code stehende Zeile und gibt sie aus. Die komplette Nachrichtendatei hat damit zwangsläufig folgenden Aufbau (jede Zeile endet mit einem Zeilenumbruch): Listing 8.8: Die Nachrichtendatei news.txt für Listing 8.7
[Mon] Heute beginnt die Woche! [Tue] Nur noch 4 Tage [Wed] Das Wochenende naht
8.1 Dateisystem _______________________________________________________ 171 [Thu] Fast fertig [Fri] Freitag nach 1... [Sat] Was suchst Du hier heute? [Sun] Was suchst Du hier heute? Praktisch kann man so sehr effizient jede erdenkliche Art von Textdateien analysieren, Teile daraus extrahieren und beliebig verwenden. Der umgekehrte Weg – das Schreiben von Dateien – ist der nächste logische Schritt. Zeilenweise in eine Textdatei schreiben Mit fputs werden Zeichenketten in eine Textdatei geschrieben. Die Angabe der Länge ist optional – ohne Angabe werden alle verfügbaren Daten übertragen. Wenn Sie Daten vor der Ausgabe formatieren möchten, nutzen Sie die Funktion sprintf. Die so bearbeitete Zeichenkette kann leicht weiter verwendet werden, auch zum Schreiben in eine Textdatei. Auch andere Formatierfunktionen wie number_format sind eine gute Ergänzung. Das folgende Beispiel schreibt die Felder eines Formulars in eine Textda- Anwendungstei. Mehr Informationen zu Formularen finden Sie im Abschnitt 9.1 beispiel Daten aus einem Formular ermitteln ab Seite 197. Der Name der Datei setzt sich aus dem Zeitstempel (Datum und Uhrzeit im Unix-Timestamp-Format) und einer fortlaufenden Nummer zusammen.
Form schreiben
Listing 8.9: Formulardaten in Dateien speichern (file_formsave)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
172 ________________________________ 8 Daten bewegen: Dateisystem und FTP break; case (strlen($plz) != 5): break; case (strlen($ort) < 2): break; default: $disabled = 'disabled'; $message = 'Vielen Dank für Ihre Nachricht'; $fc = 0; $fn = 'data/form_' . (string) time(); while (file_exists($file = sprintf('%s_%02d.frm', $fn, $fc++))); $fp = fopen($file, 'w'); foreach($_POST as $field => $value) { $field = str_pad(ucfirst("$field:"), 10, ' ', STR_PAD_RIGHT); fwrite($fp, sprintf("%s% 21s\n",$field,$value)); } fclose($fp);
} ?>
=$message?>
Das Skript verwendet einige Techniken, die auch in anderen Zusammenhängen interessant sind. So erfolgt vor der Abspeicherung eine Prüfung der Formulardaten. Das Beispiel in Listing 8.9 funktioniert außerdem nur, wenn der Webnutzer Schreibrechte im Unterverzeichnis /DATA hat (und dieses auch existiert).
Restriktionen: Rechte auf einem Webserver Bei der Einrichtung der Schreibrechte müssen Sie daran denken, dass der Webserver als spezielles Benutzerkonto (beispielsweise »nobody«) arbeitet und alle Benutzer aus dem Internet quasi über dieses Konto hereinkommen. Eine sehr strenge Vergabe der Rechte verhindert möglicherweise, dass die hier gezeigten Skripte funktionieren.
Funktionsweise Zur Prüfung wird eine switch-Anweisung eingesetzt. Diese prüft nacheinander die Felder auf bestimmte Eigenschaften. Wenn die Eigenschaft nicht TRUE ist, wird mit der Block mit break verlassen:
switch (TRUE) { case (strlen($name) < 3): break; case (strlen($adresse) < 5): break; case (strlen($plz) != 5): break; case (strlen($ort) < 2): break; Nur wenn alle Felder korrekt ausgefüllt wurden, wird der defaultZweig ausgeführt. Als Information wird dem Benutzer das Formular erneut angezeigt – diesmal mit gesperrten Feldern und einer Nachricht:
default: $disabled = 'disabled'; $message = 'Vielen Dank für Ihre Nachricht'; Dann werden die Formulardaten gespeichert. Zuerst ist dazu ein eindeutiger Dateiname erforderlich. Den ersten Teil bildet ein fester Pfad und ein Zeitstempel:
$fc = 0; $fn = 'data/form_' . (string) time(); Falls gleichzeitig mehrere Zugriffe auf das Formular erfolgen, wird der Zeitstempel (der sich nur jede Sekunde ändert), mit einer fortlaufenden Nummer ergänzt. Dazu wird in einer while-Schleife solange auf bereits vorhandene Nummern geprüft, bis ein freier Wert gefunden wurde. Gleichzeitig wird dieser freie Wert als Pfadname in der Variablen $file abgelegt. sprintf formatiert den Dateinamen in der Form data/form98124758_00.frm:
while (file_exists($file = sprintf('%s_%02d.frm', $fn, $fc++))); Anschließend wird diese Datei angelegt und zum Schreiben geöffnet:
$fp = fopen($file, 'w');
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
174 ________________________________ 8 Daten bewegen: Dateisystem und FTP Dann werden alle Felder ausgelesen (es spielt keine Rolle, wie viele es sind oder wie diese heißen):
foreach($_POST5 as $field => $value) Für die Darstellung werden die Feldname rechtsbündig mit Leerzeichen aufgefüllt (str_pad). Um eine bessere Lesbarkeit zu erreichen wird das erste Zeichen großgeschrieben (ucfirst):
$field = str_pad(ucfirst("$field:"), 10, ' ', STR_PAD_RIGHT); Die Felddaten selbst werden linksbündig aufgefüllt, dazu eignet sich sprintf. Die fertigen Daten werden in die Datei geschrieben:
fwrite($fp, sprintf("%s% 21s\n",$field,$value)); Folgen keine Felder mehr, wird die Datei geschlossen:
fclose($fp); Die folgende Abbildung zeigt eine typische Datei und wie die Daten abgelegt sind: Abbildung 8.3: Ansicht der Datei mit Formulardaten im Editor Das Formular selbst enthält wenig Besonderheiten. Beachten Sie die Sperrung der Felder nach erfolgreicher Anzeige. Abbildung 8.4: Das Formular nach dem Ausfüllen
Um Formulare ansehnlich zu gestalten, sollten Sie HTML-Tabellen einsetzen. So lassen sich Texte und Felder unabhängig voneinander ausrichten. Anzahl der Zeilen einer Textdatei bestimmen Die Anzeige der Zeilenanzahl ist leicht möglich, wenn die Funktion file in Verbindung mit count genutzt wird.
5
Mehr Informationen über die Bedeutung dieses Arrays finden Sie im Abschnitt 9.1 Daten aus einem Formular ermitteln ab Seite 197.
8.1 Dateisystem _______________________________________________________ 175 Das Skript kombiniert zwei Funktionen in einer Zeile. Die innere wird zuerst ausgeführt.
Listing 8.10: Anzahl Zeilen einer Textdatei (file_count)
Die Funktion file liest eine Datei in ein Array ein. Um die Daten geht es dabei nicht, weswegen auch keine Zuweisung an eine Variable erfolgt:
file("news.txt") Statt dessen wird das Array an count übergeben. Diese Funktion gibt die Anzahl der Elemente – hier also die Anzahl der Zeilen – zurück. echo gibt dieses Ergebnis aus. Datei in eine Zeichenkette einlesen Manchmal wird der Inhalt einer Datei nicht in einem Array, sondern in einer einzigen Zeichenkette benötigt. Vor allem im Hinblick auf die regulären Ausdrücke bieten sich gute Verarbeitungsmöglichkeiten an. Als mdirekter Befehl bietet sich file_get_contents an:
Das @ unterdrückt Fehlermeldungen, die erzeugt werden, wenn die Datei nicht existiert oder die Zugriffsrechte nicht ausreichen.
Umgang mit dem Dateizeiger beim Lesen und Schreiben Beim Lesen von Zeichen aus Dateien kann man, wenn auch mit etwas Aufwand, gezielt auf bestimmte Zeilen und Zeichen zugreifen. Prinzipiell ändert das aber nichts am sequenziellen Zugriff auf den Inhalt. Spätestens beim Schreiben hilft auch der Dateizeiger nicht weiter. Sie können also niemals auf direktem Weg mittendrin Zeilen einfügen oder an den Anfang setzen. »Anhängen« ist wörtlich zu nehmen, Daten können nur am Ende angefügt werden. Wenn ab der Position des Dateizeigers geschrieben wird, überschreibt PHP den an dieser Stelle befindlichen Inhalt. »Einfügen« gibt es nicht – in der Notation einer Textverarbeitung ist der Zugriff immer nach dem Prinzip »Überschreiben« gestaltet. Ebenso problematisch ist das gezielte Löschen einer Zeile. Nur mit der Kombination mehrerer Befehle und entsprechendem Leistungsbedarf des Skripts ist dies möglich. Das folgende Beispiel zeigt die prinzipielle Vorgehensweise, unter Nutzung eines Arrays:
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
176 ________________________________ 8 Daten bewegen: Dateisystem und FTP Listing 8.11: Löschen einer Zeile aus einer Textdatei (file_deleteline)
'; unset($myfile[$line]); $fh = fopen("$datei.bak", "w"); fputs($fh, implode('', $myfile)); fclose($fh); ?> Das Skript liest wieder eine Datei in ein Array ein.
$myfile = file($datei); Dann wird ein Element dieses Arrays gelöscht; in diesem Fall das vierte:
unset($myfile[$line]); Anschließend wird eine neue Datei zum Schreiben geöffnet, gebildet aus dem ursprünglichen Namen und der Dateierweiterung ».bak«:
$fh = fopen("$datei.bak", "w"); Das Array wird mit implode zu einer Zeichenkette zusammengefasst und diese dann in die Datei übertragen:
fputs($fh, implode('', $myfile)); Beachten Sie, dass Arrays nullbasiert indiziert sind, die erste Zeile der Textdatei also den Index 0 trägt. Entsprechend wird im Beispiel die vierte Zeile gelöscht. Der Zeilenumbruch, der jede Zeile abschließt, bleibt beim Einlesen in das Array erhalten, sodass implode ohne weitere Parameter genutzt werden kann. Das ist natürlich zugleich ein flexibler Weg, mit Dateien umzugehen, denn Sie können mit den Arrayfunktionen komfortabel die Daten manipulieren. Große Datenmengen sind aber damit trotzdem nur schwer handhabbar. Verwenden Sie dann besser Datenbanken, die auf diese Art der Manipulation spezialisiert sind. Wenn Sie nur lesend arbeiten, können Sie auch die internen Dateizeiger nutzen. Beim Schreiben finden diese keine Berücksichtigung. Mit dem Dateizeiger arbeiten Bei den bisherigen Ausführungen wurde unterstellt, dass der Dateizeiger beim Lesen automatisch weiter wandert und beim Schreiben an das Ende der geschriebenen Zeichen gesetzt wird. Wenn Sie sich aber in einer Textdatei frei bewegen möchten, ist eine gezielte Positionierung des Zeigers sinnvoll. Dateiende beachten
Bei solchen Bewegungen ist es wichtig, nicht versehentlich über das Dateiende hinaus zu lesen. In diesem Fall bricht entweder die genutzte Funktion ab oder PHP reagiert mit einem Laufzeitfehler. Das Dateiende können Sie mit feof ermitteln:
8.1 Dateisystem _______________________________________________________ 177 // übergeordnete Schleife if (feof($handle)) { break; } // Ende der Schleife Die Funktion feof gibt TRUE zurück, wenn das Ende der Datei erreicht wurde. Einige Funktionen werden mit Fehlermeldungen reagieren, wenn Zugriffe nach Erreichen des Dateiendes erfolgen. Die Funktion ftell gibt die aktuelle Position des Dateizeigers an. Sie Dateizeiger können sich damit eine bestimmte Position merken, andere Operationen positionieren ausführen und dann die alte Position mit fseek wiederherstellen. Wenn Sie mit Textdateien arbeiten, werden Sie vielleicht versuchen, Daten an bestimmten Positionen zu speichern. Eine Operation »Einfügen« gibt es jedoch nicht. Dateieigenschaften ermitteln
is_dir ermittelt, ob der Name ein Verzeichnis ist. An der Art des Na- Verzeichnis mens selbst kann man nicht feststellen, ob es eine Datei oder ein Ver- erkennen zeichnis ist. Die Funktion is_file ermittelt, ob es sich um eine reguläre Datei han- Datei erkennen delt. Dies gilt auf einem Windows-System für alle Dateien. Unix-Systeme können noch zwischen Dateien und Links unterscheiden.
is_link ermittelt, ob es sich um einen Link (Verweis) handelt. Die unter Hardlinks Windows üblichen Verknüpfungen sind keine echten, sondern nur simu- erkennen (Unix) lierte Links. PHP erkennt diese als Datei, nicht als Link. Die Funktion filemtime gibt das Datum zurück, an dem die Datei das Dateidatum letzte Mal geändert wurde. Der Rückgabewert ist der Unix-Zeitstempel, der für die Ausgabe formatiert werden muss, beispielsweise mit date(). Hinweise zu dieser Funktionen finden Sie in Abschnitt 13.4.2 Datumsfunktionen ab Seite 337.
filesize gibt die Größe der Datei in Byte zurück. Nutzen Sie die folgen- Dateigröße de Funktion, um Umrechnungen in KByte oder MByte vorzunehmen: pow(2,10)) { if ($v > pow(2,20)) { $r = (integer)($v / pow(2,20)); $r .= " MB"; } else { $r = (integer)($v / pow(2,10)); $r .= " KB"; } // end if } else {
Listing 8.12: Umrechnung von Byte in KB/MB (file_getreal)
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
178 ________________________________ 8 Daten bewegen: Dateisystem und FTP $r = (string) $v . " Byte"; } // end if return $r;
$size = filesize(“news.txt”); Die Funktion GetRealVolume() nutzt die Potenzfunktion pow, um die Grenzen für die Umrechnung zu erkennen. Zuerst werden alle Daten größer als 1 KByte (210) erkannt:
if ($v > pow(2,10)) Dann erfolgt noch mal die Unterscheidung für 1 MByte (220):
if ($v > pow(2,20)) Um den Wert in MB darzustellen, wird dieser durch 220 dividiert und dann wird » MB« angehängt.
$r = (integer)($v / pow(2,20)); $r .= " MB"; Ebenso wird mit KByte verfahren:
$r = (integer)($v / pow(2,10)); $r .= " KB"; Alle anderen Fälle belassen den Wert und hängen die Zeichenkette »Byte« an:
} else { $r = (string) $v . " Byte"; } // end if Der Wert wird dann zurückgegeben:
return $r; Statusinformationen aktualisieren
Die Statusinformationen werden von PHP in einem internen Cache gehalten. Wenn Sie eine Datei mehrfach abfragen, wird sich die Antwort nicht ändern, auch wenn sich die Dateieigenschaften geändert haben. Sie können diesen internen Puffer mit der folgenden Funktion löschen und dann die Eigenschaften erneut abfragen:
clearstatcache(); Wenn Sie damit rechnen können, dass sich die Eigenschaften während der Ausführung eines Skripts nicht ändern, sollten Sie diesen Puffer in Anspruch nehmen. Dateizugriffe benötigen einen erheblichen Teil der Systemleistung, vor allem wenn viele Nutzer parallel zugreifen. Denken Sie bei der Auswahl eines Lösungsweges daran, dass sich Dateien zum Speichern von Daten nur bedingt eignen. Schneller sind Datenbanken, die oft über ausgefeilte Puffermechanismen verfügen.
Um mit Dateien effektiv arbeiten zu können, sind Funktionen zum Verschieben, Kopieren, Löschen und Umbenennen notwendig. Diese Funktionen werden nachfolgend vorgestellt. Dateien kopieren Die einfachen Dateifunktionen setzen voraus, dass Sie genau wissen, wie copy die zu behandelnden Dateien heißen. Pfadangaben können nach Bedarf eingesetzt werden. Das folgende Skript legt zu jeder Datei eine Sicherheitskopie an und ändert dabei die Dateierweiterung. Als Datenquelle werden die in Listing 8.9 erzeugten Formulardateien genutzt.
"; } } closedir($dp); ?>
Listing 8.13: Kopieren einer Datei mit Umbenennung (file_copy)
Wenn Sie das Skript ausführen, benötigen Sie Schreibrechte für den Webnutzer im Verzeichnis /DATA. Falls die vorangegangenen Beispiele funktionierten, sollte dies bereits eingestellt sein. Sie können auch ein anderes Verzeichnis mit Hilfe der Variablen $dir konfigurieren. Das Skript durchläuft ein Verzeichnis, das zuvor mit opendir geöffnet wurde:
$dp = opendir($dir); while ($file = readdir($dp)) Dann wird mit Hilfe eines regulären Ausdrucks festgestellt, ob es ein Verzeichnisstammeintrag (».« für das aktuelle Verzeichnis oder »..« für das übergeordnete) oder eine bereits gesicherte Datei ist:
if (!preg_match('/(\.{1,2}|\.bak)$/', $file)) Informationen zu regulären Ausdrücken und der Funktion preg_match Was ist finden Sie im Abschnitt 13.3 Suchexperten: Reguläre Ausdrücke ab Seite preg_match? 323. Für alle anderen Dateien erfolgt nun der Kopiervorgang, verbunden mit der Umbenennung durch Anhängen der Dateierweiterung .BAK.
copy("$dir/$file", "$dir/$file.bak"); Die Funktion copy gibt TRUE zurück, wenn der Kopiervorgang erfolgreich war. Andernfalls wird FALSE zurückgegeben. PHP gibt außerdem eine
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
180 ________________________________ 8 Daten bewegen: Dateisystem und FTP Warnung aus, die Sie unterdrücken können, indem Sie ein @ vor den Funktionsaufruf stellen:
@copy("$dir/$file", "$dir/$file.bak"); Beim copy-Befehl sind Platzhalterzeichen leider nicht erlaubt. Hinweise, wie Sie mit ganzen Verzeichnissen umgehen, finden sie deshalb im nächsten Abschnitt. Umbenennen
Die Funktion rename benennt Dateien um. Ein anderer Einsatz ist das Verschieben von Dateien, denn rename kann ebenso wie copy mit vollständigen und auch relativen Pfaden umgehen. Stimmt der Pfad von Quelle und Ziel nicht überein, wird die Datei verschoben, auch wenn der Dateiname gleich bleibt. Das folgende Skript holt den letzten Stand der Dateien aus der Sicherung wieder zurück. Dazu werden zuerst alle Dateien gelöscht, für die eine Sicherung existiert. Dann werden die Dateinamen umbenannt.
Listing 8.14: Umbenennen und Löschen von Dateien (file_unlink)
"; } } } closedir($dp); ?> Bedenken Sie hier auch, dass der Nutzer im Zielverzeichnis Schreibrechte benötigt. Wie bereits zuvor gezeigt, wird das gesamte Verzeichnis durchlaufen. Nun werden nur die Dateien herausgefiltert, zu denen eine Sicherungskopie existiert:
if (preg_match('/\.bak$/', $file)) Falls dazu ein Original vorhanden war, kann der Umbenennungsvorgang beginnen:
if (file_exists("$dir/$file")) Dieser Prozess besteht aus zwei Schritten. Zuerst wird das Original gelöscht, dann wird die Sicherungskopie in den Namen des Originals umbenannt:
8.2 Umgang mit Verzeichnissen _________________________________________ 181 Windows-Nutzern mag die Bezeichnung unlink wenig erklärlich sein. Dateien löschen Sie stammt aus dem Unix-Umfeld. Dennoch funktioniert sie auch unter Windows ohne Probleme.
8.2
Umgang mit Verzeichnissen
Hier und im Folgenden wird immer von Verzeichnissen gesprochen, um über einheitliche Termini für alle Plattformen zu verfügen. Unter Windows sind damit »Ordner« (engl. folder) gemeint.
8.2.1
Basisfunktionen
Basisfunktionen für den Verzeichniszugriff sind dir und mkdir. Klassen und Funktionen Wenn Sie objektorientierte Funktionen mögen, können Sie die virtuelle dir Klasse dir verwenden, um auf Verzeichnisse in Form von Eigenschaften und Methoden zuzugreifen. Andernfalls stehen auch klassische Funktionen zur Verfügung. Verzeichnisse anlegen und löschen Zum Anlegen wird mkdir verwendet, gelöscht wird dagegen mit rmdir. Die Funktionen erwarten jeweils einen Verzeichnisnamen mit oder ohne Pfad dahin. Die Operation nimmt keinen Bezug auf andere Vorgänge mit Dateien, deshalb wird hier auch kein Verzeichnis- oder Dateihandle benötigt.
8.2.2
Im Verzeichnissystem bewegen
Um sich im Verzeichnissystem bewegen zu können, reichen die Verzeichnisfunktionen aus. Die Anwendung ist dennoch nicht trivial, denn es sind viele Sonderfälle zu betrachten, wenn man sich im Verzeichnisbaum frei bewegt. Vorab zwei Besonderheiten. • Beim Durchlaufen eines Verzeichnisses werden immer auch zwei »Verzeichnisse« ausgegeben, die folgende Bedeutung haben: - ».«. Der alleinstehende Punkt verweist auf das aktuelle Verzeich-
nis. - »..«. Der doppelte Punkt ist ein Alias für das übergeordnete Ver-
zeichnis. Sie können diese Namen angeben, wenn eine Leseoperation ein Ziel benötigt. Sie können diese Namen aber nicht verwenden, um sie zu Löschen, Umzubenennen oder zu Verschieben. • Verzeichnisse werden sequenziell durchsucht, so wie das Betriebssystem die Daten zurückgibt. Weder die Reihenfolge noch die Folge von Dateien oder Verzeichnissen kann beeinflusst werden.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
182 ________________________________ 8 Daten bewegen: Dateisystem und FTP Das folgende Skript liest ein Verzeichnis komplett und bietet Sortiermöglichkeiten. Es basiert auf Arrays – in denen die Verzeichnisinformationen zwischengespeichert werden. Listing 8.15: Verzeichnis auslesen und sortieren (file_dirsort)
"; foreach($directory[0] as $z) echo "$z "; closedir($dp); ?> Der Umgang mit Arrays und den zur Verfügung stehenden Funktionen wurde bereits in Kapitel 5 Komplexe Datenstrukturen: Arrays ab Seite 83 gezeigt. Informationen zum $_GET-Array finden Sie im Abschnitt 9.2 Daten per URL weiterreichen ab Seite 213. Die Überführung in ein Array zeigt der folgende Ausschnitt:
while ($file = readdir($dp)) { $directory[(int) is_dir($file)][] = $file; } Dabei wird ein zweidimensionales Array angelegt. Die erste Dimension enthält nur zwei Elemente, 0 und 1. Die Entscheidung, ob eine Datei unter 0 oder 1 einsortiert wird, trifft die Funktion is_dir, die als Index genutzt wird. Entsprechend ist schon beim Anlegen des Arrays die Trennung von Verzeichnissen und Dateien erfolgt. Der Index der zweiten Dimension bleibt leer – PHP kümmert sich darum und vergibt fortlaufend Ziffern.
8.3 Verbindungen zu Servern ___________________________________________ 183 Abbildung 8.5: Ausgabe einer sortierten Verzeichnisliste
8.3
Verbindungen zu Servern
Daten können nicht nur von der lokalen Festplatte des Webservers gelesen werden. Dieselben Funktionen eignen sich auch für den Zugriff auf entfernte Server per HTTP und FTP.
8.3.1
HTTP-Verbindungen
Um Daten per HTTP oder FTP zu laden, sind keine besonderen Befehle file_get_content nötig. Die Funktion fopen kann als Dateiname auch ein vollständig qualifizierten URL verarbeiten. Die betreffende Seite wird geöffnet und kann mit den üblichen Zugriffsmethoden gelesen werden. Meist wird jedoch die Datei als Zeichenkette benötigt. Die Funktion file_get_content ist ideal zum Lesen, sie akzeptiert jede URL und gibt den Inhalt der gelesenden Datei als Zeichenkette zurück.
Vorsicht! Copyright! Bevor Sie einfach auf fremde Server zugreifen, informieren Sie sich über bestehende Copyrights und andere Rechte der Sitebetreiber. Am sichersten ist es, sich eine schriftliche Genehmigung einzuholen. Das »Absaugen« fremder Inhalte ist generell kritisch.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
184 ________________________________ 8 Daten bewegen: Dateisystem und FTP Beispiel. Auslesen von Daten bei »Amazon.de« Der Einsatz ist äußerst vielfältig. Sie können so auf sehr einfache Art und Weise Inhalte aus fremden Seiten auslesen. Das folgende Beispiel zeigt, wie Sie nach Eingabe einer ISBN-Nummer die aktuelle Platzierung des Titels bei Amazon ermitteln können. Listing 8.16: Fremde Inhalte per HTTP auslesen (amazon)
FORM; ?> Dieses Skript verwendet Formulare. Informationen dazu enthält Abschnitt 9.1 Daten aus einem Formular ermitteln ab Seite 197. Parameterisierte Anfragen dieser Art sind kritisch, weil Sie nicht mit überschaubarem Aufwand jeden Fehlerzustand abfangen können. So reagiert Amazon auf eine nicht existente ISBN mit einer Weiterleitung zu einer Fehlerseite. Eine Weiterleitung (Redirect) unterstützt der HTTPWrapper von PHP nicht. Im Ergebnis wird eine leere Seite zurückgege-
8.3 Verbindungen zu Servern ___________________________________________ 185 ben, was mit file_get_contents aber kein Problem darstellt. Allerdings kann der Zugriff aus anderen Gründen (Leitung, Server, Provider, ...) misslingen. Deshalb werden Fehlermeldungen mit dem Fehleroperator @ unterdrückt. Eingabefehler kompensiert der reguläre Ausdruck am Anfang. preg_replace entfernt hier alle Zeichen, die nicht Ziffern oder das Zeichen »X« sind.
$isbn = preg_replace('/[^0-9]|X/i', '', $isbn); if (preg_match('|\d{10}|', $isbn)) Informationen zu regulären Ausdrücken und der Funktion preg_replace Was ist finden Sie Abschnitt 13.3 Suchexperten: Reguläre Ausdrücke ab Seite preg_replace? 323. Dann wird die URL eingelesen und in einer Zeichenkette gespeichert:
$strSite = @file_get_contents($url); Die Funktion strstr sucht das Vorkommen der Zeichenkette »Verkaufsrang« und gibt die Zeichenkette $strSite ab dem Fundort zurück.
$strSite = strstr($strSite, 'Verkaufsrang'); Wurde diese Stelle gefunden, holt ein regulärer Ausdruck die Platzierung aus dem Rest der Zeichenkette.
if (preg_match('|<[^>]+>([0-9.]{1,8})[^>]+>|U' , $strSite, $arrResult)) In der Seite6 sieht eine solche Platzierung folgendermaßen aus:
Amazon.de Verkaufsrang 93 Der fett gedruckte Teil ist der Beginn der Zeichenkette in $strSite. Der Ausdruck ermittelt den Inhalt des ersten HTML-Tags, das folgt – funktioniert also unabhängig davon, ob Amazon irgendwann die Farbe ändert oder <span> statt verwendet. Erkannt werden zwischen den Tags Ziffern und der Punkt (Tausendertrennung). Abbildung 8.6: Mögliche Ausgabe des Skripts
Beachten Sie, dass der Zugriff einige Zeit in Anspruch nehmen kann, abhängig von der Verbindung des PHP-Servers zu Amazon, der Seitengröße und der Reaktionsgeschwindigkeit des Amazon-Servers. Eine Besonderheit ist noch für die Schreibweise der URL anzumerken. Schreibweise des Diese muss, konform zu der entsprechenden RFC, mit einem schließen- URL 6
Amazon-Version von Februar 2004
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
186 ________________________________ 8 Daten bewegen: Dateisystem und FTP den »/« geschrieben werden. Andernfalls funktioniert es nicht. Im Browser müssen Sie dass nicht, weil der das ergänzt. Falls der Inhalt einer Seite nicht als Zeichenkette, sondern als Array benötigt wird, nutzen Sie die Funktion file (statt file_get_content). Andere Wrapper-Zeichenketten Die Dateifunktionen nutzen das neue Stream-Modul in PHP5 (eingeführt mit PHP 4.3). Dies erlaubt den Zugriff auf entfernte Server mit den folgenden Protokollen: • file:// Dateizugriff. Dies ist der Standardwert und die Angabe kann deshalb entfallen. kann beispielsweise /home/users/docs.txt sein. • http:// HTTP-Zugriff auf normale Webserver. kann beispielsweise www.comzept.de sein. • https:// Zugriff per SSL auf geschützte Websites. • ftp:// • ftps:// Zugriff auf FTP mit und ohne Verschlüsselung. Als kann beispielsweise mit der Syntax user:[email protected] auch der Zugriff auf geschützte Server erfolgen. Speziell für FTP gibt es auch ein eigenes Funktionsmodul, dass nachfolgende vorgestellt wird.
8.3.2
FTP-Verbindungen mit Dateifunktionen
Ebenso wie mit dem Zugriff per HTTP funktioniert es auch mit FTP. Dabei besteht die Problematik oft in der Notwendigkeit, Kennwort und Name zu übermitteln. Dafür gibt es eine besondere Form der URL, die jeder FTP-Server verstehen kann. FTP-Adresse
HTTP-Server hingegen werden folgendermaßen angesprochen:
http://server.domain.tld:port/pfad/datei.ext/ Andere Protokolle können Sie hier nicht verwenden. Die Portangabe kann entfallen, wenn es sich um die Standardports handelt. Für HTTP ist der Standardport 80, für FTP ist es 21. Zugriff auf andere Ressourcen PHP ist gut geeignet, auf alle Ressourcen eines Servers zugreifen zu können. Sicher kennen Sie die vielen Free-E-Mail-Angebote im Internet, die
8.4 FTP-Funktionen____________________________________________________ 187 oft Browser-basierte Schnittstellen bereitstellen. Der Vorteil liegt auf der Hand. Sie können Ihre E-Mail von jedem Punkt aus nur mit einem Browser lesen. Damit sind Sie unabhängig von der Bereitstellung eines entsprechenden E-Mail-Clients. Die üblichen Protokolle für E-Mail sind POP3 und IMAP4. Am weitesten POP3 abfragen verbreitet ist POP3. Entsprechend groß ist auch das Angebot an POP3Servern. Die per SMTP empfangene E-Mail wird dann den Clients per POP3 zum Download zur Verfügung gestellt. Die Umsetzung auf den Browser geschieht sinnvollerweise im Server. Als Ausgangspunkt für derlei Versuche eignen sich die Stream-Funktion Wie es weiter aus dem Modul stream sowie fsockopen als »Urfunktion« der Server- geht... zugriffsmethoden. fsockopen ist jedoch verhältnismäßig primitiv und baut eine reine Socket-Kommunikation (also auf TCP-Ebene) auf. Für den nötige Protokollablauf müssen Sie selbst sorgen. Das heißt in der Praxis, dass Sie ausgezeichnete Kenntnisse der verwendeten Protokoll, beispielsweise POP3 oder SMTP benötigen. An dieser Stelle sei auf das Buch »PHP 5 – Grundlagen und Profiwissen« vom Autor dieses Buches verwiesen, dass ab Mai 2004 bei Hanser erhältlich ist und diese Themen tiefgehend behandelt. Um den Zugriff so einfach wie möglich zu gestalten und Bibliotheksentwicklern weitere Werkzeuge an die Hand zu geben, besteht in PHP5 die Möglichkeit, eigene Protokolle zu »erfinden«. Das sieht dann folgendermaßen aus:
fopen(“mysql://localhost:3306/select/fromlist/table/where”); Dazu muss man freilich eine Klasse entwickeln, die die Anfrage verarbeitet und Zeichenketten so zurückgibt, dass der Zugriff mit den Dateifunktionen auf den von fopen erzeugten Handler gelingen kann.
8.4
FTP-Funktionen
Eine ganze Reihe von Funktionen dient dazu, speziell für die Verbindung mit FTP-Servern eingesetzt zu werden. Die Funktionsliste zeigt eindrucksvoll die Möglichkeiten, in diesem Abschnitt wird das Prinzip anhand einiger Beispiele erläutert.
8.4.1
Verbindung zu FTP-Servern
Die Funktion ftp_connect gibt ein Handle der Verbindung zurück. Mit diesem Handle arbeiten alle anderen Funktionen. Handles sind Verweise auf Verbindungen.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
188 ________________________________ 8 Daten bewegen: Dateisystem und FTP
Ersetzen Sie in allen folgenden Skripten die Zeichenfolge durch den Namen oder die IP-Adresse Ihres FTPServers, beispielsweise beim Provider. Wenn der FTP-Server eine Anmeldung verlangt, ändern Sie auch die folgenden beiden Zeilen (sind in jedem Skript vorhanden):
$name = "Administrator"; $pass = "unknown";
Der anonyme Zugriff auf FTP-Server FTP-Server können entweder anonyme Verbindungen akzeptieren oder Name und Kennwort verlangen. Anonyme Server dienen meist als reine Downloadserver und verfügen nicht über Schreibrechte. Das folgende Skript zeigt, wie Sie eine Verbindung zu einem FTP-Server aufbauen. Listing 8.17: Verbindung zu einem FTP-Server herstellen (ftp_open)
"; $open = ftp_connect($host); if ($open) echo "FTP-Server $host gefunden"; ?> Das Skript definiert zuerst eine FTP-Adresse. Die im Browser übliche Protokollangabe »ftp://« kann hier entfallen.
$host = ""; Dann wird die Verbindung geöffnet und – wenn dies erfolgreich war – ein Handle darauf zurückgegeben.
$open = ftp_connect($host); Das Handle ist entweder FALSE oder ein ganzzahliger Wert, den PHP als TRUE interpretiert.
if ($open) echo "FTP-Server $host gefunden"; Die folgende Ausgabe ist zu sehen, wenn der FTP-Server erwartungsgemäß reagiert hat: Abbildung 8.7: Ausgabe des Skripts aus Listing 8.17 im Erfolgsfall
Das nun existierende Handle – im letzten Beispiel in der Variablen $open – wird in allen folgenden Beispielen weiter verwendet. Beachten Sie, dass der Zugriff in Abhängigkeit vom FTP-Server und der Verbindung einige Zeit benötigt. Vor allem wenn Sie zu Hause entwickeln und nur per ISDN mit dem Internet verbunden sind, kann das Öffnen eines Servers im Internet spürbar dauern.
Handlungsanleitungen in Form von Handles Im Buch wird sehr oft der Begriff »Handles« genutzt. Generell handelt es sich dabei um eine Referenz (Verweis) auf eine bestimmte Verbindung, beispielsweise zu einem anderen Server.
Zugriff auf geschützte Seiten Oft ist für die Verbindung mit einem FTP-Server die Angabe eines Benutzernamens und Kennwortes erforderlich. Das folgende Beispiel zeigt, wie dies mit PHP möglich ist.
"; $name = "Administrator"; $pass = "clemens"; $open = ftp_connect($host); if ($open) { echo "Verbindung zu $host hergestellt. "; $logged = ftp_login($open, $name, $pass); if ($logged) { echo "Anmeldung erfolgt
"; } } ?>
Listing 8.18: Zugriff auf FTPServer mit Name und Kennwort (ftp_login)
Die Verbindungsaufnahme erfolgt, wie schon zuvor gezeigt. Dann erfolgt eine Anmeldung an den FTP-Server. Der FTP-Server lässt Verbindungen zu geschützten Seiten erst nach einer Authentifizierung zu.
$logged = ftp_login($open, $name, $pass); Diese Anmeldung kann jederzeit erfolgen, muss als zeitlich nicht zwingend der Verbindungsaufnahme folgen. Dies ist im FTP begründet, denn die Initiative zur Übertragung der Anmeldung ergreift der Client. Die Ausgabe in Abbildung 8.8 erfolgt, wenn der Verbindungsversuch erfolgreich war. Abbildung 8.8: Erfolgreiche Ausführung des Skripts aus Dieses Beispiel zeigt, wie die Verbindung komplett hergestellt wird. Alle Listing 8.18 anderen FTP-Funktionen benötigen diese Eröffnung der Verbindung oder die verkürzte, in Listing 8.18 gezeigt Version.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
190 ________________________________ 8 Daten bewegen: Dateisystem und FTP
8.4.2
Navigation im Verzeichnisbaum
FTP-Server sind wie lokale Verzeichnissysteme organisiert. Sie benötigen also Funktionen, mit denen Sie sich im Verzeichnisbaum bewegen können. Das folgende Skript versucht vom angewählten Stammverzeichnis »wwwroot« aus das übergeordnete Verzeichnis zu erreichen. Listing 8.19: Navigation auf FTP-Servern (ftp_cdup)
"; ftp_cdup($open); echo ftp_pwd($open); ?> Dieses Skript setzt voraus, dass $open bereits ein gültiges Handle enthält, die Verbindung also steht. Falls die Site geschützt ist, muss außerdem die Anmeldung mit ftp_login erfolgt sein. Zuerst wird das aktuelle Verzeichnis gewechselt. Falls die Funktion ftp_chdir FALSE zurückgibt, bricht das Skript mit einer Fehlerausgabe ab:
@ftp_chdir($open, "wwwroot") or die("Fehler bei chdir"); Läuft das Skript weiter, wird das neue Verzeichnis ausgegeben:
echo ftp_pwd($open); echo " "; Dann erfolgt ein Wechsel in das nächst höhere Verzeichnis:
ftp_cdup($open); echo ftp_pwd($open); Die Ausgabe erscheint in der gezeigten Form nur, wenn das übergeordnete Verzeichnis tatsächlich existiert. Man muss allerdings beachten, dass der durchsuchbare Verzeichnisbaum dem des angebotenen virtuellen Servers entspricht. Das gezeigt Root-Verzeichnis muss nicht (und darf eigentlich auch nicht) die Root des Servers sein. Abbildung 8.9: Navigation im Verzeichnisbaum
Navigationen in fremden Verzeichnissystemen können misslingen. Sie sollten deshalb die Fehlerausgaben mit dem @-Operator unterdrücken, wie es im letzten Listung gezeigt wurde.
8.4 FTP-Funktionen____________________________________________________ 191 Erzeugen und Entfernen eines Verzeichnisses Wenn entsprechende Rechte auf einem FTP-Server gegeben sind, können Sie auch Verzeichnisse anlegen und sich darin bewegen, Dateien hineinkopieren und wieder löschen.
Listing 8.20: Erzeugen und Wechseln von Verzeichnissen (ftp_chdir)
Dieses Skript erzeugt ein neues Verzeichnis, wechselt dann dorthinein und gibt den Namen des nun aktuellen Verzeichnisses aus. Ebenso ist natürlich das Löschen möglich. Dazu müssen Sie sicherstellen, dass das zu löschende Verzeichnis das aktuelle ist. Das nächste Skript setzt dafür die ftp_cdup-Funktion ein.
Listing 8.21: Löschen von Verzeichnissen auf einem FTPServer (ftp_rmdir)
Die ftp_cdup-Funktion stellt sicher, dass das Verzeichnis nicht mehr das aktuelle ist. Fehlt sie, würde das zu der in Abbildung 8.10 gezeigten Fehlermeldung führen. Ist das Verzeichnis vorhanden und stimmen die Zugriffsrechte, wird es mit ftp_rmdir entfernt. Zuvor sollten Sie sicherstellen, dass es leer ist. Auch bei diesen Funktionen ist der Fehleroperator @ sinnvoll. Die Skripte Was bedeutet müssen dann natürlich um eine eigene Fehlerbehandlung ergänzt wer- das @-Zeichen? den. Abbildung 8.10: Fehlerausschrift, wenn das aktuelle Verzeichnis gelöscht wird
Anzeige von Verzeichnisdaten Ist das richtige Verzeichnis auf dem FTP-Server erreicht, kann eine Dateiliste helfen, die richtige Datei auszuwählen. Der Test auf Eigenschaften ist nicht ganz einfach, weil verschiedene FTP-Server hier nicht einheitlich arbeiten. Das folgende Skript zeigt, wie man es auf einem zwar primitiven, aber vergleichsweise sicheren Weg ausführen kann. Wegen der vielen Zugriffe – jeder Name führt zu einem meist nicht erfolgreichen
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
192 ________________________________ 8 Daten bewegen: Dateisystem und FTP chdir per FTP – kann das Skript einige Zeit in Anspruch nehmen und auch etwas Bandbreite benötigen. Listing 8.22: Ausgabe einer Verzeichnisliste mit FTP (ftp_nlist)
Verzeichnisse
'; foreach($slist[0] as $file) echo $file[count($file)-1] . " "; echo 'Dateien'; foreach($slist[1] as $file) echo $file[count($file)-1] . " "; ?> Nach der bereits gezeigten Verbindungsaufnahme wird eine umfassende Verzeichnisinformation abgerufen:
$nlist = ftp_rawlist($open, ftp_pwd($open)); $nlist enthält jetzt ein Array, dass für jede Datei bzw. jedes Verzeichnis einen Eintrag enthält. Dieses Array wir durchlaufen:
foreach($nlist as $file) Das erste Zeichen enthält den Buchstaben »d«, wenn es sich um ein Verzeichnis handelt. Der erste Teil der Zeile ist eine Angabe der Art:
tuuugggooo Dabei steht »t« für den Typ (d für Verzeichnis, - für Datei oder l für Link). Die Dreiergruppen bilden die Zugriffsrechte ab (u für User, g für Group, o für Other). Zur Unterscheidung von Dateien und Verzeichnissen reicht also das erste Zeichen. Entsprechend dieser Angabe werden die Zeilen in ein zweites Array $slist überführt.
if ($file[0] == 'd') Dieses zweidimensionale Array enthält nun in der ersten Dimension die Unterscheidung Datei und Verzeichnis, in der zweiten dagegen ein weiteres Array, dass die Felder der Einträge speichert. Die Felder sind durch
8.4 FTP-Funktionen____________________________________________________ 193 Leerzeichen getrennt. Die Aufsplittung wird deshalb mit explode vorgenommen:
$slist[0][] = explode(' ', $file); } else { $slist[1][] = explode(' ', $file); Jetzt werden die beiden Arrays behandelt: Index 1 enthält die Dateien – diese werden aufsteigend sortiert:
asort ($slist[1][count($slist[1])-1]); Index 0 enthält die Verzeichnisse – diese werden absteigend sortiert:
rsort ($slist[0][count($slist[0])-1]); Jetzt folgen einige Formatierungen der Seite mit HTML und die Ausgabe der beide Arrays mit foreach.
echo 'Verzeichnisse'; foreach($slist[0] as $file) echo $file[count($file)-1] . " "; echo 'Dateien'; foreach($slist[1] as $file) echo $file[count($file)-1] . " "; Die Ausgabe zeigt Verzeichnisse und Dateien an, sortiert und getrennt nach dem Typ, zeigt Abbildung 8.11. Abbildung 8.11: Anzeige der Dateien und Verzeichnisse eines FTP-Servers
In der Referenz finden Sie weitere Beispiele und eine Auflistung aller Optionen. Sie können sich natürlich auch mit einem weiteren foreach alle Elemente ausgeben lassen. Außer dem Namen wird der Eigentümer, die Größe und das Datum angezeigt.
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
194 ________________________________ 8 Daten bewegen: Dateisystem und FTP In einigen Fällen kann auch eine Unterscheidung des Betriebssystems notwendig werden. Dann hilft ftp_systype; die Funktion erkennt den Basistyp des Systems (beispielsweise »Unix«). Das letzte Listing, dass die Funktion ftp_rawlist verwendete, funktionierte mit der Version PHP5.0.0RC1 nicht unter Windows. Der Fehler ist bereits aus PHP4 bekannt, wo seit 4.0.6 der Einsatz nur unter Unix möglich war. Erstaunlich, dass sich dieses Verhalten auch mit PHP5 nicht gebessert hat, obwohl der Bug bekannt ist. Das Bildschirmfoto für die Abbildung wurde unter Linux gemacht, damit der erhoffte Effekt wenigstens gezeigt werden kann.
9 Daten aus dem Browser: Formulare und Links Formulare sind der Schlüssel zu interaktiven Webseiten. Der Nutzer wird in die Lage versetzt, Daten einzugeben, und der Server kann auf diese Daten in vielfältiger Weise reagieren. Die Darstellung der Formulare erfolgt mit Hilfe dafür vorhandener HTMLTags, den Formular-Elementen. Die Übertragung der Daten zum Server übernimmt HTTP mit den Methoden POST oder GET.
9
Daten aus dem Browser: Formulare und Links
9.1 Daten aus einem Formular ermitteln __________________________________ 197
9.1
Daten aus einem Formular ermitteln
Formulare werden enorm leistungsfähig, wenn es gelingt, die Daten in einem Skript weiterzuverarbeiten. Wie dies erfolgt, finden Sie in diesem Abschnitt.
9.1.1
Formulare in HTML erstellen
HTML-Formulare sind der einzige praktikable Weg, wie Daten vom HTML-Formulare Browser zum Server und damit zu Ihrem Skript gelangen können. Ein Formular besteht aus dem Das einfache Formular aus Listing 9.1 sendet eine E-Mail. Der gesamte Prozess läuft allerdings clientseitig ab. Dabei erkennt der Browser die Zeichenfolge mailto: und ruft das lokal installierte E-Mail-Programm auf. Ohne ein solches Programm funktioniert das Skript nicht. Eleganter ist natürlich ein vollständig im Server ablaufender Prozess.
7
Mithin funktioniert die Sache nur, wenn der Browser mailto: kennt.
Listing 9.1: Einfaches Formular in HTML, noch ohne PHP
V V V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 A B C D E
198 __________________________ 9 Daten aus dem Browser: Formulare und Links
Ausflug in die Welt von HTTP Wenn ein Formular abgesendet wird, teilt der Browser dies dem Server mit einem speziellen HTTP-Kommando mit: POST. Der Webserver weis dann, dass im Körper der Anforderung weitere Informationen stehen – die Formulardaten. Diese erscheinen dort als zeilenweise Liste, bestehend aus dem Feldnamen, einem, Gleichheitszeichen und den Felddaten.
E-Mail per Skript versenden In solchen Fällen ist ein Skript auf dem Server notwendig, das unabhängig vom Browser und dessen Installationsumgebung E-Mails versenden kann. Praktisch ist dazu im Formular nur eine Zeile auszutauschen:
FORM; } // if-else ?> Das Beispiel mag auf den ersten Blick etwas unübersichtlich erscheinen. Analysieren Sie die Funktion schrittweise: Das Skript ruft sich selbst auf, der eigene Name wird PHP_SELF entnommen8: