copyright (c)  2003   useGroup
PHP

Jochen Stärk
PHP-Tutorial
Anhang C: Druckversion

nach untenDruckversion

Druckversion

nach obenDruckversion 

(PHP-Tutorial)

copyright (c)  2003   useGroup

Download

nach obenDownload 

Aktuelle Version: 3.0.3 vom 25.07.2007


Lizenz

nach obenLizenz 

                     Copyright (c) 2001-2003 usegroup.
                     Permission is granted to copy, distribute and/or modify this document
                     under the terms of the GNU Free Documentation License, Version 1.1
                     or any later version published by the Free Software Foundation;
                     with the Invariant Sections being Über diesen Artikel, with no
                     Front-Cover Texts, and with no Back-Cover Texts.
                     A copy of the license is included in the section entitled
                     
                     "GNU Free Documentation License".
                     
                  


Sie wollen dieses Tutorial weiter entwickeln ? Prima ! Tipps dazu gibt's hier.


Nutzbarkeit

nach obenNutzbarkeit 

Mein pers�nliches Ziel f�r diesen Artikel war es, eine nutzbaren Einstieg in PHP zu vermitteln. Auch Programmierer, die schon unter PHP gearbeitet haben, sollten n�tzliche Informationen finden und immer wieder nachschlagen k�nnen. Ob mir das gelungen ist entscheidet wie immer der Leser.



Entsprechend ist dieses Dokument auch keine �bersetzung des PHP-Manuals, sondern eine Erg�nzung. Teilweise �berlappen sich Gebiete (wie z.B. die bei mir unvollst�ndige ODBC-Referenz), jedoch habe ich den Text gr��tenteils selbst geschrieben ohne mich bewusst an das PHP-Manual halten zu wollen. Die Teile Variablen und Arrays hat mir netterweise Christoph Hanser abgenommen, au�erdem danke ich meinem Bruder Thorsten f�r sehr viel M�he beim Testlesen :).



Auch die Selektion der Themen unterliegt dem Grundsatz der N�tzlichkeit. Eine (unvollst�ndige)SQL-Referenz ist zwar nicht Thema einer PHP-Referenz, wohl aber das eines Einf�hrungsartikels. Auch Anwendungsbeispiele und der Aufbau eines Testsystems sind Themen die im PHP-Manual nicht oder nur teilweise angeschnitten wurden (z.B. halte ich die dortige "Installation on Windows 95/98 Systems" f�r zu kurz und ohne die Erw�hnung von ODBC f�r nicht allgemein genug).


Must-Read

nach obenMust-Read 

Dennoch ist das PHP-Manual eine komplette PHP-Referenz und ein eindeutiges must-read. Auch der Umfang des Manuals ist bei weitem gr��er als der dieser Einf�hrung - ein Arbeitsaufwand, vor dem ich gro�en Respekt habe, nachdem ich f�r einen Bruchteil der Seiten des PHP-Manuals oder SelfHTML 3 Monate gebraucht habe ;)


Danksagung

nach obenDanksagung 

F�r die freundliche Hilfe bei der Erstellung und Fehlersuche:

Josef G. Knust

Thorsten St�rk (f�r seinen Beitrag Transaktionen unter MySQL)


Was ist das?

nach obenWas ist das? 

PHP (unter Windows) ist ein CGI, d.h. ich habe mit PHP die Möglichkeit, bestimmte Informationen von meinem Server zu laden, um sie dann im Browser des Anwenders auzugeben. Diese Möglichkeiten (im Grunde die Gleichen wie in Perl) sind vielfältig, sie reichen vom "einspielen" der Uhrzeit in die Homepage zu komplexen Datenbankabfragen von Aktienkursen mit einer vom Kurs abhängigen Darstellung des Wertes.

Kurz gesagt halten viele PHP nur für etwas besser als Perl.

Entgegen PERL, das in der Unix-Welt z.B. zum Erstellen von makefiles benutzt wird, ist PHP auf HTML-Seiten spezialisiert. PHP kommt allerdings wie Perl aus der Linux-Ecke und ist kostenlos verfügbar (Siehe: Ein eigenes Testsystem aufbauen).

PHP wird allerdings eingebettet, d.h. dass ich nicht wie in Perl schreibe

print "<html> Sie sind der <b>$i</b>te Besucher </html>";
sondern
<html>
	Sie sind der
	<b>
		<?php
			echo "$i";
		?>
	</b>
	te Besucher.
</html>
Ich habe den Code hier absichtlich eingerückt. Bei ständigen print-Anweisungen wird nämlich der Code i.d.R. auch unübersichtlich. In PHP wird alles, was nicht im PHP-Tag (<?php und ?>) steht, ausgegeben, also wie eine konventionelle HTML-Datei behandelt (siehe: PHP-Code in HTML einbinden ) .



Kommentar

PHP ist meiner Meinung nach der de-facto Nachfolger von PERL im CGI-Bereich, weil PHP eingebettet werden kann und einen größeren und leichter verständlichen Funktionsumfang hat als Perl. PHP ist - wer glaubts - von einem Programmierer geschrieben, aber von einem, der seine eigene Wunschsprache verwirklichen wollte. Man merkt an vielen Stellen in PHP dass man sich Gedanken gemacht hat "wie würde ich es am leichtesten programmieren".


Die Geschichte

nach obenDie Geschichte 

Entwickelt wurde PHP im Herbst 1994 von Rasmus Lerdorf für den privaten Gebrauch. Die erste Version, die auch von anderen benutzt wurde, stammt von Anfang 1995 und nannte sich "Personal Home Page Tools", sie bestand aus einem einfachen Parser ("grammatischer Zerteiler"), der einige spezielle Makros und einige andere "Spielzeuge" wie Gästebücher und Counter "verstand" Mit PHP/FI Version 2 folgte Mitte 1995 ein überarbeiteter Parser. FI kommt von einem zweiten von Lerdorf entwickelten Programmpaket, das HTML-Formulare interpretieren konnte (FI=Form Interpreter), außerdem kam bei der Version 2 SQL-Unterstützung hinzu. Die Idee war gut, das Produkt funktionierte und wurde Open Source. Als Linux-Projekt wurde die Bezeichnung bald rekursiv als (Homepage PreProcessor) Homepage Preprocessor bezeichnet. Mittlerweile arbeitet Lerdorf nicht mehr alleine, sondern hat mehrere Helfer, die zu weiteren Entwicklungen beitragen.

Quelle: http://trumpf-3.rz.uni-mannheim.de/www/sem99s/vogelgesang/php/php-2.htm

PHP4 (Codename Zend, entwickelt von Zeev Suraski und Andi Gutmans, freigegeben am 22.5.2000) war ein komplett neu geschriebener PHP-Parser, der neben besserer Performance (5- bis 200fache Performance-Steigerungen im Vergleich zu PHP 3.0; Quelle: http://www.heise.de/ix/artikel/2000/07/052/ ) auch mehr Funktionen bietet, auffällige Veränderungen waren z.b. die Umwandelung aller Arrays in Listen (in PHP4 ist es z.b. möglich, Elemente an Arrays anzuhängen oder wegzunehmen) oder die Session-Bibliothek (PHP4 kann eindeutige Session-IDs erzeugen und verwalten, um bessere Kontrolle darüber zu haben, welcher User auf welche Seite zugegriffen hat).

Objektorientierung war zwar in PHP 4 schon möglich, diese war jedoch extrem langsam. PHP 5, das auf Zend 2 basiert, bringt endlich eine brauchbare Objektorientierung mit zusätzlichen Features wie Mehrfachvererbung durch Interfaces und die Unterstützung der SQLITE-Datenbank.


Wachstum

nach obenWachstum 

Die usage-Stats ( http://www.php.net/usage.php) zeigten für September 2000 (Version 1 dieses Dokuments) runde 600000 IP-Adressen und über 3,5 Millionen Domains, die PHP einsetzen. Version 2 zeigte fast eine Million IP-Adressen und sechs Millionen Domains, die Version 2.1 über eine Million IPs und über 13 Millionen Domänen, Version 3 über 14 Millionen Domänen.


Im Funktionsumfang

nach obenIm Funktionsumfang 

PHP ist nicht auf Listenoperationen spezialisiert wie PERL und kommt auch nur im Homepage-Bereich zum tragen. Seiten sind in PHP in der Regel leicht zu schreiben, PHP ist leicht zu erlernen und bietet einen großen Funktionsumfang. Unterstützt werden in dieser Sprache zum Beispiel Datenbankzugriff (Adabas D, dBase, FilePro, Interbase, mSQL, MySQL, Informix, ODBC, Oracle, Sybase, Solid, Velocis, Unix dbm, PostgreSQL), wofür man in fast allen anderen Sprachen erst einmal nach einer Bibliothek suchen muss die diese Funktionen bietet. PHP überrascht oft durch seine "Einfachheit". übergebene Parameter werden so z.B. automatisch in nach dem Parameternamen benannte Variablen geladen. All das macht das Programmieren sehr einfach, auch größere Projekte werden sehr schnell realisiert und bringen den schnellen Erfolg zurück ins Programmierstübchen.


Im Sprachaufbau

nach obenIm Sprachaufbau 

Zum Sprachaufbau ist zu sagen dass in der Regel für alle Arbeiten auf reguläre Ausdrücke verzichtet werden kann. Sie können benutzt werden aber für alle Anwendungsgebiete gibt es ähnlich gute, leichter zu verstehende Funktionen. Auch bemüht sich die Sprache sichtlich, um Verständlichkeit und bietet teilweise (z.B. im logischen AND) sowohl Pascal (AND) als auch die Möglichkeit, C (&&) - Syntax zu benutzen.


Geschwindigkeit

nach obenGeschwindigkeit 

Ich halte PHP für ziemlich schnell zu lernen, ziemlich schnell zu schreiben und ziemlich schnell in der Ausführung. Zumindest unter Linux hängt PHP Perl i.d.R. um Längen ab (Unter Windows-Apache wird PHP nicht als Modul genutzt, sondern als EXE, und dadurch wird die Sache langsamer). Auch ASP ist meines Wissens langsamer, von JSP ganz zu schweigen.

Nebenbei mag ich die Übersichtlichkeit von gut-geschriebenen PHP-Code. Nicht zuletzt die Einbettung sorgt dafür, dass auch fremde Programmierer (die evtl. nicht einmal viel Erfahrung mit PHP haben) schneller den Durchblick haben als in vergleichbaren Perl-Programmen. Bei Perl kann man zwar auch schnell "durchsteigen" - aber erst nachdem man es darin wirklich intensiv jahrelang programmiert hat.


Nachteile von PHP

nach obenNachteile von PHP 

PHP hat auch Nachteile:

FastCGI

JSP, ASP und Perl haben Möglichkeiten, die PHP zur Zeit nur mangelhaft hat:

Jede PHP-Seite wird unabhängig übersetzt und ausgeführt, während ich z.B. bei FastCGI sagen kann: Führe schonmal diesen Code aus (z.B. laden einer Datei) wenn der Server gestartet wird. Diese Datei ist dann beim CGI-Aufruf schon im Speicher und dementsprechend schnell kann Sie ausgegeben werden.

Das typische Anwendungsbeispiel lädt jedoch keine Datei, sondern stellt eine Verbindung zu einer Datenbank her, ein Prozess, der oft relativ zeitaufwändig ist. Das kann PHP durch "persistent connections" auch, diese persistent connections wirken jedoch auf den Sprachumfang sehr aufgesetzt und bieten auch nicht die Möglichkeiten von FastCGI (z.B. eine Datei oder Bibliothek zu laden, Objekte zu erzeugen und nach Beenden des Servers die Datein wieder zu schließen etc..).

SOAP

ich habe zwar schon eine PHP-Soap-Implementation gesehen ( http://www.gigaideas.com.cn/phpsoap/), diese erscheinen mir allerdings noch nicht ganz ausgereift.

Verbreitung

PHP ist nicht so verbreitet wie Perl (auch weil es jünger ist). Es gibt weniger Informationen und vor allem unterstützen es weniger Server.

Spezialisierung

Auch ist die Spezialisierung von PHP nicht immer ein Vorteil. Lernt man PHP, so kann man damit nur im Bereich Homepages etwas anfangen, während man z.B. mit Perl auch das ein oder andere Kommandozeilenprogramm schreiben kann z.B. um in Linux Makefiles zusammenzustellen.


"Interpretationssache"

nach oben"Interpretationssache" 

PHP ist ein Interpreter und als solcher hat man niemals die Ausführungsgeschwindigkeit eines Kompilers. Auch wären Änderungen am Funktionsumfang nötig um Kompilierungen überhaupt erst aufkommen zu lassen - z.B. das Ende aller evaluierenden Funktionen, also Funktionen die selbst PHP-Code ausführen können wie include. Die einzige Sprache bei der man von Kompilierung sprechen könnte ist Java (in eine Pseudomaschinensprache die dann wieder interpretiert wird :(), richtig schnelle Lösungen erreicht man nur mit C++, Pascal und anderen, die auch CGI-Funktionen übernehmen können. Allein schon weil auch die Internet-Server (und nicht nur die surfer) plattformunabhängig sein sollen ist das mit kompilierten Sprachen so eine Sache. Allerdings wäre ein (eingeschränkter) PHP-Kompiler für Linux eine feine Sache...


Unter Windows, mit PHPTriad

nach obenUnter Windows, mit PHPTriad 

Es gibt mehrere Projekte, die eine Zusammenstellung von PHP, MySQL und Apache für Windows vorkonfigurieren. Mit dieser Zusammenstellung kann man sich Konfigurationsaufwand sparen, aber z.B. eine neue PHP-Version sind unter Umständen nicht so leicht einzuspielen wie selbst-eingebundene. In PHPTriad (ca. 14MB), geht das aber auch relativ leicht indem man das php-Verzeichnis löscht und dann die Original-PHP-Version von php.net dorthin installiert.


Unter Windows, mit MySQL

nach obenUnter Windows, mit MySQL 

Ohne PHPTriad ist der Konfigurationsaufwand augenfällig - dabei legt PHP dem Konfigurierenden weniger Steine in den Weg als die Zusammenarbeit mit Datenbank, ODBC-Treibern und Webserver. Dennoch lohnt sich die Sache, denn wer mit PHP erstmal richtig loslegt ist wird den Konfigurationsaufwand schnell vergessen haben.


Die benötigten Dateien

nach obenDie benötigten Dateien 

Der Apache-Server für Window (no_src, MSI für Microsoft-Installer), ca. 2MB

PHP 4 für Windows, ca. 1MB

MySQL Standard binary (tarball) distributions; Windows (Intel), ca. 9,5Mb

(optional) MyODBC, ODBC-Treiber für mySQL, ca. 1,7Mb

(Empfehlung) PhpMyAdmin: Tool (in PHP geschrieben), das es erlaubt, neue Tabellen anzulegen, anzusehen, zu löschen u.m., ca. 100kb

(Empfehlung) MySQL Front: in etwa PhpMyAdmin als Exe, allerdings sind die Eingabemöglichkeiten für neue Datensätze besser gelöst, ca. 1Mb


Installationsverlauf

nach obenInstallationsverlauf 

Apache zum Laufen bringen

Installieren Sie den Apache-Server.

Wenn Sie diesen in einer Eingabeaufforderung aufrufen, sollten Sie die Meldung "Apache.Exe: cannot determine local host name" erhalten. Um diesen "Fehler" zu beheben, editieren Sie bitte im Apache\Conf - Verzeichnis die httpd.conf. Suchen Sie nach der Zeile
#ServerName new.host.name
und ändern Sie sie in
ServerName new.host.name
Auf deutsch: machen Sie aus der auskommentierten Zeile wieder eine normale Zeile der Konfigurationsdatei.

Mit der geänderten Konfigurationsdatei lässt sich Apache dann auch starten. (das Fenster mit der Ausgabe Apache/1.3.12 (Win32) running... bitte nicht schließen). Beenden lässt es sich nebenbei entweder mit STRG+C oder (sauberer) mit dem Aufruf von apache -k shutdown (z.b. aus einer anderen Eingabeaufforderungsbox).

PHP zum Laufen bringen

Entpacken Sie PHP z.b. in (ein neu erstelltes) Apache-Unterverzeichnis PHP.

Benennen Sie php.ini-dist um in php.ini und kopieren Sie diese Datei in ihr Windows\System Verzeichnis.

Beachten Sie dabei, dass der "session.save_path" auf dem Verzeichnis /tmp liegt, sollten Sie also kein Verzeichnis C:\tmp haben (gesetzt der Fall Sie installieren in C) können Sie nichts mit Sessions anfangen, so lange Sie es nicht angelegt oder den session save path geändert haben.

Sie können jetzt PHP.exe aufrufen, es sollte ohne Ausgabe einfach nur auf Eingaben warten. Auch PHP kann man mit STRG+C "schießen". PHP muss noch in die Konfigurationsdatei von Apache eingebunden werden, das laufende PHP also bitte wieder beenden.

Suchen Sie in der apache\conf\httpd.conf nach "addtype" und fügen Sie hinter dem auskommentieren AddType die Zeilen ein
  AddType application/x-httpd-php .php
  AddType application/x-httpd-php .php3
Diese Zeile sagt Apache, dass .php-Dateien vom Typ application/x-httpd-php4 sind.

Jetzt brauchen wir noch ein ScriptAlias mit der Zeile
ScriptAlias /php/ "C:/Apache/php/"
das PHP sagt, dass der Pfad C:\Apache\php\ (sofern das Ihr Installationsverzeichnis ist) dateien enthält, die ausgeführt werden müssen, um Skripte zu behandel und den PHP-Typ verknüpfen wir mit der Zeile
Action application/x-httpd-php /php/php.exe
mit PHP. (im Skriptverzeichnis /php/ also in c:\apache\php)

Wir brauchen jetzt schnell eine Testdatei um PHP zu testen, z.b.

<html>
<?
  echo "hello world";
?>
</html>
Als Dateiname wählen wir test.php und speichern es im Apache\htdocs Pfad.

Falls Sie offline sind, erzählen Sie Ihrem Browser jetzt etwas anderes: für den folgenden Aufruf müssen sie nicht zwangsläufig eine Internetverbindung herstellen, auch wenn einige Browser Sie das glaubend machen sollen. Unter dem Internet Explorer entfernen Sie z.b. das Häkchen Offlinebetrieb.

Um Apache zu testen rufen Sie jetzt in Ihrem Web-Browser die Adresse http://localhost auf, es sollte eine Ausgabe kommen, die mit "Es klappt! Der Apache Web-Server ist auf dieser Web-Site installiert! " anfängt. Das ist Ihr Server. Die Adresse http://localhost/test.php sollte dann hello world anzeigen.

Falls das nicht klappt, sind die Apache-Logdateien in Apache\Logs oft eine wertvolle Hilfe (diese werden teilweise erst nach beenden von Apache geschrieben). Denken Sie bitte daran, nach jeder Veränderung in den \conf-Dateien Apache zu beenden und neu zu starten damit die Änderungen wirken können.

Eine "korrekte" conf-datei für Apache 1.3.12 im Verzeichnis c:\temp\apache und PHP 4.02 im Verzeichnis c:\temp\apache\php finden Sie hier.

mySQL zum Laufen bringen

Allerdings ist das nicht der eigentliche Nutzen von PHP, jetzt installieren wir die Datenbank.

Entpacken und installieren Sie mySQL (Am besten benutzen Sie das vorgegebene Verzeichnis c:\mysql\). Sollten Sie in ein anderes Verzeichnis installieren müssen Sie die "basedir" in c:\windows\my.ini anpassen.

Starten Sie winmysqladmin.exe (im Verzeichnis bin) und geben Sie einen Benutzernamen und Passwort ein. Eine grüne Ampel in der Taskleiste zeigt ein laufendes MySQL. Klicken Sie auf die Ampel und auf "Show Me", wählen Sie den Reiter "Databases" und klicken Sie rechts auf den als "Databases" eingezäunten bereich, wählen Sie Create Database und als Namen z.b. testdb.

Damit haben wir ein laufendes PHP auf einem laufenden Webserver mit einer laufenden Datenbank, die wir mit den MySQL-Befehlen in PHP schon ansprechen könnten.


ODBC

nach obenODBC 

ODBC zum Laufen bringen

Jetzt kommt das optionale ODBC (Ansonsten greifen Sie bitte auf die Befehle in der kleinen MySQL-Referenz zurück): entpacken und installieren Sie myODBC. In Start/Einstellungen/Systemsteuerung/ ODBC Datenquellen wählen Sie jetzt bitte hinzufügen, mySQL. Was kommt, sollten Sie ausfüllen wie im Bild rechts.

Windows DSN Name ist die Angabe wie Sie die Datenbank ab jetzt unter ODBC ansprechen können (dieser Namen muss nicht mit mySQL database-name übereinstimmen), User und Passwort richten sich natürlich nach Ihrer mySQL-Installation.

ändern wir jetzt test.php in Richtung ODBC:
<html>
Starting PHP4....
<?php
$dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
echo "Connected to db $dbh<br/>";
$query = "create table dtab (chk integer, chkchr char(20))";
$res = odbc_exec($dbh, $query);
echo "table created<br/>";
$query = "insert into dtab values (42,'stringtotest')";
$res = odbc_exec($dbh, $query);
$query = "select * from dtab";
$res = odbc_exec($dbh, $query);
echo "number of rows:".odbc_num_rows($res)." <br/> ";
for ($i=0; $i<odbc_num_rows($res); $i++) 
{
$result= odbc_fetch_row($res, $i);
$st= odbc_result($res, 'chk');

echo "$i:res=$st <br/>";
}

odbc_close_all();
?>
... PHP4 concluded.
</html>
Die Ausgabe
 Starting PHP4.... Connected to db 1
 table created
 number of rows:1 
 0:res=42 
... PHP4 concluded. 
Zeigt, dass alles funktioniert (Eine Tabelle mit der Spalte Res wird angelegt, eine Zeile mit dem Wert 42 eingefügt und dei gesamte Tabelle selektiert und ausgegeben: Die 0te Zeile enthält tatsächlich den Wert 42 für den Integer Res.). Bitte entfernen Sie nicht winmysqladmin aus dem Autostart, mySQL muss jedemal wenn ein Datenbankzugriff erfolgen soll, gestartet sein, ebenso wie Apache.


Die empfohlenen Dateien installieren

nach obenDie empfohlenen Dateien installieren 

PhpMyAdmin entpacken Sie einfach in ein Unterverzeichnis Ihres htdocs-Verzeichnis Ihres Webservers und rufen die index.php3 im entsprechenden Verzeichnis auf.

MySQLFront ist eine "normales" Programm, entpacken Sie es einfach in das gewünschte Zielverzeichnis.


Das XML-Tutorial

nach obenDas XML-Tutorial 

Das "Schwester-Tutorial" dieses Dokuments mit dem Thema XML/XSLT finden Sie hier: http://www.usegroup.de/software/xmltutorial/.


PHP-Builder

nach obenPHP-Builder 

Es gibt viele gute Foren für PHP, aber eines sticht von der Qualität her denke ich besonders aus der Menge heraus: PHP-Builder. Neben den Foren Foren bietet die Community auch Artikel an.


Die PHP-Website

nach obenDie PHP-Website 

ist http://www.php.net. Dort findet man neben dem PHP-Manual auch aktuelle Informationen.


Das PHP-Manual

nach obenDas PHP-Manual 

Wem das ständige online-gehen auf die Nerven geht der kann auch unter http://www.php.net/docs.php das PHP HTML Manual (Zip file) mit ca. 2MB herunterladen. Es hat zwar den annähernd den gleichen Inhalt wie die Quick-Ref allerdings sind keine Kommentare angehängt.


Maguma Studio

nach obenMaguma Studio 

Maguma Studio liegt neben der kommerziellen Version auch in einr freien Version vor. Es ist meiner Erfahrung nach die einzige kostenlose IDE für PHP, die einen Debugger (in dem Fall DBG) tatsächlich integriert.


DevPHP

nach obenDevPHP 

Dev-PHP ist nicht so knallig bunt wie das Maguma Studio, aber wahrscheinlich die derzeit beste Open-Source-IDE für PHP. Zudem läuft sie auch unter Linux, einziges Manko: Derzeit kann man mit ihr PHP-Scripte noch nicht debuggen.


SQL-Tutorial

nach obenSQL-Tutorial 

Leider habe ich im Netz noch keine gute SQL-Referenz gefunden. Ein gutes Tutorial findet man jedoch unter http://w3.one.net/~jhoffman/sqltut.htm


SQL-Zoo

nach obenSQL-Zoo 

Im SQL-Zoo tummeln sich die unterschiedlichsten RDBMS. Viele Alltagsprobleme, die häufiger als Fragen in Foren landen, sind hier gelöst. Per Klick kann man sich dann dieselbe Lösung im "SQL" von Access, DB2, MySQL, Oracle, PostgreSQL und SQLServer ansehen - süß.


SQL-Referenz

nach obenSQL-Referenz 

SQL. Der Schlüssel zu relationalen Datenbanken

Gregor Kuhlmann, Friedrich Müllmerstadt

19,90 DM, 10,17 €

317 Seiten

ISBN 3499600633


MySQL-Website

nach obenMySQL-Website 

www.mysql.com hat MySQL natürlich dokumentiert. Insofern eine wichtige Ressource.


MySQL, Apache und PHP-Bündel

nach obenMySQL, Apache und PHP-Bündel 

PHPTriad bündelt MySQL, Apache und PHP für Windows, und erleichtert damit deren Installation. In die gleiche Lücke springt phpdev.


Die PHP-Tags

nach obenDie PHP-Tags 

Entgegen PERL, das in der Unix-Welt z.B. zum Erstellen von Makefiles benutzt wird, ist PHP auf HTML-Seiten spezialisiert. PHP wird so z.B. eingebettet, d.h. dass ich nicht wie in Perl die ganze Seite aufbauen muss

print "<html> Sie sind der <b>$i</b>te Besucher </html>";
sondern meinen Code in die "HTML"-Datei einfügen kann
<html>
	Sie sind der
	<b>
		<?php
			echo "$i";
		?>
	</b>
	te Besucher.
</html>
(Hinweis: die Datei ist dann strenggenommen keine HTML-Datei mehr sondern eine PHP-Datei, also bitte mit der Endung .php oder .phtml speichern, damit dies der Server auch erkennt: Siehe Ein eigenes Testsystem aufbauen. )

Ich habe den Code hier absichtlich eingerückt. Bei ständigen echo-Anweisungen wird nämlich der Code i.d.R. auch unübersichtlich. In PHP wird alles, was nicht im PHP-Tag (<?php und ?>) steht, ausgegeben, also wie eine konventionelle HTML-Datei behandelt.

Anmerkung

Natürlich kann man auch in PHP HTML-Tags ausgeben, z.B. mit der Anweisung
echo "<b>ein fetter Text</b>";
Das ist des öfteren nötig, aber im ersten Beispiel wäre es eben wegen dem Verlust der Übersichtlichkeit bemessen intelligent.


Varianten

nach obenVarianten 

Es gibt von dem PHP-Tag Varianten, eine z.B. die Kurzform

<? ... ?>
. Genau diese Kurzform versuche ich mir allerdings abzugewöhnen weil sie z.B. in Java Servlet Pages oder Active Server Pages (JSP bzw. ASP) Probleme verursacht: Dort sehen die Tags zum einfügen von Java- bzw. Microsoft-Code ansonsten genauso aus wie die Kurzform des PHP-Tags.


Kennzeichnung

nach obenKennzeichnung 

Dem Variablennamen wird ein Dollarzeichen $ vorangestellt. Ein Variablenname darf keine Leerzeichen, Satz- oder Sonderzeichen oder deutschen Umlaute wie äöüß enthalten. Eine Variable definiert man, indem man ihr einen Wert zuweist, z.B.

	$a = 1;
	$b = "Testtext"
Die Variablen sind nicht getypt wie in anderen (Kompiler-) Programmiersprachen; das heißt, eine Zahl wird zur Ausgabe automatisch zum String. So kann man z.B. mit
<?php
$a="My House <br>";  //My House 
echo $a;
$a=5;
echo $a."<br>";  // 5
$a="5";
echo $a+7;  // 12
?>
Den Typ einer Variable scheinbar ändern - und vor allem, ein String (der aber nur die Zahl 5 enthält) mit 7 summieren -- und PHP errechnet 12.


Geltungsbereich

nach obenGeltungsbereich 

Variablen gelten grundsätzlich nur für den Bereich, in dem sie definiert wurden. Wenn eine Variable beispielsweise in der Funktion definiert wurde, gilt sie nur für diesen Bereich. Um eine Variable zu importieren die z.B. im Hauptprogramm definiert wurde, gehen Sie folgendermaßen vor:

$a=2;
$b=3;
Sum();
echo $b; // 5
Function Sum () {
    global $a, $b;
    $b = $a + $b;
} 


Cookies

nach obenCookies 

Cookies (Informationshäppchen, die auf einer bestimmten Stelle der Platte des Surfers gespeichert werden, eine bestimmte Größe nicht überschreiten dürfen und nur von der Domain die sie gesetzt hat einsehbar sind) werden gesetzt mit

setcookie(<Cookiename>, <Wert>, <Haltbarkeitsdatum>)
Cookieinhalte wie Variablen bereitgestellt - wenn Cookies da sind, sind sie unter Ihrem Namen als Variable anzusprechen.

		$Count=50;
		setcookie ("Count", $Count, time()+3600); // hier nur eine Stunde haltbar,
	// dann wird er vom Browser gelöscht
		echo $Count;
Das Vorhandensein von Cookies (und Variablen überhaupt) überprüft man mit isset()


Übernahmereihenfolge

nach obenÜbernahmereihenfolge 

Die übernahmereihenfolge regelt Namensstreitigkeiten: wenn z.B. ein Cookie denselben Namen hat wie eine Systemvariable. wird in der PHP.INI festgelegt. Default ist "EGPCS": Environment, Get, Post, Cookies, System variables. Ein Cookie der den Namen einer Form-Variable (Get oder Post) hat würde diesen Wert (den der Form-Variable) dann überschreiben.


null

nach obennull 

Das Schlüsselwort null sagt, dass eine Variable zwar vorhanden, aber nicht belegt ist. Bitte auf keinen Fall mit 0 verwechseln, das ja eine Belegung ist. null weißt also eine gewisse ähnlichkeit mit undefined in javascript auf.

$myvar=null;

Ob eine Variable vorhanden ist, überprüfen Sie ggf. bitte mit der derselben Funktion wie Cookies,


isset

nach obenisset  bool isset(<Variable>);

Mit isset können Sie überprüfen, ob eine Variable definiert ist. Da Cookies und Formulardaten automatisch in Variablennamen übernommen werden, können Sie so auch überprüfen, ob Cookies definert sind.


unset

nach obenunset  void unset(<Variable>);

Möchten Sie Variablen loswerden, sprich löschen, können Sie dies mit unset tun. Ein kleines Beispiel zu isset und unset:

<?php
if (isset($intelligenz)) echo "Intelligenz existiert,";
else echo "Intelligenz existiert nicht,";
$intelligenz=5;
if (isset($intelligenz)) echo "jetzt existiert Intelligenz,";
else echo "jetzt existiert Intelligenz nicht,";
unset($intelligenz);
if (isset($intelligenz)) echo "und es gibt sie immer noch.";
else echo "und jetzt ist sie wieder verschwunden.";
?>
Auflösung: Die Variable existiert nur beim zweiten Aufruf, die Ausgabe lautet also "Intelligenz existiert nicht,jetzt existiert Intelligenz,und jetzt ist sie wieder verschwunden.".


error_reporting

nach obenerror_reporting  error_reporting($level);

Werden Variablen zuerst ausgelesen, bevor sie zum ersten mal beschrieben werden, geht PHP davon aus, dass sich der Programmierer im Namen vertan hat. Dem Programmierer liefert er einen leeren Inhalt zurück und generiert eine Fehlermeldung mit der Priorität notice. Diese wird in den Defaulteinstellungen von PHP komplett ignoriert, also auch nicht ausgegeben,

In Perl kann man sich dazu zwingen lassen, jede Variable zu deklarieren, bevor man sie nutzt (use strict). Das klingt unbequemm, hilft aber selbst bei kleinen Projekten enorm, Fehler zu finden beziehungsweise gar nicht erst auftreten zu lassen. In PHP gibt es so etwas zwar nicht, ein einschalten der "Notices" hat jedoch eine ähnliche Auswirkung. Das kann in der PHP.ini unter dem Schlüsselwort error_reporting geschehen oder im Script durch nutzen von

error_reporting(E_ALL);
$spukschloss=55;
echo "Der Wert der Variablen ist $spuckschloss";
/*Ausgabe:
Notice: Undefined variable: spuckschloss in /srv/www/htdocs/tutorial/vars/ex2.php on line 4
 Der Wert der Variablen ist
*/

Der Standardwert von Error_reporting ist E_ALL & ~E_NOTICE. Wer kann, sollte das ändern.

Gerade auf einem Entwicklungsserver gibt es keinen Grund, Notices nicht anzuzeigen, und es gibt auch keinen Grund, ein Script ohne error_reporting(E_ALL) zu entwickeln, es sei denn, man mag Bugs.


Allgemeines

nach obenAllgemeines 

Arrays bieten die Möglichkeit, mehrere Variablen desselben Types zu speichern und über einen Index (in der Form eines Integers) anzusprechen. Sie werden von PHP in der Namensgebung wie Variablen (also mit vorangestelltem $) behandelt. Angesprochen werden Arrays, indem hinter dem Variablennamen in der rechteckigen Klammer die Ziffer des auszulesenden Segments eingefügt wird, die Segmentnummerierung beginnt mit Null.

$zahlen = array(1,2,3,4);
$namen = array("Christoph","Martin");
$datei=file("arrays.html");  // diese Funktion speichert die Zeilen
//der Datei in je einem Segment eines Arrays
echo $zahlen[0]             // ergibt "1"
echo $namen[1]             // ergibt "Martin"
echo $datei[10];           //gibt die 11te Zeile der Datei aus
Einem Array können Sie einen Inhalt folgendermaßen anfügen:
$<Arrayname>[]  = <neues Element>
$namen[]="Claudia";
Jetzt beinhaltet der Array $namen die Inhalte Christoph, Martin und Claudia.


Assoziative Arrays

nach obenAssoziative Arrays 

Assoziative Arrays, auch Hashs genannt, bieten die Möglichkeit, von der Segmentnummerierung mit Integern Abstand zu nehmen und die Array-Elemente z.B. mit Strings anzusprechen.

$mitglieder = array(
    "John"  => 5,
    "Daniel" => 3,
    "Julie"   => 2
);
echo $mitglieder["Julie"];
oder auch etwas komplexer, mehrdimensionalerer
$gebaeude = array(
    "Möbel"  => array("liegen"=>"Bett","liegen"=>"Sessel","sitzen"=>"Stuhl"),
    "Häuser" => array("Einfamilienhaus",2 => "Mehrfamilienhaus",3 => "Reihenhaus"),
    "Garagen"   => array("für 1 Auto", "für zwei Autos", "Tiefgarage")
);
echo $gebaeude["Möbel","liegen"]; // Sessel, da der zweite Wert mit demselben Segment
// den ersten überschreibt


Mehrdimensionale Arrays

nach obenMehrdimensionale Arrays 

Niemand hindert sie daran, als Array-Variable einen Array anzugeben. (Genaugenommen ist das letzte Beispiel schon ein mehrdimensionales Array). Ein zweidimensionales Array stellt man i.d.R als Tabelle dar. Codiert sähe das dann so aus

$kleineseinmaleins=array(array(0,0,0,0,0,0,0,0,0,0),array(0,1,2,3,4,5,6,7,8,9),array(0,2,4,6,8,10,12,14,16,18));
echo $kleineseinmaleins[2][3];  // 6
wobei $kleineseinmaleins[2] faktisch der Name des Arrays ist dessen viertes (Segmentnummerierung beginnt bei 0) Element ausgegeben wird. n-Dimensionale Arrays sind möglich, jedoch steigt der Speicheraufwand exponentiell und spätestens nach drei Dimensionen ($arrayname[2][5][6]) verlieren die meisten auch die übersicht.


Weitere Array-Funktionen

nach obenWeitere Array-Funktionen 

sizeof

int sizeof (<Array>)
liefert die Anzahl der Elemente im Array zurück.
$w=0;
for ($i=0; $i<sizeof($theArray); $i++)
{
  $w+=$theArray[$i];
}
$avg=$w/sizeof($theArray);

array_merge

array array_merge(<Array1>,<Array2>)
Setzt Array2 an das Ende von Array1.
$kleineseinmaleins=array(array(0,0,0,0,0,0,0,0,0,0),array(0,1,2,3,4,5,6,7,8,0),array(0,2,4,6,8,10,12,14,16,18));
$fortsetzung=array(array(0,3,6,9,12,15,18,21,24,27),array(0,4,8,12,16,20,24,28,32,36),array(0,5,10,15,20,25,30,35,40,45));
$kleineseinmaleins=array_merge($kleineseinmaleins, $fortsetzung);
echo $kleineseinmaleins[5][5]; // 25

array_push

int array_push (<Array>,<Variable>[,<Variable2...>])
Fügt Variable(n) an Array an und liefert die neue Anzahl von Elementen in dem Array. Hat denselben Effekt wie
<Array>[]=<Variable>;
$kleineseinmaleins=array(array(0,0,0,0,0,0,0,0,0,0),array(0,1,2,3,4,5,6,7,8,0),array(0,2,4,6,8,10,12,14,16,18));
array_push($kleineseinmaleins, array(0,3,6,9,12,15,18,21,24,27));
echo $kleineseinmaleins[3][9]; // 27

array_pop

mixed array_pop(<Array>)
liefert das letzte Element des Arrays zurück und entfernt es aus ihm:
$kleineseinmaleins=array(array(0,0,0,0,0,0,0,0,0,0),array(0,1,2,3,4,5,6,7,8,0),array(0,2,4,6,8,10,12,14,16,18));
$malzwei=array_pop($kleineseinmaleins);
echo $malzwei[2].""; // 4
echo $kleineseinmaleins[2][2]; // keine Ausgabe, da Element nicht mehr in Array

array_pad

array array_pad (<Array>, <Länge>, <Wert>)
hängt den Wert an das Array an bis die Länge Länge erreicht ist.
$sexarray=array("f");
$sexarray=array_pad($sexarray, 9, "m");
$sexarray[8]="f";
echo implode(",",$sexarray);  //f,m,m,m,m,m,m,m,f

in_array

bool in_array (<Objekt>,<Array>)
Liefert true, wenn Objekt in Array vorkommt.
$kleineseinmaleins=array(array(0,0,0,0,0,0,0,0,0,0),array(0,1,2,3,4,5,6,7,8,0),array(0,2,4,6,8,10,12,14,16,18));
if (in_array(2, $kleineseinmaleins[2])) echo "2 gefunden";  // 2 gefunden
if (in_array(3, $kleineseinmaleins[0])) echo "3 gefunden";  // findet er nicht, weil er im falschen array sucht

sort

void sort (<Array>)
tauscht Arrayelemente so, dass der übergebene Array am Schluss sortiert ist (Vorsicht vor $sorted=sort($unsorted);).
$myarray=array(0);
for ($i=1; $i<10; $i++) $myarray[]=$i;
shuffle($myarray);  // array durcheinanderwürfeln
echo implode(",",$myarray)."";  // 7,8,6,1,9,2,4,0,3,5
sort($myarray);
echo implode(",",$myarray);  //0,1,2,3,4,5,6,7,8,9


Nutzen von Referenzen

nach obenNutzen von Referenzen 

Das = kopiert in PHP den Wert des rechten Teil des Ausdrucks in den Linken. Das ist an sich nicht ungewöhnlich, ungewöhnlich ist eigentlich mehr, dass das auch passiert, wenn man eine Funktion aufruft. Übergibt man die Variable $value als Parameter $firstParam, wird der Wert von $value in $firstParam kopiert (call by value).

Wie bei Babyalligatoren ist das solange praktisch, bis die Parameter groß werden. Wenn Sie ein paar tausend Einträge in einem Array haben, dauert es Zeit, bis diese kopiert sind, und außerdem wird Ihr Speicherplatz benutzt. Unpraktisch ist es auch, wenn Sie mit den Elementen im Array noch etwas vor haben. Denn weil alles, was in der Funktion geschieht, nur auf die Kopie angewendet wird, haben Sie zum Beispiel ein Problem, wenn Sie den Wert verändern und später in der aufrufenden Funktion noch nutzen möchten.

So haben sie zum Beispiel ein Funktion, die ein Array umsortiert und als HTML ausgibt. Rückgabewert der Funktion ist der HTML-Quelltext, und genau dann müssen Sie ohne Referenzen künsteln, wenn Sie das umsortierte Array noch einmal brauchen. Eine mögliche Lösung wäre es, anstelle nur HTML ein Array oder einen komplexen Datentyp aus HTML und umsortierten Array zurück zu geben. Beim zurück geben wird das Array nochmals kopiert. Referenzen sind eleganter.

Eine Referenz (bei PHP auch Alias genannt) auf eine Variable ist keine Kopie, Referenz und Variable haben immer denselben Wert. Ändern Sie die Variable, hat sofort die Referenz denselben geänderten Wert und ändern Sie die Referenz nimmt im selben Augenblick die Variable den geänderten Wert an.

Mit Referenzen wird das Beispiel mit dem umsortierten Array nicht nur schneller, sondern belegt auch weniger Speicherplatz. Sie übergeben als Parameter die Referenz auf das Array, nicht das Array selbst, und innerhalb der Funktion ändern Sie die Reihenfolge der Elemente und geben als Rückgabewert der Funktion nur den HTML-String zurück. Da die Referenz der Variablen entspricht, hat sich auch die Reihenfolge im Originalarray in der aufrufenden Routine geändert. Den Aufruf nennt man call by reference.


Verschnellert und verkleinert

nach obenVerschnellert und verkleinert 

Referenzen erzeugen Sie, indem Sie dem Dollarzeichen der Variable einen Ampersand (&) voranstellen. Das geht auch ohne eine Funktion aufzurufen.

$val="Bernd Buchhalter";
$ref=&$val;
$ref="Ralf Differenz";
echo "Bernd Buchhalter hei&szlig;t jetzt $val";
Als Parameter eine Referenz zu übergeben ist allerdings wie angedeutet besonders witzig:

function dreizurueck(&$refEins, &$refZwei, &$refDrei)
{
  $refEins=1;
  $refZwei=2;
  $refDrei=3;
}

dreizurueck($one, $two, $three);
echo "$one $two $three";

Das Ergebnis ist bei großen Arrays oft um ein mehrfaches schneller als ohne Referenzen, weil ansonsten viel Zeit beim unnötigen Kopieren verschwendet wird. Obwohl man davon laut Zend nicht ohne weiteres ausgehen kann - intern optimiert PHP selbst einiges auf Pointer.


Weitere Beispiele

nach obenWeitere Beispiele 

In Sprachen, die weniger virtuos mit Listen umgehen, wird das zum Teil benutzt, um eine Funktion mehrere Werte "zurück geben" zu lassen; die Variablen, die mit den Rückgabewerten belegt werden sollen, werden dabei als Parameter übergeben. Abgesehen davon, dass man durchaus auch komplexe Datenstrukturen oder Objekte zurück geben kann ist das spätestens dann kontraproduktiv, wenn außenstehenden Programmierern nicht sofort klar ist, dass die Variablen innerhalb der Funktion geändert werden, und das Programm deswegen nicht oder falsch verstehen.

Ein Beispiel schlechten Gebrauchs wäre, wenn ein anderer nicht ersehen könnte, welche Parameter geändert werden.

function umsatzsteuer(&$steuercode, &$betrag, &$ust)
{
if ($steuercode==1) { $ust=0.16; $betrag*=1.16; }
else { $betrag*=$ust; }
return $betrag;
}
Hier wird die Umsatzsteuer nur dann geändert, also quasi zur Rückgabe verwendet, wenn $steuercode==1 ist, der Betrag wird als Variable verändert UND zurück gegeben (die Werte sind zwar gleich aber es ist trotzdem nicht schön den Anwender entscheiden zu lassen, welches Ergebnis er benutzt) und Steuercode wird nur so aus Spaß als Referenz übergeben. Das ist dann aber schon extrem grausig anzusehen. Ach so, und eine Referenz sowohl zur Ein- als auch zur Ausgabe zu nutzen, macht bei Arrays manchmal Sinn, bei normalen Variablen macht es das lesen und verstehen oft unnötig schwer.

So etwas wie function lohnsteuer(&$steuerklasse, $betrag, &$lohnsteuer) kommt weit häufiger vor. Entweder hat der Programmierer die Referenz beim zweiten Parameter vergessen oder er traut seinem Leser zu, dass der spontan die richtige Eingebung hat, und sieht, dass links die Referenz zum Beispiel eine Eingabevariable ist, und und Lohnsteuer beschrieben wird (quasi ein Ausgabeparameter ist). Um das optisch zu trennen hat er vielleicht Betrag als normalen Parameter eingefügt. Besser finde ich erklärende Benennung und ein Gruppieren aller Referenzen etwa an den Anfang der Parameterliste wie addiereLohnsteuer(&$ref_in_steuerklasse, &$ref_out_lohnsteuer, $betrag).


Rückgabe aus einer Funktion

nach obenRückgabe aus einer Funktion 

Die Rückgabe von Referenzen erreicht man, indem man vor den Funktionsnamen ein Ampersand schreibt, und zudem den zurück gegebenen Wert im aufrufenden Programm als Referenz (sprich mit &) behandelt.

function &giveObject()
{
  $returnRef=new CmyObject();
  return $returnRef;
}

$myObjectFromFunction = & giveObject();
Man kann vereinfacht annehmen, dass "=" eine Kopie anlegt, und "= &" eine Referenz.


Referenzen als Indizes

nach obenReferenzen als Indizes 

Referenzen kann man auch in einem assoziativen Array verwenden, um über mehrere, verschiedene Schlüssel auf einen Wert zuzugreifen. Es kann für solche Probleme elegantere Lösungen geben, so ist das immer mit Vorsicht zu genießen. Beispielsweise möchte ich auf ein assoziatives Array zugreifen können und auch einen Index benutzen können, um an die einzelne Elemente zu kommen:

$arrayVariable=array();
$anzElemente=0;

$neuerSchluessel="Pestalozzi";
$neuerWert="Wer etwas wert ist, den macht Erfahrung und Unglück besser.";
$arrayVariable[$neuerSchluessel]=$neuerWert;
$arrayVariable[$anzElemente]=&$arrayVariable[$neuerSchluessel];
$anzElemente++;

$neuerSchluessel="Ebner-Eschenbach";
$neuerWert="Der Weise ist selten klug.";
$arrayVariable[$neuerSchluessel]=$neuerWert;
$arrayVariable[$anzElemente]=&$arrayVariable[$neuerSchluessel];
$anzElemente++;

Diese Lösung hat einige schöne, und einige unschöne "Nebenwirkungen". Unelegant ist das in der Hinsicht, dass dann kleine ganze Zahlen, z.B. 3 als Schlüssel eventuell vorhandene Schlüssel überschreiben, es sollte sich also um Strings handeln, die keine Zahlen annehmen können und dürfen. Ebenfalls unelegant ist die Lösung, weil foreach und count den Array doppelt durchgehen.

Elegant ist die Lösung hingegen, weil die verschiedenen Schlüsel immer auf denselben Wert verweisen, weil einer nur eine Referenz ist, dadurch auch Speicherplatz gespart wird und der Zugriff auf den Array sehr einfach ist - einfach $arrayVariable["Schlüsselname"] oder $arrayVariable[0]..$arrayVariable[$anzElemente]

Das geht auf mit mehreren Schlüsseln, wird dann aber bald ein ziemlich zusammengewürfeltes Array ergeben. Für eine andere Anwendungsmöglichkeit könnte den Index durch einen anderen Schlüssel tauschen:

$arrayVariable=array();
$indexArray=array();
$anzElemente=0;

$neuerSchluessel="Pestalozzi";
$neuerWert="Wer etwas wert ist, den macht Erfahrung und Unglück besser.";
$arrayVariable[$neuerSchluessel]=$neuerWert;
$indexArray["Erfahrung"][]=&$arrayVariable[$neuerSchluessel];
$anzElemente++;

$neuerSchluessel="Ebner-Eschenbach";
$neuerWert="Der Weise ist selten klug.";
$arrayVariable[$neuerSchluessel]=$neuerWert;
$indexArray["Erfahrung"][]=&$arrayVariable[$neuerSchluessel];
$anzElemente++;
Sie können dann sowohl über $indexArray["Erfahrung"][0] als auch über $arrayVariable["Pestalozzi"] auf das erste Zitat zugreifen.


Referenzen auf Objekte

nach obenReferenzen auf Objekte 

Besonders wichtig sind Referenzen in PHP 4 im Zusammenhang mit Objekten. Da erst PHP 5 Objekte in Parametern von Funktionen automatisch als Referenz übergibt, und Methoden (Funktionen innerhalb von Objekten) meistens die Eigenschaften (Variablen des Objekts) ändern, übergeben Sie ein Objekt meist manuell als Referenz. Wenn Sie das nicht tun, und innerhalb der aufgerufenen Funktion eine Methode aufrufen, die eine Eigenschaft des Objekts ändert, hat das Objekt, weil Sie nur eine Kopie bearbeitet haben, nach Abschluss der Funktion im aufrufenden Code plötzlich wieder die Eigenschaften als hätte die Funktion sie nie geändert (hat sie auch nicht, da sie nur die Kopie geändert hat).

Da das unpraktischer ist als Würfelzucker in Tuben, und man im Zusammenhang mit Objekten ständig die Referenzen braucht und sich schnell verzettelt, werden Objekte in PHP 5, wie auch in Java, standartmäßig per call-by-reference und andere Datentypen wie Strings und Integer, per call-by-value an Funktionen übergeben beziehungsweise von diesen zurück gegeben.


Herausfinden, ob ein Objekt eine Referenz ist

nach obenHerausfinden, ob ein Objekt eine Referenz ist 

Es gibt leider keine Eigenschaft "ID" eines Objektes, die bei Referenzen gleich wäre und bei Kopien unterschiedliche Werte annimmt - denn bei Kopien wird der entsprechende Wert mit-kopiert. Auch Vergleiche fruchten nicht, da Sie immer gleich zurück liefern, selbst mit dem Identitätsoperator ===. Der einzige mir bekannte Weg, herauszufinden, ob etwas eine Referenz oder nur eine Kopie ist, ist der von Digiways, eine Variable zu verändern beziehungsweise zu erweitern und zu sehen, ob sich die andere auch geändert hat:

function comparereferences(&$a, &$b)
/*

Vergleicht zwei Objekte und findet heraus, ob das eine eine
Kopie oder eine Referenz des anderen ist, indem es in einem
eine zusätzliche member-variable anlegt, kuckt, ob die
im anderen jetzt auch existiert, und dann wieder löscht.

Wird von mir zu debuggen benutzt.

von http://www.digiways.com/articles/php/smartcompare/

*/
   {
       // creating unique name for the new member variable
       $tmp = uniqid("");
       // creating member variable with the name $tmp in the object $a
       $a->$tmp = true;
       // checking if it appeared in $b
       $bResult = !empty($b->$tmp);
       // cleaning up
       unset($a->$tmp);
       return $bResult;
   }


Referenzen und foreach

nach obenReferenzen und foreach 

Foreach ist wunderschön - leider arbeitet es nicht mit Referenzen zusammen, sondern kopiert sich die Elemente. Sollten Sie mal in die Verlegenheit kommen, Ihre Schlüssel nicht zu wissen, können Sie sich jedoch mit reset, current und next helfen:

    reset($arrayMitReferenzen);
    $currentRow =& current ($arrayMitReferenzen);
      do
      {
        $currentObject=&$currentRow->getObject($columnName);
        $currentObject->someMethod();

   } while ($currentRow =& next ($arrayMitReferenzen));
   


Klassen

nach obenKlassen 

In Klassen kann man Variablen und Funktionen angeben, die dann eine logische Einheit bilden. Wenn man nur die Variablen betrachtet, ist eine Klasse ist also ein zusammengesetzter Datentyp, aus dem man Variablen (die dann Objekte genannt werden) ableiten kann.

Da die objekteigenen Funktionen (=Methoden) auf die Variablen des Objekts (=Eigenschaften) zugreifen können müssen, und gleichzeitig eine Trennung zu lokalen Variablen, die sich auf die Funktion, in der sie definiert sind, zu verdeutlichen, wird bei Zugriff auf die Eigenschaften ein Schlüsselwort vorangstellt, das immer "das aktuelle Objekt" bedeutet, $this. $this->variablenname (nicht $this->$variablenname) greift also auf eine Variable zu, die im gesamten Objekt gültig ist, während $variablenname eine lokale Variable ist, die nur in der aktuellen Funktion gültig ist.

Um zusätzlich, unabhängig davon, welche Funktionen abgelaufen sind, die auf Klassenvariablen hätten zugreifen können, ein Zugriff von außerhalb des Objektes auf die Klassenvariablen zu erlauben, werden diese mit dem Schlüsselwort var in der Klasse deklariert.


Beispiel

nach obenBeispiel 

Um ein Objekt aus einer Klasse zu erzeugen, verwendet man new.


class Golf
{
var
  $geschwindigkeit; // geschwinigkeit ist eine Eigenschaft, eine Member-Variable,
  		    // oder eine Klassenvariable der Klasse Golf.
function beschleunige()
{
  $this->geschwindigkeit=$this->geschwindigkeit+10;
}
function bremse()
{
  $this->geschwindigkeit=$this->geschwindigkeit-10;
}
}

Golf $meinGolf=&new Golf();  // Golf ist der Klassenname, $meinGolf ist der Instanzname,
			    // also das Objekt.
$meinGolf->beschleunige();
$meinGolf->beschleunige();
$meinGolf->bremse();
echo $meinGolf->geschwindigkeit;


Referenzen auf den Owner

nach obenReferenzen auf den Owner 

Tatsächlich gibt es manchmal Probleme, wenn man, wie in der Literatur beschrieben, Golf $meinGolf=new Golf(); verwendet. Es ist nämlich gar nicht mal so unüblich, dass das instanziierte Objekt dem Objekt, das es aufgerufen hat, noch etwas zu sagen hat. Üblicherweise baut man sich dann einen Konstruktor, in dem man eine Referenz auf das aufrufende Objekt übergibt; der Konstruktor speichert sich dann die Referenz, üblicherweise in einer Klassenvariable wie parent oder owner und die Member-Methoden können dann per $this->owner->funktionsname() eine Funktion des Vaterobjekts aufrufen.

Ich persönlich bevorzuge den Variablennamen owner, weil parent in PHP eigentlich die Vaterklasse, also die Klasse von der geerbt wurde, bezeichnet, und nicht die Klasse, aus der aufgerufen wurde.

Das Problem mit dem = new ist, dass es new aufruft und wieder eine Kopie übergibt, während $instanzname = & new Objektname(); die Instanz übergibt, die auch kreiert wird.

<?php

class myC1
{
  var
    $greeting;
  var  $theC2;
  function sayHello()
  {
      echo $this->greeting;
  }
  function myC1()
  {
      $this->greeting="Howdy";
      $this->theC2=new myC2($this);
  }



}

class myC2
{
  var $owner;
  function myC2(&$owner)
  {
    $this->owner=&$owner;
    $this->owner->greeting="bah";
  }
  function something()
  {
    $this->owner->greeting="hulla";
    // This is supposed to change myC1's greeting to hulla, overwriting "bah".
  }
 }


$test=new myC1(); //$test=&new myC1();
$test->theC2->something();
$test->sayHello();

?>
Das Beispiel gibt unter PHP4 "bah" aus - anstelle "hulla", wie es offensichtlich geplant und beabsichtigt war. $test=&new myC1(); dagegen gibt hulla aus. Generell schadet =& new nie, während man mit = new manchmal Probleme geben kann. Unter PHP5 ist das =& auch in dem Beispiel unnötig, da Objekte standardmäßig als Referenz übergeben werden. Also benutzen Sie es nicht! Sie würden Code riskieren, den man auch unter PHP4 ausführen kann!


Konstruktoren

nach obenKonstruktoren 

Wie Sie im Beispiel sehen, ist "geschwindigkeit" vor Gebrauch gar nicht initialisiert. Außerdem wird new Golf() aufgerufen, obwohl Golf eigentlich keine Funktion ist, sondern ein Datentyp (eine Klasse). Allerdings wird jeder Klasse eine Funktion zugeordnet, die so heißt wie die Klasse, und die Konstruktion neuer Objekte erledigt. Und diesen Konstruktor kann man verwenden, um Variablen zu initialisieren. Er wird innerhalb der Klasse bei der Erzeugung eines neuen Objektes zuerst aufgerufen. Angenommen, "mein Golf", oder besser die ganze Klasse Golf, rollt bei der Kontruktion langsam vom Band:

class Golf
{
var
  $geschwindigkeit;
  function Golf()
  {
    $this->geschwindigkeit=2;
  }
//...
}
Sie können dem Konstruktor auch Werte übergeben, etwa die Farbe:
class Golf
{
var
  $geschwindigkeit;
  $farbe;
  function Golf($farbe)
  {
    $this->geschwindigkeit=2;
    $this->farbe=$farbe;
  }
//...
}
Golf $meinGolf=&new Golf("Blau");


Methoden

nach obenMethoden 

Wie im beispiel beschrieben kann ich Funktionen in Klassen relativ einfach deklarieren. Ich kann Sie durch objektname->Funktionsname auch relativ einfach nutzen. Das bedeutet auch, dass im Klasseninternen Gebrauch ein $this-> voranzustellen ist.

class Golf
{
var
  $geschwindigkeit; // geschwinigkeit ist eine Eigenschaft, eine Member-Variable,
  		    // oder eine Klassenvariable der Klasse Golf.
function beschleunige($wert)
{
  $this->geschwindigkeit=$this->geschwindigkeit+$wert;
}
function bremse($wert)
{
  $this->beschleunige((-1)*$wert);
}
}


Vererbung

nach obenVererbung 

Eine der Gründe, warum Objektorientierung so erfolgreich ist, liegt darin, dass eine Einheit aus Funktionen und Variablen in der Lage ist, Zustände abzubilden. So war "meinGolf", nachdem er zweimal Beschleunigt hatt, 20 Einheiten schnell, bevor er wieder auf 10 Einheiten abgebremst wurde. Diese Zustände zu verändern, ist wichtig, um das Objekt anpassen zu können, und es über mehrere Operationen hinweg tun zu können, hilft dem Programmierer. So kann ich alle Golf mit Standarteigenschaften und Standartwerten konstruieren, danach vielleicht ein paar "Blumen" giessen(), dann ein paar Golf klauen() und umspritzen().

Sie können also Methoden verwenden, um Eigenschaften auszulesen oder zu ändern, und das führt zum Grundsatz, dass von außen nur die Schnittstellen zum Objekt (einige Methoden) sichtbar sein sollen, während die Eigenschaften und die genaue Arbeitsweise der Methoden "geheim gehalten" werden sollte, um es zu ermöglichen, diese später noch zu ändern. Das nennt man Black-Box-Prinzip, bei dem zwar die Box, aber nicht deren Inhalt sichtbar ist. Denn wenn es möglich ist, Klassen noch zu verbessern (das tut man oft durch geänderte Methoden und geänderte Datentypen), macht das Erweitern von Klassen erst richtig Spaß.

Klassen können nämlich um Methoden und Eigenschaften erweitert werden, ohne sie zu verändern. Es werden einfach neue Klassen geschaffen, die alle Methoden und Eigenschaften von den "Vaterklassen" erben. Sie erben damit nicht nur alle Namen von Methoden, sondern auch die Methoden selbst.

class Fahrzeug
{
var
  $geschwindigkeit; // geschwinigkeit ist eine Eigenschaft, eine Member-Variable,
  		    // oder eine Klassenvariable der Klasse Golf.
function Fahrzeug()
{
}
function beschleunige()
{
  $this->geschwindigkeit=$this->geschwindigkeit+10;
}
function bremse()
{
  $this->beschleunige(-10);
}
}

class Golf extends Fahrzeug
{
function bremseABS()
{
  $this->beschleunige(-12);  // Die Funktion kommt aus der Vaterklasse,
  			     // wird hier aber wie eine eigene Funktion
			     // angesprochen

}

}

Golf $meinGolf=&new Golf();  // Golf ist der Klassenname, $meinGolf ist der Instanzname,
			    // also das Objekt.
$meinGolf->beschleunige();
$meinGolf->beschleunige();
$meinGolf->bremseABS();
echo $meinGolf->geschwindigkeit;


Sie müssen in PHP 4, wenn Sie den Konstruktor einer verebten Klasse ändern, bei Bedarf den Konstruktor der Vaterklasse aufrufen:
class Golf extends Fahrzeug
{
var
  $geschwindigkeit;
  $farbe;
  function Golf($farbe)
  {
    parent::Fahrzeug();
    $this->geschwindigkeit=2;
    $this->farbe=$farbe;
  }
}
Golf $meinGolf=&new Golf("Blau");


Aufruf von Vaterfunktionen

nach obenAufruf von Vaterfunktionen 

Entgegen einem richtigen Erbfall, der mit der Anzahl Erben komplizierter wird, wird das objektorientierte Vereben umso schöner und leichter, je öfter man eine Vaterklasse nutzen kann: Zusätzlich zum Golf erbt z.B. auch ein LKW vom Fahrzeug:

class LKW extends Fahrzeug
{
  var
    $maxBeladung;

  function LKW()
  {
    $parent::Fahrzeug();
    $this->maxBeladung=30;
  }
  function belade($gewicht)
  {
    $this->maxBeladung=$this->maxBeladung-$gewicht;
  }

}
In dem Fall muss die Behandlung von Bremsen und Beschleunigen nicht mehr programmiert werden, weil das Verhalten und die Eigenschaften vererbt sind. Dummerweise wird ein erweiterter Konstruktor benötigt, und PHP sort nicht, wie andere Sprachen, für den automatischen aufruf des Vaterkonstruktors. In der Zeile $parent::Fahrzeug(); holen wir das nach.


Übergabe von Objekten

nach obenÜbergabe von Objekten 

Es spricht nichts dagegen, eine Funktion wie schrottpresse($meinGolf) mit einem Golf-Objekt aufzurufen. Abgesehen davon, dass man das verschrotten vielleicht in die Fahrzeug-Klasse implementieren könnte, müssten Sie beim obigen Beispiel nur aufpassen, das Sie das richtige Auto verschrotten. Denn jede Funktion wird per default "by value", also in dem Fall mit einer Kopie des meinGolf-Objekts aufgerufen. Sie sollten daher sichergehen, dass die entsprechende Funktion eine Referenz des Objekts bekommt und keine Kopie.

Beispiel:

class Golf
{
var
  $geschwindigkeit;
  $farbe;
  function Golf($farbe)
  {
    $this->geschwindigkeit=2;
    $this->farbe=$farbe;
  }
}

class Lackiererei {
  function spritze_um($GolfObjekt, $neueFarbe)
  {
     $GolfObjekt->farbe=$neueFarbe;
  }
}

$meineLackiererei =& new Lackiererei();
$meinGolf =& new Golf("Blau");
// Bitte jetzt nicht fragen, warum ich einen NEUEN Golf umspritze ;-)
$meineLackiererei->spritze_um($meinGolf, "Rot");
echo $meinGolf->farbe;
Das liefert ihnen weiterhin einen Blauen Golf, weil Sie nur eine Kopie des blauen Golfs, die auch nur ganz kurz in der Lackiererei "gelebt" hat, umgespritzt haben. Richtig wäre gewesen:
  function spritze_um(&$GolfObjekt, $neueFarbe)
  {
     $GolfObjekt->farbe=$neueFarbe;
  }
Siehe Referenzen.


Sinn von Objektorientierung

nach obenSinn von Objektorientierung 

Der Sinn von Objektorientierung ist es nicht, schnellere Programme zu erstellen, sondern deren Entwicklung zu beschleunigen und vor allem deren Erweiterbarkeit zu gewährleisten.

Vererbung ist dabei ein wichtiger Punkt, und abstrakte Klassen sind eine wundervolle Möglichkeit, einen generellen Ansatz zu programmieren, der dann von vielen Erben auf die Bedrfnisse angepasst wird. Das, und zum Beispiel Zugriffsschutz, wurde von PHP 4 noch nicht untersttzt. Das heißt nicht, dass es nicht ging, zum Beispiel so etwas wie eine abtrakte Klasse zu machen, von der eine Methode von einem Erben berschrieben wurde, aber PHP hat zum Beispiel nicht gewarnt, wenn ein Erbe es vergessen hat, diese Methode zu berschreiben.

Zum Glück wird mit PHP 5 vieles von der Sprache unterstützt. So entfällt zum Beispiel auch das umständliche und oft fehlerträchtige herumgekrame mit Referenzen. Objekte werden in PHP 5, wie in Java auch, ganz einfach nicht mehr kopiert, sondern referenziert.


Zugriffskontrolle auf Methoden und Eigenschaften

nach obenZugriffskontrolle auf Methoden und Eigenschaften 

Als eines der nebenwirkungsfreiesten Konzepte in der Computerwelt gilt das Blackbox-Prinzip, nach dem eine Klasse nicht wissen muss, wie die andere funktioniert. Um zu verhindern, dass zum Beispiel ein anderer Programmierer auf Funktionen oder Eigenschaften zugreift, die die interne Arbeitsweise der Klasse betreffen, kann man den Zugriff auf Funktionen beschränken, die der Klasse angehören (private).

<?

class SearchResult
{
  private $numResults=0;
  private $resArray;

  public function search($query)
  {
    $this->resArray=array("eis","wasser","dampf");
    $this->numResults=count($this->resArray);


  }
  public function getResult()
  {
    $res="";
    for ($resIndex=0; $resIndex<$this->numResults; $resIndex++)
    {
      $res.=$this->resArray[$resIndex];
    }
    return $res;
  }

  public function getNumResults()
  {
    return $this->numResults;
  }
}

$mySearchResult=new SearchResult();
$mySearchResult->search("nach einer guten Idee");
echo $mySearchResult->numResults;  //Fehler: Cannot access private property
$mySearchResult->numResults=5;
// Hier auch. Ihn das Ändern zu lassen wäre gefährlich, weil getResult
// Fehler machen wrde. Der Programmierer kann getNumResults() verwenden.
echo $mySearchResult->getResult();

?>
In diesem Beispiel sind die Methoden public, und die properties private. Obwohl es nichts gegen private Methoden einzuwenden gäbe, gibt es einige Grnde gegen öffentliche Properties:

Nebenbei gibt es noch weitere Schlsselwörter: Neben public und private ist mein Favorit "protected", das der eigenen Klasse, aber auch deren Erben den Zugriff gestattet.


Abstrakte Klassen

nach obenAbstrakte Klassen 

Abstrakte Klassen werden nicht instanziiert, sie dienen nur dazu, gemeinsame Methoden und Eigenschaften zu definieren, die später von einer Anzahl ähnlicher Erben verwendet werden.

Bei vielen Dinge, die klassifiziert werden k�nen, k�nte man die Klassifizierung als abstrakte Klasse verwenden. Elefanten, Pinguine und Menschen sind Tiere, es gibt aber kein Tier, das einfach nur ein Tier w�e und keiner Gattung angeh�en wrde.

Abstrakte Klassen sind deswegen so sch�, weil sie die Dinge, die die Erben unterscheiden, gar nicht implementieren, sondern mehr oder weniger sagen k�nen: Diese Methode muss jeder einzelne meiner Erben fr sich implementieren, ich kann aber nicht sagen, wie das bei meinen Erben im einzelnen aussieht.

<?

abstract class cAbstractAnimal
{

  function getNumberFeets()
  {
    return 4;
  }

  abstract function getCommunicationMethod();

}

class cHuman extends cAbstractAnimal
{
  function getNumberFeets()
  // hier wird eine Funktion berschrieben, obwohl sie nicht abstrakt ist. Das
  // h�te man auch in einer normalen Klasse gekonnt.
  {
    return 2;
  }
  function getCommunicationMethod()
  {
  // Wenn diese Funktion in dieser Klasse NICHT deklariert wird, kommt der Fehler
  // Class chuman contains 1 abstract methods and must therefore be declared abstract
    return "sprechen";

  }
}

class cDog extends cAbstractAnimal
{
  function getCommunicationMethod()
  {
  // Wenn diese Funktion in dieser Klasse NICHT deklariert wird, kommt der Fehler
  // Class chuman contains 1 abstract methods and must therefore be declared abstract
    return "bellen";

  }
}

  $human=new cHuman();
  echo "Menschen haben ".$human->getNumberFeets()." F� und ".$human->getCommunicationMethod().".<br>";
  $dog=new cDog();
  echo "Hunde haben ".$dog->getNumberFeets()." F� und ".$dog->getCommunicationMethod().".<br>";

?>
Das Beispiel ist noch nicht so prickelnd, weil abstrakte Klassen desto mehr Arbeit sparen, desto mehr nicht-abstrakte Funktionalit� in ihnen deklariert ist und desto mehr Erben sie haben, die sich in möglichst wenigen (in der Ursprungsklasse abstrakten) Funktionen unterscheiden. Bei 50% abstrakter Funktionen und nur zwei Erben spielen sie also noch nicht wirklich ihre St�ken aus.


Interfaces

nach obenInterfaces 

Ein ziemlich extremer Ansatz von abstrakten Funktionen sind Interfaces. In Interfaces sind alle Funktionen abstrakt. Wie Java ist PHP eine Sprache, die keine Mehrfachvererbung unterstützt, und wie bei Java wird dieser Grundsatz für Interfaces aufgeweicht. Eine Klasse kann von eine Vaterklasse und einer beliebige Anzahl von Interfaces erben.

Warum mehrfachvererbung problematisch ist erklärt folgendes Beispiel: Sowohl rectangle als auch triangle erben von Klasse shape und überschreiben bei dieser Gelegenheit die Methode getArea. Kommt man jetzt auf die Idee ein Rechteck mit einem aufgesetzten Dreieck als Erbe beider Klassen definieren zu wollen müsste man für dessen Methode getArea entweder festlegen, welche der Vaterklassen benutzt werden soll oder eine Warnung einbauen dass in diesem Fall getArea in der Klasse RectangleWithTriangle neu implementiert werden muss. Beide Vatermethoden aufzurufen kommt im Grunde nicht in Frage, weil die Reihenfolge des Aufrufs für das Ergebnis entscheidend sein kann und man nicht wüsste was man mit eventuellen Return-Werten machen sollte.

In Java werden Interfaces zum Beispiel gerne bei Funktionen benutzt, die einen Benutzerzugriff erlauben. So kann in einem Interface zum Beispiel eine virtuelle Funktion liegen, die die Behandlung eines Tastendrucks definiert. Man kann dann von bestimmten Klassen erwarten, dass sie bestimmte Interfaces implementieren, und aus der Basisklasse die entsprechenden virtuellen Funktionen aufrufen.

Im hart typisierten Java macht das Sinn, beim schwach typisierten PHP, bei dem man höchstens durch Hints eine gewisse Klasse (zur Laufzeit) erwarten darf, ist das problematisch. Ein Interface ist also so etwas wie ein Satz von abstrakten Funktionen.

Das Originalbeispiel aus der Zend 2-Ankndigung

interface Throwable {
   public function getMessage();
}

class MyException implements Throwable {
   public function getMessage() {
       // ...
   }
}


Objekte kopieren

nach obenObjekte kopieren 

Da PHP ab der Version 5 bei Objekten mit Referenzen arbeitet, muss man auch irgendwie in der Lage sein, explizit eine Kopie eines Objekts herzustellen:

<?
class cAngestellter
{
  public $wohnort; // Ausnahmsweise public-Variablen, die sind hier unabhängig, unkritisch
  // und ansonsten zuviel Schreibarbeit.
  public $ausbildung;
  public $name;

  function __toString() {
       return "<br>".$this->name." hat ".$this->ausbildung." und wohnt in ".$this->wohnort;
   }


}


$zwilling1=new cAngestellter();
$zwilling1->wohnort="Berlin";
$zwilling1->ausbildung="allgemeine Hochschulreife";
$zwilling1->name="Bernd Bleifuß";
$zwilling2=$zwilling1->__clone();
// bei $zwilling2=$zwilling1; hießen beide gleich Benjamin Bleifuß $zwilling2->name="Benjamin Bleifuß";


  echo $zwilling1;
  echo $zwilling2;

?>


Ganz nebenbei zeigt das Beispiel auch noch eine schönee Möglichkeit, Objekte zu konvertieren. Zumindest in Strings, mit einer "überladenen" (also berschreibenen) __toString-Funktion, deren Rückgabewert automatisch verwendet wird, wenn das Objekt als ganzes in einen String konvertiert werden soll.


Exceptions

nach obenExceptions 

Exceptions sind Error-Codes im objektorientiertem Paradigma, sie sind besonders dort sinnvoll wo

Erstmal wichtig zu wissen: exceptions werden mit throw geworfen und mit catch gefangen. Sollte etwas unabhängig von einer Exception ausgeführt (zum Beispiel Bereinigungen nach angefangener arbeit) kommt das in einen "finally" block.

Das Throw sieht dabei so aus: throw new Exception("illegal handler type");

Beispiel: Error codes mit einem Programm das eine Datei liest in der eine positive oder negative Zahl (oder 0) enthalten ist:


$fh=null;
If (seeIfFileExists($f)===true) {
if (hasFileAccess($f)===true) {
$fh=fopen($f);
if ($fh) {
if (fileSize($fh)>0) {
$fcont=readfile($fh);
if ($fcont!==null) {
$balance=analyzeContentAndGetTotalSum($fcont);
if ($balance<0) {
		echo "Account is negative";
		} else if ($balance===0) {
		echo "Account is 0";
		} else if ($balance>0) { 
	echo "Account is positive";
	} else if ($balance===null) { 
	fclose($fh);
	die("don't know what happened, no balance returned, maybe file content not numeric");
	
	}	
	
	} else { fclose($fh); die("could not read file content"); }
	
	} else { fclose($fh); die("something wrong with file: <=0 file size reported"); }
								  } else die ("could not open file");
								  
								  } else die("have no access privileges");
								  } else die("file does not exist");

Das gleiche Beispiel könnte mit Exceptions so aussehen:	
$fh=null;
  try {
	  seeIfFileExists($f);
	  hasFileAccess($f);
	  $fh=fopen($f);
	  $fcont=readfile($fh);
	  $balance=analyzeContentAndGetTotalSum($fcont);
	  if ($balance<0) {
		echo "Account is negative";
	  } else if ($balance===0) {
		echo "Account is 0";
	  } else if ($balance>0) { 
	        echo "Account is positive";
	  } 
	} catch (Exception $e) {
	  echo $e->getMessage();
	}
  finally {
	  if ($fh!==null) {
		  fclose($fh);
	  }
  }
Wenn die Funktionen anständige exceptions throwen, sprich verschiedene exceptionklassen benutzen, kann man auch leicht auf den Art des Vorfalls reagieren indem man das catch (Exception $e) ersetzt durch
  
   } catch (fileContentException $e) {
	  blameFileAuthor();
	  echo $e->getMessage();
   } catch (fileReadException $e) {
	  blameFileAuthor();
	  echo $e->getMessage();
   } catch (fileSizeException $e) {
	  blameFileAuthor();
	  echo $e->getMessage();
  } catch (fileOpenException $e) {
	  blameOperatingSystem();
	  echo $e->getMessage();
  } catch (fileAccessException $e) {
	  blameOperatingSystem();
	  echo $e->getMessage();
  } catch (fileExistenceException $e) {
	  blameUserForSelectingWrongFile();
	  echo $e->getMessage();
  }

Wie man leicht sieht, wären hier verschiedene Kategorien von Fehlern sinnvoll, was man implementieren kann, indem man die Exception-Klasse vererbt: mit einer Klassenhierarchie wie

  	    Exception->AuthorException->fileContentException
		Exception->AuthorException->fileReadException 
		Exception->AuthorException->fileSizeException 
		Exception->OSException->fileOpenException 
		Exception->OSException->fileAccessException 
  
kann man den code nämlich kürzen in
	
  } catch (AuthorException $e) {
	blameFileAuthor();
	echo $e->getMessage();
  }  catch (OSException $e) {
	  blameOperatingSystem();
	  echo $e->getMessage();
  }  catch (fileExistenceException $e) {
	  blameUserForSelectingWrongFile();
	  echo $e->getMessage();
}
 
Noch ein paar Bemerkungen zu Vorteilen von Exceptions:
  1.  
    } catch (fileContentException $e) {
    blameFileAuthor();
    echo $e->getMessage();
      }
     
    nett zu erwähnen dass, da die fileContentException in der funktion gethrowet wird wo sie festgestellt wird, auchaus auch detailliertere angaben enthalten kann als "don't know what happened, no balance returned, maybe file content not numeric".

    Die Funktion könnte zum Beispiel so aussehen:

      
      Protected function analyzeContentAndGetTotalSum($fileContent) {
      if (!ctype_numeric($fileContent)) throw new fileContentException("file content is not numeric: $fileContent");
      $res=intval($fileContent);
      // the following two thrown exceptions are per se probably more assertions
      if ($res>1000000) throw new fileContentException("you are too rich for this program: $res");
      if ($res<1000000) throw new fileContentException("you are too poor for this program: $res");
    return $res;
    }
    

  2. Exception-Subklassen kann man prima verwenden um event-handler-code einzubauen, zum Beispiel
    Class AuthorException extends Exception {
    	public function __construct($message) {
    	mail ("author@usegroup.de", "exception", "Your program is buggy because some idiot managed to write a file that you can't parse:".$message->getText());
    	super::__construct($message);
    }
    }
    
    Class OSException extends Exception {
    	public function __construct($message) {
    	mail ("linus.torvalds@osdl.org", "exception in linux", "Fix this:".$message->getText());
    	super::__construct($message);
    }
    


Formulardaten übernehmen

nach obenFormulardaten übernehmen 

Formulardaten werden in PHP in der Regel automatisch übernommen. Ruft ein Formular eine PHP-Seite als "Action" auf, so werden alle im Formular benannten Felder egal ob mit POST oder mit GET übergeben automatisch in nach den Namen der Felder benannte Variablen umgemünzt (Meist nach dem EGPCS-System).

Mit



<html>
<form action=form.php method=get>
<input name=email><br>
<input type=checkbox name=newsletter><br>
<input type=submit><br>
</form>
</html>

kann man in form.php die Variablen $email und $newsletter ansprechen.

ABER Alle Punkte in Formularfeldnamen werden in Unterstriche (_) in den Variablennamen umgewandelt.

Sollte es wider Erwarten nötig sein, Variablen zu dekodieren (z.B. wenn sie Sonderzeichen enthalten) bietet raw_urldecode eine gute Möglichkeit dazu.

Als Einschränkung muss man bei dieser Bequemlichkeit hinnehmen, dass man die Variablennamen wissen muss, um sie ansprechen zu können. Um dies zu umgehen finden Sie einen Trick im Kapitel Ein Form-Mailer


Warum klappt das bei mir nicht?

nach obenWarum klappt das bei mir nicht? 

Aus Sicherheitsgründen kann in der php.ini (unter Linux: /etc/php.ini, unter Windows:\windows\system32\php.ini) register_globals auf off geschaltet sein, was die automatische Übernahme in gleichlautende globale Variablen abschaltet. Erstens: Die Alternative lautet $_REQUEST["namedesformularfelds"] und Zweitens: es gibt zwei Möglichkeiten zu überprüfen ob register_globals eingeschaltet ist, einerseits kann man einfach testen ob $_REQUEST["namedesformularfelds"]==$namedesformularfelds, andererseits steht es auch in der Funktion phpInfo. Beides können Sie mit folgendem Script machen.

<html>
  <body>
    <form method="post" name="form1">
      <input name="hiddenInput" type="hidden" value="1">
      <?php
        if ($_REQUEST["hiddenInput"]=="1") // $_Request funktioniert auch bei ausgeschaltetem register_globals
        {
      	      if ($_REQUEST["hiddenInput"]==$hiddenInput) echo "register_globals ist ON.<br>";
	      else					    echo "register_globals ist OFF.<br>";
        }
      ?>
      <input type=submit value="Hier klicken um Register-Globals zu testen">
      <input type=submit onClick="document.form1.hiddenInput.value=2;" value="Hier klicken um phpInfo zu  zeigen.">
      <?php  // phpInfo ist ziemlich lang, deshalb unter den Knöpfen zeigen
        if ($_REQUEST["hiddenInput"]=="2") phpinfo();
      ?>
    </form>
  </body>
</html>
Falls Sie PHP als Modul benutzen (meist unter Linux und erheblich schneller als die Windows-CGI-Variante) vergessen Sie bitte nicht, den Apache neu zu starten (apachectl reload) nachdem Sie die php.ini geändert haben. Wenn Sie den Apache nicht selbst hosten brauchen Sie ihn auch nicht neu zu starten - in dem Fall haben Sie in der Regel auch überhaupt keine Rechte, die php.ini zu ändern.


Dateiuploads

nach obenDateiuploads 

Selten genutzt, verkümmert der HTTP-Upload im Schattendarsein der Web-Mailer. Seine einzige Funktion ist es oft, Anhänge an die www-basierend-geschriebene eMail zu ermöglichen. Im Grunde ist es aber ein perfektes Administrationstool.

<!-- HTML-Datei -->
<html>
<form method=post action="formaction.php">
<input type=file name="myfile">
<input type=submit>
</form>
</html>
<!-- PHP-Datei formaction.php-->
<?php
    echo $myfile; // Der Name ergibt sich logischerweise
//aus dem Namen des input-tags in der HTML-Datei
    echo $myfile_size;
    copy ($myfile, "temp.pp0");
?>
Die Formaction gibt den temporären Dateinamen aus und die Dateigröße des Uploads (Dieser darf nebenbei bemerkt, die Größe die in der PHP.INI unter upload_max_filesize festgelegt ist, nicht überschreiten. Der default-Wert liegt bei 2MB). Diese Datei ist so erstaunlich temporär, dass es sie nach dem Ende des Script schon gar nicht mehr gibt: also schnell umkopieren (hier in temp.pp0, schöner vielleicht in $myfile_name, dem Namen der Ursprungsdatei).


Wofür braucht ein normaler Mensch Sessions?

nach obenWofür braucht ein normaler Mensch Sessions? 

HTTP leidet an akuter Amnesie und vergisst leider alles was weiter als ein Formular entfernt ist: Wenn Sie ein Formular submitten, können Sie in der nächsten angezeigten Seite noch sagen, was der Benutzer eingegeben hat, in der übernächsten jedoch nicht mehr. Das ist ziemlich unpraktisch, etwa wenn Sie Assistenten anbieten, auf denen der Benutzer über mehrere Seiten Eingaben machen kann, die erst am Ende in eine Datenbank geschrieben werden sollen oder wenn Sie möchten, dass sich Ihre Benutzer einloggen, und dann auf jeder Seite überprüfen, ob der Benutzer authorisiert ist (obwohl ich da ein angepasstes HTTP-Auth beinahe empfehlen würde).

Theoretisch könnten Sie alle Formulardaten immer in Hidden Inputs in jede Seite schreiben aber das wäre umständlicher als Sessions und erfordert eine Anzahl Hidden Inputs pro Seite die genauso groß ist wie die Anzahl der Inputfelder in Ihrem gesamten Projekt. Wenn Sie diese Lösung noch so programmieren, dass Sie einfach weitere Formularfelder hinzufügen können, ohne die anderen Seiten zu ändern (mit PHP geht das...) haben Sie sich defintiv zu viel Arbeit gemacht.


Wie funktionieren Sessions technisch?

nach obenWie funktionieren Sessions technisch? 

Sie legen einmal eine Session an und erhalten eine ID zurück. PHP speichert dann alle Daten, die unter dieser ID anfallen, zentral und abrufbereit auf dem Server. Der Browser behält sich (nur) seine Session-ID in der Regel in einem Cookie. Ein Problem ist, wenn diese Cookies abgeschaltet sind, würde die Session-ID auf jeder Seite verloren gehen, Sessions wären also vollkommen wirkungslos. Deshalb gibt es die Möglichkeit, die Session ID, SID, auch in (ein einzelnes) Hidden Input einzutragen oder als Parameter bei einem Link zu übergeben. Das heißt, Sie müssen jeden Link, hinter dem Sie die Session noch gebrauchen wollen, so präparieren, dass er die Session ID erhält, falls Sie Cookieablehnende Benutzer nicht vor den Kopf stoßen wollen. Um Session Hijacking zu vermeiden (jemand anders loggt sich mit derselben SID wie ein angemeldeter Benutzer ein), ist die Session ID eine Zufallszahl. Und um Angreifern ihr Handwerk zu erschweren verfällt diese Session ID nach einer angegebenen Laufzeit, außerdem sind Benutzer gehalten, sich auszuloggen (wobei die Session durch den Programmierer als ungültig erklärt wird).


Konfiguration von PHP: session.auto_start

nach obenKonfiguration von PHP: session.auto_start 

Wie auch beim Übernehmen von Formulardaten gibt es bei den Sessions einen Trick, wie Sie es sich einfach machen können: In der php.ini (unter Linux: /etc/php.ini, unter Windows:\windows\system32\php.ini) schalten Sie dazu session.auto_start auf 1 und starten Sie Ihren Apache mit apachectl reload neu. Ob dies auf Ihrem Webserver so gesetzt ist, können Sie praktisch nur über die phpinfo()-Funktion kontrollieren, entweder schreiben Sie sich selbst kurz ein <?php phpinfo(); ?> oder Sie verwenden das Beispiel in Formulardaten übernehmen. Ist session.auto_start nämlich 1, können Sie Sessions einfach verwenden, indem Sie das assoziative Array $_SESSION verwenden.

   <!-- Bitte in die Datei test.php -->
Anzahl Seitenwechsel: <?php
if (!isset($_SESSION["zaehler"])) $_SESSION["zaehler"]=0;
$_SESSION["zaehler"]++;
echo $_SESSION["zaehler"];
?>
<a href="test2.php?<?=SID?>">next</a>
   <!-- Bitte in die Datei test2.php -->
Anzahl Seitenwechsel: <?php
if (!isset($_SESSION["zaehler"])) $_SESSION["zaehler"]=0;
$_SESSION["zaehler"]++;
echo $_SESSION["zaehler"];
?>
<a href="test.php?<?=SID?>">next</a>

   
Beachten Sie, dass die Konstante SID in beiden Dateien im Link angegeben ist. So funktioniert das Beispiel auch mit ausgeschalteten Cookies und zeigt dort dann bei einem Seitenwechsel etwas in der Form test2.php?PHPSESSID=9970ac048cb8b015b5969f1a75e1c4fa in der Adresszeile (und im Link).


Die Konstante SID

nach obenDie Konstante SID 

Die Session-ID können Sie an Links durch die Konstante SID anhängen. Ihr wird nicht, wie bei Variablen, ein Dollarzeichen vorangestellt.

  <a href="test.php?<?=SID?>">next</a>
  
Diese "Konstante" enthält den Namen, wie Session-IDs genannt werden (Standard:PHPSESSID), ein Gleichzeichen und die ID. Sie sollten also an ihren Link vor der SID-Konstante ein ? oder ein & anschließen, je nachdem, ob PHPSESSID der erste oder ein folgender Parameter ist (Man verwendet zum Abgrenzen des Dateinamens vom ersten Parameter ein ? und zwischen den Parametern ein &. Sollten Sie dieses Fragezeichen unterlassen, bekommt der Besucher wohl etwas wie eine File-Not-Found-Fehlermeldung).
  <a href="test.php?action=next&<?=SID?>">next</a>
  


SID in Formularen: session_id

nach obenSID in Formularen: session_id 

Haben Sie ein Formular und möchten nicht, dass durch Drücken des Absenden-Buttons die Session verloren geht, sollten Sie ein verstecktes Eingabefeld mit Namen PHPSESSID einbauen und mit der ID der aktuellen Session belegen. Die Konstante SID ist in dem Fall wertlos, da sie nicht nur die Session-ID speichert, sondern zusätzlich PHPSESSID= vornedran. Interessanter ist in dem Fall wohl die Funktion session_id():

  <form method="post" action="test2.php">
    <input type="hidden" name="PHPSESSID" value="<?=session_id()?>">
    <input type="submit">
  </form>
  
PHPSESSID wird in dem Fall automatisch ausgewertet.


Die Variable _SESSION

nach obenDie Variable _SESSION 

_SESSION ist ein globaler assoziativer Array, die Sie als solche nicht gesondert in Ihrer Funktion zu deklarieren brauchen (keine Angabe von global). Eine Variable speichern Sie in der Session durch Angabe von $_SESSION['variablenname'] = wert;, auslesen tun Sie sie logischerweise mit $auslesewert=$_SESSION['variablenname'].


Sitzungen beenden/verlassen

nach obenSitzungen beenden/verlassen 

Beim ausloggen eines Benutzers sollten Sie PHP die Variablen in dieser Sitzung löschen lassen, damit niemand anders, mit spionierter oder erratener Session-ID, die Session im eingeloggten Zustand kapern kann. Dazu kann ein session_unset() nicht schaden. Der Dokumentation nach solle man bei Verwendung von $_SESSION eher $_SESSION=array(); benutzen, das kann ich aber nicht nachvollziehen, weil session_unset() zumindest in meinem System die gleiche Wirkung zu haben scheint. Allerdings verbietet auch niemand die doppelt-gesichert-hält-besser-Taktik.

  session_unset();
  $_SESSION=array();
  


Sitzungen teilweise beenden

nach obenSitzungen teilweise beenden 

Sie können wie gewohnt mit unset nur ausgewählte Sitzungsvariablen ungültig machen:

    unset ($_SESSION["nameDerZuZerstoerendenVariable"]);
  
Schönen Gruß vom Programmierer: unset ($_SESSION) sollen Sie möglichst vermeiden :-) (siehe Sitzungen beenden)


Sitzungen starten

nach obenSitzungen starten 

Sitzungen starten und beenden sind, zumindest in PHP, zwei grundverschiedene Dinge: Dieselbe Sitzung kann (und muss meistens) häufiger als einmal gestartet, darf und kann allerdings nur einmal beendet werden. Ist session.auto_start in der php.ini aktiviert, wird sie bei Bedarf und so oft als nötig gestartet, beendet wird sie typischerweise nur beim Ausloggen, durch den Programmierer immer extra anzugeben, durch session_unset bzw. $_SESSION=array();. Gestartet im Sinne von session_start() muss die Session immer werden, wenn Sie innerhalb der PHP-Datei verwendet werden soll, also in der Regel in allen PHP-Dateien des Bereichs, in dem Sessions verwendet werden. session_start() ist also eher eine Anmeldung zur Benutzung von Sessions. Existiert noch keine Session-ID, wird diese allerdings beim ersten Session-Start angelegt.

Wie bei header-Funktionen muss session_start() vor jeder Ausgabe (wie echo oder auch nur einem Leerzeichen oder Zeilenvorschub im HTML-Quelltext) erfolgen. Wie erwähnt wird es nur benötigt, wenn session.auto_start=0 ist . Das erste Beispiel für Systeme ohne Session-Auto_start:

<?php session_start(); ?>
<!-- Bitte in die Datei test.php -->
<!-- Keine Kommentare, Leerzeilen oder Leerzeichen - irgendwas - vor dem PHP-Block mit session_start. -->
Anzahl Seitenwechsel: <?php
if (!isset($_SESSION["zaehler"])) $_SESSION["zaehler"]=0;
$_SESSION["zaehler"]++;
echo $_SESSION["zaehler"];
?>
<a href="test2.php?<?=SID?>">next</a>
<?php session_start(); ?><!-- Bitte diese Zeile schon (incl. session_start()) in die Datei test2.php -->
<!-- Keine Kommentare, Leerzeilen oder Leerzeichen - irgendwas - vor dem PHP-Block mit session_start. -->
Anzahl Seitenwechsel: <?php
if (!isset($_SESSION["zaehler"])) $_SESSION["zaehler"]=0;
$_SESSION["zaehler"]++;
echo $_SESSION["zaehler"];
?>
<a href="test.php?<?=SID?>">next</a>

  


Vergleichsoperatoren

nach obenVergleichsoperatoren 

$a==$b ergibt dann true, wenn a gleich b ist ( Vorsicht: a=b ist eine Zuweisung und ergibt immer true)
$a!=$b ergibt dann true, wenn a nicht gleich b ist
$a<$b ergibt dann true, wenn a kleiner ist als b
$a>$b ergibt dann true, wenn a größer ist als b
$a<=$b ergibt dann true, wenn a kleiner oder gleich b ist
$a>=$b ergibt dann true, wenn a größer oder gleich b ist


If

nach obenIf 

if (<Bedingung>)
                     {
                     <Befehle>
                     }
                     [else
                     {
                     <Alternative>
                     }]
                     
                  
Führt Befehle aus wenn eine bestimmte Bedingung erfüllt ist, ansonsten den (optionalen) Alternativblock.
if ($alter<18)
{
echo "Sie haben hier eigentlich gar kein Zutritt";
}


Rechenoperatoren

nach obenRechenoperatoren 

Wie so viele Sprachen ist PHP zu C++ Operator-Kompatibel. Das heißt im Einzelnen: Rechenoperatoren:

$a=$b Zuweisung, A nimmt den Wert von B an ( Achtung, es wird mit == verglichen, ob beide Werte gleich groß sind (siehe Vergleiche) )
$c=$a+$b C nimmt die Summe von A und B an (wer hätt's gedacht)
$c=$a-$b Subtraktion
$c=$a*$b Multiplikation
$c=$a/$b Division
$c=$a%$b Modulo, c nimmt den Rest der Division a-b an.


Zuweisung nach Rechnung

nach obenZuweisung nach Rechnung 

Es gibt in PHP (wie auch in C) eine Kurzform der Anweisungen

A=A <Operator> B
und zwar sieht diese so aus:
A<Operator>=B
Ausdrücke in der Form a <Rechenoperator>= <Ausdruck>; bewirken, dass a mit dem angegebenen Rechenoperator und b berechnet wird und das Ergebnis auf a zugewiesen wird. a+=10 z.b. ist die Kurzform von a=a+10; a/=b ist das gleiche wie a=a/b.

$a=5;
$b=3;
a%=3; // a nimmt den Wert von 5%3 an, also 2 (1*3+2=5 :-)).

$s="Hallo ";
$s.="Welt"; // s enthält jetzt Hallo Welt, obwohl . eigentlich 
// kein Operator ist. Siehe Arbeiten mit Strings.


Inkrement und Dekrement

Desweiteren gibt es eine Kurzform um eine Variable um eins zu erhöhen oder zu erniedrigen. diese sieht so aus:
$<Variablenname>++
(Erhöhen um 1) bzw.
$<Variablenname>--
(um 1 erniedrigen). $i++ ist gleichbedeutend mit $i+=1 und das ist dasselbe wie $i=$i+1;

Preinkrement

In C++ gibt es die Norm, z=5+i++; zuzulassen und so auszuwerten: z wird zugewiesen 5+i, DANACH wird i erhöht um 1.Das nennt man Postinkrement.

z=5+(++i); Würde z 5+i+1 zuweisen, i also VOR der Ausführung der Operation erhöhen (Preinkrement). Das (Erhöhen/Erniedrigen VOR der Operation) sieht in PHP so aus:
++$i;


while

nach obenwhile 

Schleifen funktionieren in PHP wie in jeder C-ähnlichen Sprache nach dem Muster

                     while (<Bedingung>) 
                     {
                     <Schleifeninhalt>
                     }
                     
                  
Dies prüft am Anfang der Schleife, ob Bedingung wahr ist und wenn ja, wird die Schleife eben ausgeführt. Dann wird der Wahrheitsgehalt nochmals überprüft und die Schleife gegebenenfalls nochmal ausgeführt.
                     do
                     {
                     <Schleifeninhalt>
                     }
                     while (<Bedingung>)
                     
                  
Prüft am Ende der Schleife, ob Bedingung wahr ist, nachdem die Schleife schon ausgeführt wurde. Eine solche Schleife wird also mindestens einmal ausgeführt.
$i = 1;
while ($i <= 10) {
    print $i++;  // Siehe Operatoren

}


for

nach obenfor 

For ist ebenfalls C-ähnlich, die Anweisung lautet

                     for (<Startanweisung>; <Bedingung>; <Schleifenanweisung>) 
                     {
                     <Schleifeninhalt>
                     }
                     
                  
Zuerst wird Startanweisung ausgeführt, dann, falls Bedinung erfüllt ist, der Schleifeninhalt und dann die Schleifenanweisung, dann wird nochmals überprüft ob die Bedingung noch wahr ist und falls ja wird der Schleifeninhalt nochmals ausgeführt, dann die Schleifenanweisung und so weiter.

Diese Schleife zählt von 1 bis 10
for ($i=1; $i<=10; $i++)
{
  echo "$i ";
}
Diese Schleife zählt in zehnerschritten von 100 bis 200
for ($i=100; $i<=200; $i+=10)
{
  echo "$i ";
}


foreach

nach obenforeach 

Perl-ähnlich ist einen Foreach-Anweisung, besonders nützlich im Zusammenhang mit Arrays.

                     foreach(<Array> as <Temporärvariable>) 
                     {
                     <Schleifeninhalt>
                     }
                     
                  
Speichert den ersten Wert des Arrays in der Temporärvariable und führt den Schleifeninhalt aus. So kann man z.b. einen Array besonders schön ausgeben:
foreach($myarray as $myvariable) 
{
  echo "$myvariable, ";
}
Beachten Sie dass foreach offensichtlich einen PHP-internen Cursor im Array verwendet, wenn Sie foreach in einer rekursiven Funktion auf ein globales Array anwenden, dann verreckt die Rekursion genau an der Stelle. Verwenden Sie dann lieber whiles (mit lokalen Zählern) oder lokale Arrays.


Echo

nach obenEcho  echo STRING; oder echo (STRING);

"echo" ist die Standardausgabefunktion von PHP. Echo gibt es in zwei Geschmacksrichtungen: einmal als "language construct" wie

echo "hallo welt";
und einmal funktionsähnlich wie
echo ("hallo welt");
. Beide Funktionen bewirken dasselbe, eine Ausgabe in den Browser des benutzers. Wie in Was ist PHP erwähnt, können auch HTML Tags ausgegeben werden.

Zur Ausgabe von Sonderzeichen (wie: neue zeile (im HTML-Quelltext, die Ausgabe an "strategisch wichtigen Stellen", z.B. einer neuen Tabellenzeile ist zu empfehlen, da dann der ausgegebene Quelltext einfacher lesbar ist.), Backslashes, Dollarzeichen und Gänsefüßchen) lesen Sie bitte Arbeiten mit Strings

Ausgabe von Variablen

Variablen werden mit dem Dollarzeichen gekennzeichnet (siehe Variablen) und die Ausgabe erfordert keine speziellen Befehle. echo $i; gibt z.b. die Variable i aus, echo "$i"; tut -Gott sei dank- dasselbe, so bleiben Ausgaben wie "Sie sind der $i te Besucher" übersichtlich. (Ausgabe z.B. "Sie sind der 5 te Besucher")

Anfügen von Strings

Wie in Arbeiten mit Strings beschrieben werden Strings in PHP mit "." aneinandergereiht. Soll der i-te Besucher etwa kein Abstand nach seiner Nummer sehen ist man zu einem Trick gezwungen: da echo "Sie sind der $ite Besucher" nach einer -nicht vorhandenen - Variable namens ite suchen würde verwendet man entweder
echo "Sie sind der ";
echo $i;
echo "te Besucher";
oder das kürzere
echo "Sie sind der ".$i."te Besucher";
Wird an das Ende des Strings "Sie sind der " der String (die Variable $i wird hier als String aufgefasst) $i (Wert z.B. 5) angehängt, an die direkt der String "te Besucher". angehängt wird.


Kurzform von Echo

nach obenKurzform von Echo 

Eine Kurzform von Echo binden Sie mit

<?=$VARIABLENNAMEN?>
in Ihre PHP-Dateien ein. Das funktioniert allerdings nichtb bei jedem Presence Provider.


Header

nach obenHeader  Header (NEUER HEADER);

funktioniert zwar nur am Ausgabenanfang (es darf kein HTML-Quelltext vor der Header-Anwesinung stehen und es darf kein echo erfolgt sein: nicht einmal das Einbinden von Dateien mit Include oder eine Leerzeile darf vor dem PHP-Tag stehen), ist aber ziemlich nützlich. Zum Einen kann man dadurch den Benutzer automatisch zu einer anderen Datei umleiten, zum Anderen erfüllt es Aufgaben, ohne die eine (genormte) Authentifizierung des Benutzers gar nicht möglich wäre.

Header-Ausgaben erfolgen i.d.R. für den Anwender unsichtbar und erscheinen auch nicht in der HTML-Datei.

Umgeleitet wird mit:

<?php
header("Location: http://www.php.net");  
exit;  /* Um keinen weiteren Code dieser Seite auszuführen */
?>
die PHP-Tags habe ich als Erinnerung angeben, sie auf jeden Fall an den Anfang der Datei zu setzen.
<?php

header("http/1.0 404 Not Found");
?>
Gehört eher zum bereich Spielerei solange man vorher nicht authentifiziert und
<?php
  header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");             // Zeitpunkt in der Vergangenheit
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // immer geändert
  header("Cache-Control: no-cache, must-revalidate");           // HTTP/1.1
  header("Pragma: no-cache");                                   // HTTP/1.0
?>
(Quelle: PHP-Manual) ist gut um eine Aufnahme in den Browsercache oder Proxycache zu verhindern.


WWW-Authenticate

nach obenWWW-Authenticate 

Der WWW-Authenticate-Header ist im Grunde nur ein Spezialfall eines Headers (funktioniert nur im Linux-Apache ohne Probleme), ruft aber im Browser folgende wahrscheinlich bekannte Dialogbox auf:

Screenshot einer Standartauthorisierung

<?php
  if(!isset($PHP_AUTH_USER)) {
    Header("WWW-Authenticate: Basic realm=\"Mein Bereich\"");
    Header("HTTP/1.0 401 Unauthorized"); //auf diese Seite wird der User geschickt wenn er sich falsch authorisiert.
    Header("Location: canceled.php");  //auf diese Seite wird der User geschickt wenn er den Cancel-Button klickt.
    exit;
  } else {
    echo "Hallo User $PHP_AUTH_USER mit Passwort $PHP_AUTH_PW.";
  }
?>
Sowohl der Benutzer als auch der Programmierer hat von dieser Standartmethode einige Vorteile: Die Variablen $PHP_AUTH_USER und $PHP_AUTH_PW werden nämlich von Browser auf jeder Folgeseite zur Verfügung gestellt; der Programmierer kann dieselbe Abfrage vor jede zu schützende Datei stellen und muss sich nicht darum kümmern, die Variable ständig weiter zu geben (das erledigt der Browser des Anwenders); der User braucht sich nur einmal einzuloggen (und es ist egal, bei welcher Datei) und kann eventuell vorhandene Passwortmanagementfunktionen des Browsers nutzen.

Dass diese Authorisierung erst richtig Spaß macht, wenn man die Benutzerdaten mit einer Datenbank abgleicht, sollte klar sein (erst dann kann man Möglichkeiten von PHP gegenüber einer einfachen .htaccess Zugriffsbeschränkung ausspielen), auch der Weg ist unglaublich einfach (einfache eine mysql_query auf Select password where user='$PHP_AUTH_USER' ausführen, in etwa so:

  if(!isset($PHP_AUTH_USER)) {
    Header("WWW-Authenticate: Basic realm=\"Mein Bereich\"");
    Header("HTTP/1.0 401 Unauthorized"); //auf diese Seite wird der User geschickt wenn er sich falsch authorisiert.
    Header("Location: canceled.php");  //auf diese Seite wird der User geschickt wenn er den Cancel-Button klickt.
    exit;
  } else { // Benutzername und Passwort liegen vor
    $res=mysql_db_query ("testdb","select password from users where user='$PHP_AUTH_USER'");
    $num=mysql_num_rows($res);
    if ($num==0)/* kein treffender Datensatz*/    
    {
      Header("HTTP/1.0 401 Unauthorized");
      exit;
    } 
    echo "Hallo User $PHP_AUTH_USER mit Passwort $PHP_AUTH_PW.";
  }

Das ist eine ziemlich professionelle Methode, seinen Administartionsbereich zu schützen, weil alle Browser (bspw. IE, Netscape, Mozilla, Opera) solche Authorisierungen unterstützen.

Zwei Dinge noch: Da es Header-Kommandos sind, müssen Sie immer am Anfang der Ausgabe gesendet werden (es darf nicht einmal ein Leerzeichen vor dem <?php stehen) und das macht auch die Einbindung per Include etwas tricky.

Zum Anderen: Vergessen Sie die Zeilen in keiner Datei: Wenn Sie eine Passwortgeschützte Datei verwalte_artikel.php haben aber verwalte_user.php diese Abfrage NICHT hat kann sich jeder bequem zuerst die Rechte geben und dann die Artikel verändern. Ausserdem sehen Sie im Browser ja nicht in welcher Datei Sie die Zeilen vergessen haben könnten, weil Sie ja nur einmal die Aufforderung zum Login erhalten.


Die Mail-Funktion

nach obenDie Mail-Funktion 

Mailen geht in PHP einfach mit

mail("Empfänger","Betreff","Nachricht");


Aber: Ein vierter Parameter ist möglich und gibt die "Extras" an, wie Absenderadresse, Reply-To-adresse oder Kopienempfänger. Reply-To auf den Absender zu setzen ist eine gute Idee, weil man dann im Mailprogramm nur auf Antworten zu klicken braucht um dem Absender auch tatsächlich eine Mail zu schreiben -- ansonsten schickt man die Antwort einem Standardempfänger.
    mail("vetterheinz@provinz.de", "$betreff", $message, "From: $email\nReply-To: $email\nX-Mailer: PHP/" . phpversion(). "\nCC: sie@provider.de");


Maskierung

nach obenMaskierung 

In C-ähnlichen Sprachen werden Zeichen in Strings durch ein Backslash (\) maskiert. Nicht-druckbare Zeichen wie Enter (neue Zeile) werden als n (newline) kodiert und durch einen Backslash wird der Interpreter darauf hingewiesen, dass ein Sonderzeichen folgt. (Daraus ergibt sich, dass der Befehl für "neue Zeile" \n lautet). Weiterhin interessant sind die Codes " (um "Gänsefüßchen" auszugeben, die normalerweise das Ende eines Strings bedeuten würden), \ (um Backslashes anzugeben, die normalerweise den Anfang einer Befehlssequenz bedeuten) und $ (um ein Dollarzeichen auszugeben das normalerweise das darauffolgende Wort als Variable parsen würde). Die Anweisung

$string="\"Hallo\nBackslash\" \\ "
ist jedoch durchaus korrekt: Die Ausgabe lautet
                     "Hallo
                     Backslash" \
                     
                  


Zwei Backslashes im String erzeugen ein sichtbares, drei erzeugen zwei usw..


Aneinanderreihen von Strings

nach obenAneinanderreihen von Strings 

Strings werden in PHP mit "." aneinandergereiht. Soll der i-te Besucher etwa kein Abstand nach seiner Nummer sehen ist man zu einem Trick gezwungen: da echo "Sie sind der $ite Besucher" nach einer -nicht vorhandenen - Variable namens ite suchen würde verwendet man entweder

echo "Sie sind der ";
echo $i;
echo "te Besucher";
oder das kürzere
echo "Sie sind der ".$i."te Besucher";
Wird an das Ende des Strings "Sie sind der " der String (die Variable $i wird hier als String aufgefasst) $i (Wert z.B. 5) angehängt, an die direkt der String "te Besucher". angehängt wird.


strlen

nach obenstrlen  int strlen(<String>)

Strlen gibt Ihnen die Möglichkeit, festzustellen, wie lang der String ist, den Sie bearbeiten.


Explode

nach obenExplode  array explode (<Trennzeichen>, <String>)

teilt einen String in ein Array. (In das erste Element der Abschnitt bis zum ersten Trennzeichen, in das zweite der Abschnitt vom ersten bis zum zweiten Trennzeichen usw. Die Trennzeichen kommen im Array nicht mehr vor.) Trennzeichen sind oft Einzelzeichen, können jedoch auch Strings sein.

  $mitarbieter = "Fr. Müller, Hr. Cambeis, Hr. Schreiber, Hr. Hoffmann, Fr. Gudelke";
  $personen = explode(",", $mitarbeiter);


Implode

nach obenImplode  string implode(<Trennzeichen>, <Array>);

Fügt einen Array aneinander in einen String zusammen, zwischen den Elementen werden Trennzeichen eingebaut. Trennzeichen sind oft Einzelzeichen, können jedoch auch Strings sein.

  $mitarbeiter = array("Fr. Müller", "Hr. Cambeis", "Hr. Schreiber", "Hr. Hoffmann", "Fr. Gudelke");
  $personen = implode(",", $mitarbeiter);
  echo $personen;


htmlentities

nach obenhtmlentities  string htmlentities(<Quelle>);

maskiert alle Umlaute und Sonderzeichen HTML-gerecht. Aus ä wird &auml; usw... und aus < wird &lt; -- was z.B. wichtig ist dass keine Eingabe Javascript-Aktionen auslösen kann.

echo htmlentities ("äöüäüöß<\\"); // gibt aus: äöüäüö<\


rawurldecode

nach obenrawurldecode  string rawurldecode(<Quelle>);

dekodiert eine URL - und ist ziemlich praktisch, wenn man wie in ein Form-Mailer den Query-String anfassen will.

$s=rawurlencode("String mit Sonderzeichen:äöüß&%$§\"\\ /");// Ergibt String%20mit%20Sonderzeichen%3A%E4%F6%FC%DF%26%25%22%5C%20%2F
echo $s;
echo rawurldecode ($s);


substr

nach obensubstr  string substr(<Quelle>, <Start>, [<Länge>] );

liefert aus Quelle ab Start Länge Zeichen zurück. Wenn keine Länge angegeben ist, wird der String ab Start bis zum Ende zurückgeliefert.

  echo substr("Personal Home Page Tools",9); // Home Page Tools
  echo substr("Personal Home Page Tools",9,9); // Home Page


strpos

nach obenstrpos  int strpos(<Gesamtstring>,<Teilstring>)

sucht nach Teilstring in Gesamtstring und gibt seine Position zurück. Wird der Teilstring nicht gefunden, wird der Wert null (nicht 0) zurückgegeben.

  echo strpos("Personal Home Page Tools","Home");
  echo " Searching once more: result=";
 if (strpos("Personal Home Page Tools","Hme")==null) echo "NULL";
  
Eine andere Möglichkeit nur nach dem Vorkommen des Teilstrings im String zu suchen ist strstr(<Gesamtstring>,<Teilstring>) : Diese Funktion liefert einen Boolean ob der Suchstring im Gesamtstring vorkommt.

Sollten Sie jemals auf die Idee kommen, wissen zu wollen, ob ein String am ANFANG eines anderen steht, werden Sie mit strpos Probleme bekommen, weil es 0 zurückliefert, wenn der String am Anfang steht, und null wenn er nicht enthalten ist. Null wird jedoch in allen Fällen in PHP mit ==0 als wahr verglichen, d.h. strpos($haystack, "$needle")==0 ist wahr, wenn der String am Anfang steht oder gar nicht vorkommt. Eine Möglichkeit, das Problem zu umschiffen, wird hier gezeigt:
	$spos=strpos($haystack, $needle);
  	if (is_int($spos)&&($spos==0)) 
  	{
	// $needle steht am Anfang von $haystack
  	}


trim

nach obentrim  string trim (<Quellstring>)

entfernt "Whitespace" vom Anfang und Ende des Quellstrings. Damit sind Leerzeichen, Sonderzeichen wie \n und andere Codes die kein druckbares Zeichen besitzen gemeint.

  echo trim("  \n\n\t\n\n\n dies ist ein whitespace ");


strtolower, strtoupper

nach obenstrtolower, strtoupper  string strtoXXXXX(<Quellstring>)

StrToLower konvertiert alle Zeichen im String in Kleinbuchstaben, StrToUpper in Großbuchstaben.

$s="Dies war gemischte Schreibweise mit Umlaut-ä.";
echo strtolower($s);
echo strtoupper($s);
Die Dokumentation sagt, Umlaute werden nach den lokalen Einstellungen umgewandelt (heißt das z.B. auf amerikanischen Servern überhaupt nicht?), bei mir (PHP/4.0.1 amerikanische Version), werden Sie konvertiert ohne dass ich etwas in der PHP.INI geändert hätte - dort finde ich auch keine Spracheinstellungen.


sprintf

nach obensprintf  string sprintf(<Quellstring>[, <Argumente>])

ist ein mächtiges Werkzeugt, Strings zu formatieren - und das einzige mir bekannte um Fließkommazahlen mit einer definierten Zahl Nachkommastellen auszugeben.

Die Position der Variable wird mit einem Prozentzeichen, ihr Typ mit einem Codebuchstaben festgehalten. Diese sind:

% Für ein echtes Prozentzeichen
b Als Integer, der binär dargestellt wird
c Als Integer, der mit seinem ASCII-Zeichendargestellt wird (siehe auch Funktion chr)
d Als Integer, dezimal dargestellt
f Als Double, Darstellung im Fließkomma-Standard
o Als Integer, der oktal (8-er-Zahlensystem) dargestellt wird
s Als String
x Als Integer, der hexadezimal (16-er-Zahlensystem) dargestellt wird, mit den Kleinbuchstaben a für 10, b für 11 ... f für 16
X Als Integer, der hexadezimal (16-er-Zahlensystem) dargestellt wird, mit den Großbuchstaben A für 10, B für 11 ... F für 16
Bei Fließkommazahlen kann die
<Anzahl der Vorkommastellen>.<Anzahl der Nachkommastellen>
zwischen Prozent und Code (bei Fließkomma: f) stehen.
$fliess=2/3;
echo $fliess;				//0.66666666666667
echo sprintf ("<br>Ergebnis mit sprintf:%1.4f!", $fliess);  	//Ergebnis mit sprintf:0.6667!
								// 1 Vorkomma, 4 Nachkommastellen
sprintf ist auch ein Ideales Werkzeug, um Strings aufzufüllen. In proportionalen Schriften möchte man teilweise dass von einem vom Benutzer eingebener String immer 10 Buchstaben angezeigt werden - wenn nötig mit 5 voranstehenden Leerzeichen. das geht bei sprintf so:
echo sprintf ("<br>%10s", "hallo welt");        // hallo welt
echo sprintf ("<br>%10s", "dies ist");          //   dies ist	
echo sprintf ("<br>%10s", "ein langer Satz");   // ein langer Satz
echo sprintf ("<br>%10.10s", "ein langer Satz");// ein langer 
Beim letzten Beispiel bedeutet 10.10 nicht zehn Vor und 10 Nachkommastellen sondern 10 Stellen UND MAXIMAL 10 Stellen.


str_replace

nach obenstr_replace  string str_replace(<Muster>, <Ersetzung>, <String>)

Ersetzt alle Vorkommnisse von Muster in String durch Ersetzung und liefert das Ergebnis als String zurück.

  $meinstring="Ich bin doch blöd.";
  echo str_replace("blöd", "nicht blöd", $meinstring);


ereg

nach obenereg 

Am Schluss die vielleicht mächtigsten Werkzeuge:

int ereg(<Suchausdruck>, <Vollstring>, [<Zielarray>]);
                     string ereg_replace(<Suchausdruck>, <Ersetzung>, <Vollstring>);
                     
                  
wendet reguläre Ausdrücke auf Strings an (und hat den Programmierern von PHP zweifellos viel Kopfzerbrechen beschert). Mehr zu regulären Ausdrücken lesen Sie z.B. in Perl 5 by Example


Dateien, generell

nach obenDateien, generell 

Auf unserem Testsystem beschäftigen uns nur Windows-Dateien. Linux-Dateien mit Ihren erweiterten Eigenschaften wie Eigentümer, Gruppe, Ausführrechte usw.. können in PHP auch verwaltet werden, lesen Sie dazu bitte die Filesystem functions im Referenzmanual.


Dateien einfach lesen

nach obenDateien einfach lesen 

Wie so oft in der Welt der Informatik ( ;-)) muss man auch in PHP teilweise Dateien lesen. Dies kann man mit file (Dateiname) tun. $datei=file("c:/autoexec.bat"); liest z.b. die gesamte Autoexec.bat in den Array $datei. Jede Indexnummer des Arrays enthält eine Zeile der Autoexec. (Für jeden, er das jetzt für ein Sicherheitsloch hält muss gesagt werden, dass es sich nur um die Autoexec des SERVERS und keinesfalls um die des Anwendungscomputers handeln kann.) Allerdings kann man mit PHP auch Vollzugriff auf den Server gewährleisten -- wenn man z.B. den Benutzer selbst einen Variablennamen eingeben lässt, der dann als Datei geöffnet wird.

Pfadangabe

Pfäde werden in Linux und Windows mit dem Slash getrennt. Eine Angabe mit Backslash ist unter Windows auch möglich, wenn dieser auch als Doppelbackslash (\\) maskiert werden muss (siehe: Stringfunktionen).


URLs einfach lesen

nach obenURLs einfach lesen 

Geht wie Dateien lesen :)

$url=file("http://www.altavista.com/");
schreibt je eine Zeile der HTML-Quelltextes in das Array url. Leichter gehts nicht.


Dateien schreiben

nach obenDateien schreiben 

Geht mit

int fopen(<Dateiname>,<Kommando>), fwrite (<Dateiindex>,<String>) und fclose(<Dateiindex>)
.

Kommandos

r Nur Lesen (von Anfang der Datei)
r+ Lesen und Schreiben (von Anfang der Datei)
w Nur Schreiben (falls Datei vorhanden, überschreiben)
w+ Lesen und Schreiben (falls Datei vorhanden, überschreiben)
a Nur Schreiben (Anhängen an bereits exisitierende Datei, ggf. Datei erstellen)
a+ Lesen und Schreiben (Anhängen an bereits exisitierende Datei, ggf. Datei erstellen)

Bitte beachten

Bitte beachten Sie, dass nur Strings geschrieben werden und das Schreiben des Bytes 0 mit diesen Funktionen nicht funktioniert, weil 0 das Ende eines Strings signalisiert. (Genauso kann man meines Wissens auch mit file() nicht über ein 0 hinweglesen)

URLs

Wenn als Dateiname eine URL angegeben wird, wird dorthin entweder per HTTP oder per FTP geschrieben, je nach Protokoll. Benutzername und Passwort kann in der Form ftp://benutzername:passwort@server.de/verzeichnis/datei angegeben werden.

Beispiele

$fp=fopen("c:/test.txt",w);
fwrite ($fp, "Dies ist ein magischer String\nin zwei Zeilen");
fclose($fp);
---
$fp = fopen("ftp://user:password@netzwelt.com/", "a+");
fwrite ($fp, "Dies ist ein magischer String\nin zwei Zeilen");
fclose($fp);


Verzeichniszugriff

nach obenVerzeichniszugriff 

Erfolgt mit

int opendir(<Pfad>), readdir(<Pfadindex>) und closedir(<Pfadindex>)
$dir=opendir("c:/");
while ($file = readdir($dir))
{
    echo "$file\n";
}
closedir($dir);


Dateiupload

nach obenDateiupload 

Diesen Abschnitt finden Sie in Formulardaten übernehmen.


Funktionen

nach obenFunktionen 

Funktionen definiert man in PHP Javascript-ähnlich: Man gebe das Schlüsselwort function an, den Funktionsnamen und die Parameter, Anfang und Ende der Funktion werden wie üblich mit den geschweiften Klammern { und } bezeichnet,

Rückgabewerte mit return zurückgeliefert.

<?php
echo "2 mal 2 ist ".ergebnis(2);

function ergebnis($zahl)
{
  $res=$zahl*$zahl;
  return $res;
}

?>


Globale, lokale Variablen

nach obenGlobale, lokale Variablen 

Variablen sind in PHP grundsätzlich, Gott sei dank, lokal (nicht wie in Perl!). Will man anderswo verwendete Variablen "importieren" schreibt man vor der Verwendung global <Variablenname>;, examplew.

<?php
echo "2 mal 2 ist ".ergebnis(2);
$res="gleich ";
function ergebnis($zahl)
{
  global $res;
  $res=$res.$zahl*$zahl;
  return $res;
}

?>


Bibliotheken

nach obenBibliotheken 

Dateien, die nur PHP-Funktionen enthalten, werden mit require eingebunden. Anders als in vielen anderen Sprachen gibt es zusätzlich eine Include-Funktion (include "xyz.php";) wobei die mit include eingebundenen Dateien zuerst ausgewertet und dann eingebunden werden. Require arbeitet also schneller. (Um die Verwirrung perfekt zu machen tut require das, was in C++ die Direktive #include tut, ein PHP-ähnliches Include ist im C-Standard nicht vorgesehen weil C-Programme nicht interpretiert, sondern kompiliert werden).

require "dbconnect.phtml";

connecttomydatabase();
Sicherheitshinweis: Sollten Sie die Dateien, die Sie mit require einbinden, nicht .phtml oder .php benannt werden (wie sie Ihre PHP-Dateien bennennen), und wenn diese (z.b. als .inc Datei) nicht vom Webserver ausgeführt, sondern nur angezeigt werden, kann Ihnen ein fremder in den Quelltext Ihrer Bibliotheken spicken. Bei einer beliebten Anwendung von Bibliotheken, der Verwaltung von Datenbankzugriffen kommt so ein Angreifer an Benutzernamen und Passwort Ihrer Datenbank und kann diese von außen manipulieren.


Fehler in Bibliotheken deutlich machen

nach obenFehler in Bibliotheken deutlich machen 

Sollte es möglich sein (und eigentlich ist das immer möglich), Fehler beim Aufruf von Bibliotheksfunktionen zu machen, zum Beispiel indem falsche Argumente übergeben werden, sollten Sie das innerhalb Ihrer Bibliothek mit trigger_error bemängeln. Im Gegensatz zum DIE-Statement hat das den Vorteil, dass die genaue Zeile des Fehlers, eventuell sogar mit einem backtrace, angegeben wird, und die Fehlerbehandlung darauf reagieren kann - bei einem eigenen Error-Handler zum Beispiel mit einer Mail an den Entwickler.


Vorgehensweise

nach obenVorgehensweise 

Neben dem Einschalten der Notices sollte man sich Gedanken machen, ob man sich einen anständigen Debugger beschafft.

Kostenlos gibt es zum Beispiel für Windows ältere Versionen des Maguma Studio. Das ist zwar eine IDE, kein Debugger, aber dummerweise kann z.B. der DBG-Debugger nur durch eine IDE bedient werden, die ihn unterstützt. Sie können Breakpoints setzen, Variablen "watchen", das Programm schrittweise ausführen und damit Fehler sehr schnell erkennen.

Apropos schrittweise ausführen und Fehler erkennen: Mit einem guten eigenen Error-Handler mit debug_backtrace (ab PHP 4.3), sehen Sie nicht nur die Zeile, in der das Problem aufgetreten ist, sondern auch die Zeilen, die die entsprechenden Funktionen aufgerufen haben.

Profiler werden zwar von PHP unterstützt (register_tick_function), dem Autor ist dennoch derzeit kein guter bekannt. (Ein Profiler würde Ihnen sagen, welche Teile des Programms besonders Rechenzeit- oder Speicherintensiv sind). DBG ist zwar auch ein Profiler nur müssten die Informationen aus ausgewertet werden...

Weitere, hoffentlich nützliche Funktionen, die beim Debuggen helfen, sind hier aufgeführt.


print_r

nach obenprint_r  print_r($variable); oder $res=print_r($variable, true)

"print_r gibt Variablen [und damit auch Arrays und Objekte] in einer menschenlesbaren Form aus" und ist damit ziemlich praktisch, wenn man zum Beispiel gerade ohne Debugger debuggt. Leider liefert print_r kein HTML, sondern Plain-Text.

<?
$myObject=new myClass();
?>
<pre>
<?
print_r($myObject);
?>
</pre>

<!-- Ausgabe:
myclass Object
(
    [content] => Array
        (
            [aphorismen] => Array
                (
                    [Michael Richter 1952] => Array
                        (
                            [0] => Fanatismus ist Mord am Zweifel.
                            [1] =>  Kann man Frieden kriegen?
                            [2] => Laß mich bitte zu Ende dazwischen
           reden!
                        )

                )

            [sprueche] => Array
                (
                    [Konfuzius] => Array
                        (
                            [0] => Ein Mensch mag noch so herausragende F?igkeiten
           haben-wenn er arrogant und selbstsüchtig ist, sind sie nichts wert
                        )

                )

        )

)
-->
$myObject=new myClass();
print_r("Bitte auszugebende Variable nicht mit einem String vermischen: $myObject");
// Ausgabe: Bitte auszugebende Variable nicht mit einem String vermischen: Object
class myClass
{
  var $content;
  function myClass()
  {
    $this->content=array(
    "aphorismen"=>
      array("Michael Richter 1952"=>array("Fanatismus ist Mord am Zweifel.",
          " Kann man Frieden kriegen?", "Laß mich bitte zu Ende dazwischen
	   reden!")),
    "sprueche"=>
      array("Konfuzius"=>array("Ein Mensch mag noch so herausragende Fähigkeiten
           haben-wenn er arrogant und selbstsüchtig ist, sind sie nichts wert")));
  }

  function replaceNewLinesSpacesForHTML($input)
  {
    $res=$input;
    $res=str_replace("\n", "<br>",$res);
    $res=str_replace(" ", "&nbsp;",$res);
    return $res;
  }
  function write()
  {
    echo $this->replaceNewLinesSpacesForHTML(print_r($this->content, true));
  }
}

$myObject=new myClass();
echo "print_r in der Klasse mit bool return=true ";
$myObject->write();


trigger_error

nach obentrigger_error  trigger_error($fehlertext[, $typ]);

Es kann an verschiedenen Stellen sinnvoll sein, Fehlermeldungen zu produzieren. Wenn Sie eine Bibliothek schreiben, die Sie oder andere später noch nutzen wollen, ist es oft sinnvoll, statt mit DIE abzubrechen, eine Fehlermeldung per trigger_error zu produzieren.

Das hat zwei Nebenwirkungen, zum Einen, dass das Verhalten einer normalen Fehlermeldung angenommen wird, die zum Beispiel nicht auf dem Bildschirm ausgegeben, sondern direkt per Mail an einen Entwickler verschickt wird (das kann zum Beispiel relativ leicht durch http://www.php.net/manual/de/function.set-error-handler.php bewerkstelligt werden).

Zum Anderen kann es sinnvoll sein, eine solche Fehlermeldung zum Debuggen zu verwenden. trigger_error kann nicht nur "fatale Fehler" produzieren, die das Programm sofort zum Stillstand bringen, sondern auch einfache Warnungen ausgeben.

In beiden Fällen wird der Fehlertext mit der Zeilennummer und der Datei, in der er aufgetreten ist, ausgegeben.

Es macht theoretisch Sinn, die Debug-Anwendungsweise von trigger_error in einer speziellen Funktion zu kapseln. Tut man das nicht, kann man zwar beim Übergang in die Produktionsumgebung anhand eventuell noch auftretender Meldungen sehr schnell die entsprechenden Zeilen erkennen und säubern, jeden Pfad abzulaufen, um alle Meldungen mindestens einmal auftreten zu lassen, um sie zu grillen, ist jedoch nicht besonders zuverlässig, sprich, Sie könnten Meldungen übersehen. Das Problem dabei ist, dass mit DEFINEs keine Funktionen definiert werden können und mit create_function erzeugte Funktionen von PHP nicht als "inline" behandelt werden. Sie bekommen es also nicht hin, die Zeile, in der der Funktionsaufruf stattfand, zurück gegeben zu bekommen, Sie bekommen die Zeile in der selbstdefinierten Funktion.

Ein Kapseln der Bibliotheks-Weise würde dasselbe Problem bei Bibliotheksfunktionen beschwören. Eine Konstante, z.B. E_DEBUG, mit E_USER_ERROR zu definieren, beim Debuggen zu verwenden, und beim Ausliefern auf E_USER_NOTICE zu ändern, ist zwar relativ elegant, und tatsächlich werden normalerweise E_USER_NOTICEs nicht ausgegeben, jedoch sind unsere Produktionsserver so konfiguriert, dass Notices als Warnings zählen. Und es gibt keinen Fehlermeldungstyp, der komplett ignoriert würde. Ein PHP > 4.3 und debug_backtrace() bleiben dem Autor also nicht erspart.

Gültige Werte für "typ" sind:

E_USER_ERROR
E_USER_WARNING
E_USER_NOTICE
<?

error_reporting(E_ALL);

DEFINE("DEBUG_SEVERITY",E_USER_WARNING);
// Beim Ausliefern umstellen auf E_USER_NOTICE!

function mydeb($value)
{
   if ($value===0)
   {
     echo "ERROR:";
     var_dump(debug_backtrace());  // Diese Zeile funktioniert nur mit PHP 4.3 und höher
     trigger_error("Bibliotheksfehler. Bitte myfunc nicht mit 0 aufrufen.", E_USER_ERROR);
   }
}

trigger_error("Hier wird die Funktion korrekt aufgerufen.", DEBUG_SEVERITY);
mydeb("hallo welt");
trigger_error("... und hier falsch.", DEBUG_SEVERITY);
mydeb(0);

/*
Ausgabe:

Warning:  Hier wird die Funktion korrekt aufgerufen. in /srv/www/htdocs/test/testphp.php on line 18

Warning:  ... und hier falsch. in /srv/www/htdocs/test/testphp.php on line 20
ERROR:array(1) {
  [0]=>
  array(4) {
    ["file"]=>
    string(32) "/srv/www/htdocs/test/testphp.php"
    ["line"]=>
    int(21)
    ["function"]=>
    string(5) "mydeb"
    ["args"]=>
    array(1) {
      [0]=>
      &int(0)
    }
  }
}

Fatal error:  Bibliotheksfehler. Bitte myfunc nicht mit 0 aufrufen. in /srv/www/htdocs/test/testphp.php on line 14


*/

?>


set_error_handler

nach obenset_error_handler  set_error_handler($neuerHandler);

Ein eigener Error_Handler kann (ab PHP 4.2) auch durchaus mehr als nur die Zeile zeigen, in der der Fehler aufgeteten ist. Dieser zeigt zum Beispiel auch die Zeile, wo der Fehler hergekommen ist, wenn beispielsweise eine Funktion mit falschen Parametern aufgerufen wurde: Denn dann zeigt er nicht nur die Zeile in der Funktion, sondern auch die Zeile des Aufrufs.

function print_my_backtrace($severity,$arr)
{
$bgColor="808070";
if (($severity==E_COMPILE_ERROR)||($severity==E_CORE_ERROR)||($severity==E_USER_ERROR)||($severity==E_ERROR)) $bgColor="A08070";

echo "<table style='background-color:#$bgColor;color:#ffffff;font-size:8pt;'>";
foreach ($arr as $linebefore)
{
    if (isset($linebefore["file"])) echo "<tr><td>file</td><td>".$linebefore["file"]."</td></tr>";
    echo "<tr><td>function</td><td>".$linebefore["function"]."</td></tr>";
    if (isset($linebefore["line"])) echo "<tr><td>line</td><td>".$linebefore["line"]."</td></tr>";
    echo "<tr><td colspan=2><hr></td></tr>";
}
echo "</table>";
}

function myErrorHandler($errno, $errstr, $errfile, $errline) {
switch ($errno) {
case E_COMPILE_ERROR:
case E_CORE_ERROR:
case E_USER_ERROR:
case E_ERROR:
 echo "<div style='width:50%; border-style: solid; border-width:1px; border-color: red; margin-top:10px;'>\n";
 echo "<br><b>FATAL</b> [$errno] $errstr<br />\n";

 print_my_backtrace($errno,debug_backtrace());

 echo "  Fatal error in line $errline of file $errfile";
 echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
 echo "Aborting...<br />\n";
 echo "</div>\n";
 exit(1);
 break;

case E_PARSE:
case E_COMPILE_WARNING:
case E_CORE_WARNING:
case E_USER_WARNING:
case E_WARNING:
 echo "<div style='width:50%; border-style: solid; border-width:1px; border-color: red; margin-top:10px;'>\n";
 echo "<br><b>ERROR</b> [$errno] $errstr<br />\n";
 print_my_backtrace($errno,debug_backtrace());
 echo "</div>\n";
 break;
case E_USER_NOTICE:
case E_NOTICE:
 echo "<div>\n";
 echo "<br><b>WARNING</b> [$errno] $errstr<br />\n";
 print_my_backtrace($errno,debug_backtrace());
 echo "</div>\n";
 break;
default:
 echo "<div>\n";
 echo "Unkown error type: [$errno] $errstr<br />\n";
 echo "</div>\n";

 break;
}
}

set_error_handler("myErrorHandler");

Kleiner Wermutstropfen: Compiler-Fehler, wie etwa ein falschgeschriebener Funktionsname, die dazu führen, dass PHP das Script nichtmal starten kann, können auch so nicht abgefangen werden.

Devshed hat unter http://www.devshed.com/c/a/PHP/Error-Handling-In-PHP-part-1/5/ übrigens eine Anleitung, wie man auftretende PHP-Fehler in eine MySQL-Tabelle einträgt.


Skalierbarkeit

nach obenSkalierbarkeit 

Was kann ich mit Datenbanken anfangen?

Ziemlich simpel ausgedrückt sind sie Programme, die Daten speichern und anderen Programmen zugänglich machen. Das können einfache Dateien zwar auch, aber Datenbanken sind skalierbar ausgelegt, dass heißt, ich kann eine Datei mit 20 Einträgen anlegen und jeden einzelnen Eintrag sortiert ausgeben. Nehmen wir an die Datei wächst ein bisschen und hat plötzlich 20 Millionen Einträge. Wenn Sie jetzt den 15ten Eintrag löschen wollen müssen Sie zusehen, ob Sie die anderen Daten nach oben wandern lassen oder nicht -- oder in einer Datei einen Index mit Dateinamen führen die auf die Einträge verweisen. dann würden Sie nur noch die Stelle im Index löschen und die Datei auf Ihrer Platte. So ähnlich schaffen es große Datenbanken, in großen Mengen einzufügen, zu ändern und zu löschen ohne viel auf der Platte (oder im Speicher) zu bewegen.


weitere Features

nach obenweitere Features 

Wenn Sie diese Datenmenge sortiert ausgehen wollen, nimmt Ihnen die Datenbank auch diese Arbeit ab. Welcher Sortieralgorithmus in der Datenbank arbeitet kann Ihnen egal sein - Hauptsache, die Daten sind schnell da. Und das sind sie bei Datenbanken in der Regel. Außerdem gruppieren, filtern, suchen, hinzufügen und löschen sie für Sie.


Multi-User-Fähigkeit

nach obenMulti-User-Fähigkeit 

Bis auf wenige Ausnahmen brauchen Sie sich über die Fähigkeit Ihrer Datenbank, mehrere Benutzer gleichzeitig zu betreuen, keine Sorgen zu machen - sie kann es und macht es und in der Regel kommt man sich dabei auch nicht ins Gehege.


Sicherheit

nach obenSicherheit 

Neben ausgefeilten Benutzer- und Netzwerk-Zugriffsrechten bieten Datenbanken auch eine gewisse Sicherheit für Daten: Durch Transaktionen kann zum Beispiel ein logisch konsistenter Zustand der Daten auch bei unvorhergesehenen Ereignissen oder Programmierfehlern gewahrt werden, und Anweisungsblöcke können so gekapselt werden, dass Sie nicht durch andere Anweisungen, die ggf. im Hintergrund laufen würden, beeinflusst würden.


Einsatzgebiet

nach obenEinsatzgebiet 

Datenbanken sind gut für große und/oder wachsende Datenmengen die evtl. zueinander in Bezug stehen und von einer oder mehreren Anwendungen gebraucht werden.

Eine Bestelldatenbank würde zum Beispiel sämtliche Bestellungen die an ein Unternehmen gerichtet sind aufnehmen und für die benötigten Daten von den Programmen zur Auftragsbearbeitung und Rechnungsstellung abgefragt werden. Daten die zueinander in bezug stehen möchte ich hier an einem Beispiel erläutern: Ein Kunde hat bisher 5x bei der Firma bestellt und die Details aller seine Bestellungen liegen noch vor (Artikelnummer, Anzahl). Zusätzlich ist in jeder Bestellungstabelle (relationale Datrenbanken arbeiten mit Tabellen) seine Kundennumer gespeichert, in einer anderen Tabelle steht in einer Spalte jeweils eine Kundennummer und in der anderen Spalte derselben Zeile die zugehörige Anschrift. Die Auftragsbearbeitung fragt also ab: "welche Bestellung ist noch nicht erledigt" und ein Mitarbieter richtet die Posten. Dann fragt die Auftragsverwaltung ab: "welche Kundennummer gehört zu der nicht erledigten Bestellung und welche Anschrift gehört zu der Kundennummer". Dabei werden 2 verschiedene Tabellen angesprochen, diese "gut kombinieren zu können" ist eine wichtige Eigenschaft von relationalen Datenbanken.



Oft wird dem Programmierer über die "Datenbankabfragesprache" SQL mehr Arbeit abgenommen als bloßes speichern. Vielleicht sollten Sie einmal versuchen, gerade Anwendungen die viele kleine Datenstücke speichern, in Datenbanken schreiben zu lassen.


Definition

nach obenDefinition 

SQL (structured query language) wurde in den 1970er bei IBM entwickelt und ist eine Datenbankabfragesprache. Jede Abfrage (und jeder Eintrag, jedes Löschen) einer "seriösen" Datenbank geschieht über SQL oder ist zumindest mit SQL auch möglich. Der Sprachumfang von SQL ist gewaltig, einer der Gründe, warum es offensichtlich keine Konkurrenz zu diesem Standart gibt. SQL-Abfragen werden als String einer Funktion von PHP übergeben, die diese dann an die Datenbank weiterleitet und ausführen lässt.


Vorteile

nach obenVorteile 

SQL bietet die Möglichkeit, Tabellen zu erstellen und zu löschen, Spalten hinzuzufügen und wegzunehmen, Einträge vorzunehmen und zu löschen, zu suchen, sortieren und kombinieren. Ausserdem ist das der einzig anerkannte und auf unterschiedliche Datenbanktypen anwendbare Weg.


Nachteile

nach obenNachteile 

Obwohl SQL als ANSI-Standart vorliegt, haben viele Entwickler Ihre eigenen Datentypen in Ihrer Datenbank über SQL ansprechbar gemacht. Einige Datentypen und Schlüsselwörter funktionieren nur auf einigen Datenbanken und SQL bietet auch nur die Abfrage, den Verbindungsaufbau muss die Programmiersprache wieder selbst leisten. Da sich die Optionen beim Verbindungsaufbau schon bei vielen Datenbanken unterscheiden muss eigentlich jede Datenbankanwendungen auf eine andere Datenbank erst portiert werden (was zugegebenermaßen schnell erledigt ist).

Außerdem ist SQL langsam - mit ODBC geht dann die Geschwindigkeit endgültig den Bach 'runter. Zumindest bei kleinen Datenmengen: Oft kostet der Verbindungsaufbau wertvolle Zehntelsekunden, teilweise genausoviel Zeit, die notwendig ist, um 10000 Datensätze auszulesen. Trotzdem ist SQL schon deshalb langsam (in Programmiererausdrücken: von der Geschwindigkeit eher Visual Basic als Pascal), weil die Statements als Text übergeben werden und mühsam geparst werden müssen. Komisch eigentlich, wäre eine etwas binärerer Sprache schneller und leichter zu realisieren gewesen.


Vorwort

nach obenVorwort 

Diese "Referenz" zeigt nur die allerwichtigsten SQL-Befehle. Für eine ausführlichere Referenz empfehle ich das Buch "SQL. Der Schlüssel zu relationalen Datenbanken".

SQL ist, nebenbei bemerkt, Case Insensitive, d.h. Groß- und Kleinschreibung spielen keine Rolle. Aus Gründen der Übersicht schreibe ich SQL-Statements groß - allein schon aus Abgrenzung zum PHP-Code.

In SQL:


Datentypen in SQL

nach obenDatentypen in SQL 

Die wichtigsten SQL-Datentypen sind

bool TRUE oder FALSE
int Ganzzahlen
char (<Länge>) String fester Länge
varchar (<Länge>) String variabler Länge mit festgesetztem Maximum
long varchar (unter einigen Datenbanken: Text mit mehr als 2048 Zeichen)
dec (<Vorkommastellen>, <Nachkommastellen>) Offensichtlich Festkomma. übersteigt ein Wert die Zahl der möglichen Stellen wird der höchstmögliche Wert gespeichert (bei dec(3,3) und 4321.4321 ist das z.B. 999.999).
float (<Präzision>) Fließkommazahl
date Datumsangabe. Vorsicht: Datumsformate unterscheiden sich in den Datenbanken, MySQL möchte z.B. Daten in der Form 4 Zeichen für Jahr, 2 für Monat und 2 für Tag in der Formatierung YYYY-MM-DD. Außerdem werden Daten meist als Strings übergeben (also bei INSERT unbedingt auf Hochkomma achten). Angesichts all dieser Probleme und den Schwierigkeiten, z.B. herauszufinden wie viele Tage zwischen zwei DATEs lagen, beschränke ich meine Programmierung auf Unix-Timestamps: Die haben den Wert int.


SELECT

nach obenSELECT 

Einfaches SELECT

select ist der erste Schritt für eine Ausgabe aus einer Datenbank. Die Syntax ist
SELECT <Spaltenamen> FROM <Tabellennamen> [WHERE <Bedingung>]
. Es werden immer alle Zeilen einer Datenbank angesprochen, sind alle Spalten gemeint genügt ein *, ansonsten werden Spaltennamen (genau wie Tabellennamen) durch ein Komma getrennt. example.
select * from hauptbuch
selektiert alle Zeilen mit allen Spalten aus hauptbuch. Mit Funktionen, die die Programmiersprachen dann zur Verfügung stehen, können selektierte Einträge z.b. in Strings gespeichert werden (siehe: Eine kleine ODBC-Referenz).
select transaktion, datum from hauptbuch
gewährt dann nur Zugriff auf die Spalten transaktion und datum (nur wenige Spalten auszuwählen vergrößert oft übersicht und verschnellert).

SELECT mit Bedingungen



select * from hauptbuch where (transaktion>40000)
selektiert nur die Zeilen (alle Spalten, da *) einer Tabelle, bei denen Transaktion>40000 ist. Ein weiteres Beispiel ist
select kundennr, letztebestellung from faktura where 
(kundennr=192)


SELECT mit Sortierung



Syntax ist
<selectanweisung> order by <Schlüsselwert>[ascending/descending]
select * from hauptbuch where (transaktion>40000) order by transaktion descending
selektiert z.b. bei einer Tabelle mit den Werten



wobei descending eine fallende Reihenfolge symbolisiert, ascending oder keine Angabe der Reihenfolge sortiert die Selektion in die andere Richtung.

JOINS



Oft werden die Einträge aus einer Tabelle in einer anderen Tabelle als Schlüsselwerte benutzt. Schlüsselwerte sind Werte, die in jeweils nur einer Zeile der Tabelle vorkommen. Z.B. wäre einen Kundendatenbank, in der jedem Kunden eine eindeutige Kundennummer zugeordnet ist, eine Datenbank mit Schlüsselwerten. Haben wir zwei Tabellen Können wir in SQL auch in einem Schritt nach einem Kundennamen suchen und den Zeitpunkt der letzten Bestellung ausgeben. Das nennt man dann einen JOIN aus beiden Tabellen.

Um gleichnamige Spaltennamen in verschiedenen Tabellen unterscheiden zu können schreibt man den Tabellennamen mit einem Punkt vor den Spaltenname:
select hauptbuch.transaktion, hauptbuch.datum from 
                     hauptbuch
                  
sieht zwar dumm aus, das ändert sich aber bei
select * from kunden, bestellungen where ((kunden.kundennr=bestellungen.kundennr) AND (kundenname='Fr. Brisbois'))
liefert Kundennr und Bestellzeitpunkt von Andreas Mühl zurück.

Die letzte anweisung ist für konventionelle Programmierer (zumindest ging es mir so) erst einmal schwer zu verdauen. Es zeit aber auch, das ein where kein if ist: where wird alle Spalten aller Datenbanken angewendet. So funktioniert die Geschichte auch wenn z.B. Anderas Mühl in der Kundendatenbank die erste Zeile innehat, in der Bestelldatenbank aber nur die 5te oder 1000ste.


CREATE

nach obenCREATE 

create ist für uns nur im Zusammenhang mit create table interessant. Es erzeugt eine neue Tabelle in einer Datenbank. der Syntax ist

create table <Tabellenname>  <Spaltenname><Spaltentyp>[,(<Spaltenname><Spaltentyp>... ] )
z.B.:
create table kunden (kundennr int, name varchar(50))



INSERT

nach obenINSERT 

insert into < Tabellenname > values (<Wert>[,<Wert>...])
Beachten Sie, dass der Wert sowohl bei Datumsangaben (i.d.r in der Form 'JAHR-MO-TG') als auch bei Varchars (Strings) in Hochkomma (') eingeschlossen sein muss.

Eine Insert-Anweisung führt logischerweise zu Fehlern, falls nicht die gleiche Anzahl Werte in die Tabelle eingefügt werden soll, wie Spalten vorhanden sind.

Hin und wieder passiert es, dass ein Programmierer insert into tabelle1 value (2001, 'Jahr') schreibt. Ein fehlendes s mach es dem Programmierer da bei der Fehlersuche schwer weil (nicht das korrekte values) value auch als Schlüsselwort erkannt wird.


DELETE

nach obenDELETE 

Zeilen löscht man in SQL mit

DELETE FROM <Tabellenname> WHERE < Bedingung >
Beispiel:
DELETE FROM ANTIQUES WHERE ITEM = 'Ottoman'; 


DROP

nach obenDROP 

Drop ist das delete für ganze Tabellen. Mit

DROP <Tabellenname>
löschen Sie die ganze Tabelle. Ich verwende es oft in den setup-Dateien, den Dateien, die Tabellen in den Datenbanken aufbauen. Vor dem Erstellen der Tabellen versuche man sie oft zu löschen, da ein Erstellen mit geänderten Spalten nicht möglich ist wenn die Tabelle noch in anderer Form existiert.


ALTER

nach obenALTER 

Mit

ALTER TABLE <Tabellenname> ADD/DROP COLUMN <Spaltendefinition>/<Spaltenname>
kann man Tabellen im Nachhinein ändern -- praktisch.

Spalten hinzufügen

Spalten hinzufügen kann man z.B. mit
ALTER TABLE ANTIQUES ADD COLUMN (PRICE INT); 

Spalten löschen

... und Spalten löscht man mit ALTER....DROP:
ALTER TABLE ANTIQUES DROP COLUMN PRICE; 


UPDATE

nach obenUPDATE 

UPDATE <Tabellenname> SET  <Variablenname>=<neuer Wert> WHERE <Bedingung>
ändert einen Eintrag in einer Datenbank (und ist als solches sehr praktisch in Administrationsseiten wo desöfteren in den Datenbankinhalten etwas geändert wird. Man denke nur an Preisänderungen in Online-Shops.)
UPDATE ANTIQUES SET PRICE = 500.00 WHERE ITEM = 'Chair'; 


Vorwort

nach obenVorwort 

Diese "Referenz" zeigt nur die allerwichtigsten MYSQL-Befehle. Für eine ausführlichere Referenz empfehle ich die PHP-Referenz.

Viele dieser Befehle sind ähnlich den ODBC-Befehlen aufgebaut. Durch Ersetzen des MYSQL_ durch ODBC_ erreicht man die ODBC-Befehle. Dabei verliert man jedoch auch viel Performance und z.B. unter Linux ist ODBC sowieso ein Sorgenkind.


MYSQL_CONNECT(...)

nach obenMYSQL_CONNECT(...)  int mysql_connect(string host, string [user], string [password]);

Ist Voraussetzung in jedem PHP-Programm, das MySQL benutzen will. Wenn kein Benutzername und Passwort angegeben werden, wird mit dem aktuell angemeldeten Benutzernamen und Passwort verbunden; was üblicherweise sogar funktioniert. Als Hostnamen können Sie getrost auch auf entfernten Systemen "localhost" verwenden, da die Dateien ja (üblicherweise) jeweils auf dem System gespeichert werden, auf dem sich auch die Datenbank befindet.

Mit MySQL_Connect wird meist auch gleich die Datenbank der Wahl ausgewählt; dies geht mit

mysql_select_db(...)
und ist deshalb hier gleich mit-erwähnt.
mysql_connect('localhost') OR DIE ("Konnte nicht mit MySQL verbinden.");
@mysql_select_db("datenbankname") OR DIE ("Konnte nicht mit Datenbank auf MySQL verbinden.");
Hinweis:
OR DIE (String)
lässt den Programmablauf mit der Meldung String abbrechen falls das Ergebnis der vorangegangenen Operation false war. Der Klammeraffe @ vor dem mysql_select_db verhindert eine Fehlerausgabe (die wird mit OR DIE behandelt).


MYSQL_CLOSE()

nach obenMYSQL_CLOSE() 

Schließt die Verbindung zu MySQL und ist am Ende jedes Programmes, das auch MYSQL_CONNECT benutzt hat, aufzurufen.

mysql_close();


MYSQL_QUERY(...)

nach obenMYSQL_QUERY(...)  int MYSQL_QUERY(string query_string);

führt eine SQL-Anweisung aus. Zurückgeliefert wird false für fehlerhafte Ausführung und ansonsten eine Identifikation des Ergebnisses.

mysql_query ("insert into kueche values ('Messer')");


MYSQL_DB_QUERY(...)

nach obenMYSQL_DB_QUERY(...)  int MYSQL_DB_QUERY(String Datenbank, String query_string);

führt eine SQL-Anweisung auf Datenbank aus. Im Unterschied zu

MYSQL_QUERY
erwartet es jedoch mehrere Ergebnisse: Denn eine Abfrage der Einzelergebnisse wie durch ODBC_RESULT ist in MySQL vermutlich aus Performancegründen tatsächlich nur in ODBC vorgesehen. (ODBC_RESULT muss, um eine komplette Tabelle auszugeben, für jede Spalte eine Ergebnisabfrage starten, MYSQL_FETCH_ARRAY nur für jede Zeile). Zurückgeliefert wird false für fehlerhafte Ausführung und ansonsten eine Identifikation des Ergebnisses.
$result=mysql_db_query ("testdb","select * from kueche");
while ($row=mysql_fetch_object($result))
{
  $geraet=$row->kuechengeraet;
  echo "$geraet ";
}
mysql_free_result($result);


MYSQL_FREE_RESULT(...)

nach obenMYSQL_FREE_RESULT(...)  int MYSQL_FREE_RESULT(int result_id);

gibt den Speicherbereich wieder frei, den MYSQL_DB_QUERY eingenommen hat (und ist nach dem Abfragen der gewünschten Zeilen durch MYSQL_FETCH_OBJECT auch empfohlen).

siehe MYSQL_DB_QUERY-Beispiel.


MYSQL_FETCH_ARRAY(...)

nach obenMYSQL_FETCH_ARRAY(...)  array mysql_fetch_array(int result_id);

Lädt in der Form

array mysql_fetch_object(int result_id);
die nächste Zeile des MYSQL_DB_QUERY in ein Array und liefert true zurück, falls es eine gibt. Das zurückgegebene Array ist i.d.R. assoziativ (es gibt einen weiteren Parameter, der den Typ angibt; dieser Parameter wird allerdings selten genug benötigt um ihn hier nicht zu erwähnen ;)), d.h., ich kann durch Ansprechen von $arrayname->Spaltenname die entsprechenden Spalten auslesen. Der Spaltenname darf dabei nicht in Hochkommas stehen, da es sich bei den Teilen des Arrays um Variablen handelt, die nun mal die gleichen Namen wie die Spalten in der Datenbank bekommen.
siehe MYSQL_DB_QUERY-Beispiel.


MYSQL_RESULT(...)

nach obenMYSQL_RESULT(...)  string mysql_result(int result_id, int row, int column);

Um mehrere Spalte zurückgeliefert zu bekommen ist MYSQL_RESULT aus Performancegründen zum wegschmeißen, verwenden Sie lieber mysql_fetch_object(...) nach einem mysql_db_query(...).

$result=mysql_query("select MAX(id) FROM mydatabase");
$id=mysql_result($result,0,0);
Hinweis: Achten Sie darauf, nie ein Leerzeichen zwischen arithmetischen Funktionen wie MAX, MIN oder AVG und dem Spaltennamen einzufügen.


MYSQL_NUM_ROWS(...)

nach obenMYSQL_NUM_ROWS(...)  int mysql_num_rows(int result_id);

Liefert die Anzahl der Zeilen im Ergebnis zurück. Nützlich z.B. bei MYSQL_DB_QUERYs mit einer Where-Bedingung oder eben um die Anzahl der Datenbankeintragungen zu wissen.

mysql_connect('localhost') OR DIE ("Konnte nicht mit MySQL verbinden.");
@mysql_select_db("testdb") OR DIE ("Konnte nicht mit Datenbank auf MySQL verbinden.");


mysql_query ("drop table kueche"); // da machen wir kein OR DIE dahinter weil die Anwendung
//                                    sonst immer stirbt wenn es kueche noch nicht gab
mysql_query ("create table kueche (kuechengeraet varchar(100))") OR DIE ("invalid request at l2");
mysql_query ("insert into kueche values ('Messer')") OR DIE ("invalid request at l3");
mysql_query ("insert into kueche values ('Gabel')") OR DIE ("invalid request at l4");
mysql_query ("insert into kueche values ('Löffel')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Mixer')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Kochlöffel')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Teelöffel')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Besteck')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Teller')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Toaster')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Tassen')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Sieb')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Topflappen')") OR DIE ("invalid request");
mysql_query ("insert into kueche values ('Topfdeckel')") OR DIE ("invalid request");

$res=mysql_db_query ("testdb","select * from kueche");
$zeile=mysql_num_rows($res)-10;
if ($zeile<0) $zeile=0;
mysql_data_seek($res,$zeile);
while ($row=mysql_fetch_object($res))
{
  $geraet=$row->kuechengeraet;
  echo "$geraet";
}
mysql_free_result($res);
mysql_close();
(obiges Beispiel gibt die 10 letzten Küchengeräte aus ;))


MYSQL_DATA_SEEK(...)

nach obenMYSQL_DATA_SEEK(...)  int MYSQL_DATA_SEEK(int result, int row)

Offensichtlich akzeptiert MYSQL_FETCH_OBJECT keine Angabe, welche Zeile gelesen werden soll -- es ist immer die nächste. Um etwaige Versuche vorwegzunehmen: Auch MYSQL_FETCH_ROW und MYSQL_FETCH_ARRAY nehmen keine solchen Parameter an -- es ist trotzdem möglich.

int MYSQL_DATA_SEEK(int result, int row)
veranlasst MySQL angegebene Zeile als nächstes Ergebnis (z.B. auf eine MYSQL_FETCH_OBJECT-Anfrage) zurückzuliefern. Die jeweils nächste Abfrage wird dann wieder die jeweils nächste Zeile zurückliefern.

Row beginnt bei 0.

Rückgabe: true bei Erfolg, false bei Misserfolg.
siehe MYSQL_NUM_ROWS-Beispiel.


Transaktionen unter MySQL

nach obenTransaktionen unter MySQL 

Datenbanken bieten mit sogenannten Transaktionen die Möglichkeit, mehrere SQL-Anweisungen in einen Block zu kapseln und diesen Block dann "sicher" auszuführen. Sicher bedeutet in diesem Zusammenhang z.B.,

Je anspruchsvoller die Aufgabe, desto eher empfiehlt sich eine Datenbank statt einer Datei. Nehmen wir die o.g. Überweisungsvorgänge im Bankenbereich:

Da wird ein Betrag von einem Konto abgezogen und auf ein anderes gutgeschrieben. Dieser Vorgang muss atomar geschehen, das heißt, ganz oder garnicht. Wird nach einer Gutschrift festgestellt, dass der Überweisende garnicht genug Geld auf seinem Konto hat, wird der gesamte Vorgang rückabgewickelt. Das unterstützen Datenbanken, indem sie die Möglichkeit bieten, mehrere Vorgänge zu sogenannten Transaktionen zusammen zu fassen. Der Programmierer muss sich dann nicht mehr um die Rückabwicklung jedes einzelnen Vorganges kümmern, sondern nur noch entscheiden, ob die gesamte Transaktion durchgeführt (ganz) oder rückabgewickelt (garnicht ausgeführt) wird. Bei Interesse können Sie mal im Internet nach dem ACID-Prinzip für Datenbanken suchen.

Das Behandeln von Transaktionen unter MySQL ist sehr elegant gelöst. Vor Transaktionsbeginn wird dem Datenbanksystem der SQL-Befehl begin übergeben, nach Ende commit zur Ausführung oder rollback zum Verwerfen. Man benötigt transaktionssichere Tabellentypen wie InnoDB oder BDB, dazu wiederum benötigt man die Max-Version von MySQL. Zur Erstellung von transaktionssicheren Tabellen siehe die MySQL-Referenz ( www.mysql.com/doc/en/Table_types.html).
// Dieses Beispiel führt eine Überweisung über 100 Euro von Konto 3301 auf Konto 3302 durch.
// Wenn 3301 zu wenig Geld hat, wird nichts durchgeführt.
// ...
mysql_connect("localhost") OR DIE("Konnte nicht mit localhost verbinden.");
mysql_select_db("test") OR DIE("Konnte Datenbank nicht finden.");


function echoBalance()
{
 mysql_query('begin');
 $res=mysql_query('select * from konto');
 mysql_query('commit');
 echo "<br>";
 while ($row=mysql_fetch_array($res))
 {
    echo "Konto ".$row["kontonummer"]." hat ".$row["kontostand"]."Euro <br>";
 }

}


mysql_query('drop table if exists konto');
mysql_query('create table konto (kontonummer varchar(5), kontostand float) type=InnoDB');
mysql_query('insert into konto (koconnect("localhost") OR DIE("Konnte nicht mit localhost verbinden.");
mysql_select_db("test") OR DIE("Konnte Datenbank nicht finden.");


function echoBalance()
{
 mysql_query('begin');
 $res=mysql_query('select * from konto');
 mysql_query('commit');
 echo "<br>";
 while ($row=mysql_fetch_array($res))
 {
    echo "Konto ".$row["kontonummer"]." hat ".$row["kontostand"]."Euro; <br>";
 }

}


mysql_query('drop table if exists konto');
mysql_query('create table konto (kontonummer varchar(5), kontostand float) type=InnoDB');
mysql_query('insert into konto (kontonummer, kontostand) values (3301, 120.70)');
mysql_query('insert into konto (kontonummer, kontostand) values (3302, 11)');
echo "Vor der ersten Transaktion";
echoBalance();

mysql_query('begin');
mysql_query('update konto set kontostand=kontostand-100 where kontonummer=3301');
mysql_query('update konto set kontostand=kontostand+100 where kontonummer=3302');
$res=mysql_query('select kontostand from konto');
$kontostand=mysql_result($res,0,0);
if ($kontostand<0)
{mysql_query('rollback');}
else
{mysql_query('commit');}
echo "Nach der ersten Transaktion";
echoBalance();

mysql_query('begin');  // Die gleiche Transaktion nochmal
mysql_query('update konto set kontostand=kontostand-100 where kontonummer=3301');
mysql_query('update konto set kontostand=kontostand+100 where kontonummer=3302');
$res=mysql_query('select kontostand from konto');
$kontostand=mysql_result($res,0,0);
if ($kontostand<0)
{mysql_query('rollback');}
else
{mysql_query('commit');}

echo "Nach der zweiten, hoffentlich fehlgeschlagenen Transaktion";
echoBalance();
// ...
zu beachten ist, dass man innerhalb der Transaktion mit select bereits einen Stand lesen kann, den man nur logisch (also ohne Commit auszulösen) geschrieben hat.


ODBC: Microsoft-Datenbank-Treiber

nach obenODBC: Microsoft-Datenbank-Treiber 

Open DataBase Connectivity ist ein Microsoft-eigenes (unter Linux nur sehr wenig verbreitetes) Treiberprofil für SQL-Datenbanken.

ODBC ist wie SQL Datenbankunabhängig, übernimmt aber Funktionen die SQL nicht kennt: Zum Beispiel muss überhaupt erst einmal eine Verbindung zur Datenbank hergestellt werden (hier ist der Name Programm: database connectivity). Der Verbindungsauf- und abbau (z.B.) ist außer in ODBC auch nicht standartisiert

 int mysql_connect(string [hostname [:port] [:/path/to/socket] ] , string [username] , string [password] ); 
                     v.s.
                     int ibase_connect (string database [, string username [, string password [, string charset [, int buffers [, int dialect [,
                     string role]]]]]])
                     
                  
, MySQL erlaubt es dem Benutzer im Gegensatz zu Interbase-Beispiel während einer aktiven Verbindung den Benutzer zu wechseln u.m..

Fast alle größeren Datenbanken wie mySQL, SQL Server, Oracle, DB/2 usw. liefern ODBC-Treiber für Windows mit Ihren Produkten aus. Diese Treiber werden in vielen Programmiersprachen (C++, Perl) in speziellen Bibliotheken zur Verfügung gestellt, in PHP ist keine Einbindung einer Bibliothek nötig.

In den verschiedenen Programmiersprachen unterscheiden sich die ODBC_Kommandos oft nur durch den Funktionsnamen (In PHP alle odbc_-Funktionen).


Progammierersicht: DSNs und entfernte Datenbanken

nach obenProgammierersicht: DSNs und entfernte Datenbanken 

Unter Systemsteuerung/32-Bit ODBC können dort DSNs (Data Source Names) angelegt werden, die ODBC-Datenbanken einen Namen geben. Neben der DSN können dort Benutzername und Passwort angegeben werden, sodass ein ODBC-Aufruf dann im Prinzip nur noch sagt: öffne ODBC-Quelle Datenbank1 und damit wird z.b. db1 mit dem Benutzer sysdba und dem passwort changeme "aktiviert". ODBC-DSNs müssen nicht auf dem lokalen Computer vorliegen: auch Datenbanken im lokalen Netzwerk können angesprpochen werden. Auch übernimmt ODBC eine Weitervermittlung der SQL-Anfragen bzw. Ergebnisse. In PHP gibt es z.b. verschiedene Implementationen von EXEC (also em Ausführen eines SQL-Kommandos), eine für mySql (mysql_exec), und eine für Interbase (ibase_exec) (und weitere), weil jeweils eine andere Bibliothek angesporchen werden muss. Auch das entfällt unter ODBC (dort heißt die Sache für alle angeschlossenen Datenbanken odbc_exec). Die ODBC-Kommandos unterscheiden sich nur geringfügig von den Datenbank-Spezialisierten SQL-Aufrufen.


Vorteile

nach obenVorteile 

ODBC-Vorteile sind also datenbankunabhängige Entwicklung (sowohl der Programmiertools als auch der Anwendungen) mit einer konsequenteren Unterstützung entfernter Datenbanken (Datenbanken die über das Intra- oder Internet angesprochen werden),


Nachteile

nach obenNachteile 

Nachteile sind die oft quälende Langsamkeit (odbc-sql-kommando sind schmerzhaft langsamer als die Datenbanknativen), Microsoft-Fixiertheit (von Linux keine Spur), die Notwendigkeit, eigene Treiber für die Datenbanken zu installieren und Datenbanken u.U. selbst eintragen zu müssen (siehe ein eigenes Testsystem aufbauen).


Vorwort

nach obenVorwort 

Diese "Referenz" zeigt nur die allerwichtigsten ODBC-Befehle. Für eine ausführlichere Referenz empfehle ich die PHP-Referenz.

Viele dieser Befehle sind ähnlich den Datenbankspezifischen aufgebaut. Durch Ersetzen des ODBC_ durch z.B. IBASE_ oder MYSQL_ erreicht man eine höhere Performance und ist nicht auf ODBC angewiesen. Dabei verliert man jedoch auch die Möglichkeit, mit anderen Datenbanken zusammenzusarbeiten (für diese müsste dann der gesamte Code geändert werden). Zu den MySQL-Befehlen lesen Sie bitte auch die MySQL-Referenz.


ODBC_CONNECT(...)

nach obenODBC_CONNECT(...)  int odbc_connect(string dsn, string user, string password, int [cursor_type]);

Ist Voraussetzung in jedem PHP-Programm, das ODBC benutzt. DSNs vergibt man z.B. in der Windows-Systemsteuerung (siehe: ein eigenes Testsystem aufbauen ), Benutzername und Passwort vergibt man sich in der Datenbank(-Verwaltung). Als Cursor-Type ist SQL_CUR_USE_ODBC zu empfehlen. Der Rückgabewert der Funktion identifiziert im weiteren Programmverlauf die Datenbank, ist also wichtig.

$dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);


ODBC_CLOSE_ALL()

nach obenODBC_CLOSE_ALL() 

Schließt alle Datenbankverbindungen und ist am Ende jedes Programmes, das ODBC_CONNECT benutzt hat, aufzurufen.

ODBC_CLOSE(int id)
ist zwar ähnlich, aber ODBC_CLOSE_ALL() ist komfortabler und sicherer -- falls man dann doch mal vergessen sollte eine Datenbank zu schließen.
odbc_close_all();


ODBC_EXEC(...)

nach obenODBC_EXEC(...)  int ODBC_EXEC(int connection_id, String query_string);

führt eine SQL-Anweisung aus. übergeben müssen Sie die ID der Datenbank und den SQL-String. Zurückgeliefert wird false für fehlerhafte Ausführung und ansonsten eine Identifikation des Ergebnisses (z.B. bei Select-Anweisungen wichtig).

$res=odbc_exec ($dbh, "insert into kueche values ('Messer')");


ODBC_FETCH_ROW(...)

nach obenODBC_FETCH_ROW(...)  int odbc_fetch_row(int result_id, int [row_number]);

Lädt in der Form

int odbc_fetch_row(int result_id);
die nächste Zeile und liefert true zurück, falls es eine gibt und in der Form
int odbc_fetch_row(int result_id, int row_number);
lädt es eine bestimmte Zeile.
while(odbc_fetch_row($res)) 
{
   $name=odbc_result("Name"); // angenommen die Datenbank hat eine Spalte namens Name
   echo " $name ";
}


ODBC_RESULT(...)

nach obenODBC_RESULT(...)  string odbc_result(int result_id, mixed field);

"field" ist lediglich entweder die Nummer oder der Name der zurückzuliefernden Spalte. Hierbei ist der Name zu empfehlen, weil sich ansonsten Fehler drohen wenn an den Tabellen noch "geschraubt" wird. Nur nach ODBC_FETCH_ROW(...) anzuwenden ! Um jede Spalte zurückgeliefert zu bekommen ist ODBC_RESULT aus Performancegründen eigentlich eine Todsünde, jedoch in ODBC schwer anders zu realisieren (unter MYSQL geht da die Sache z.b. mit mysql_fetch_object(...) nach einem mysql_db_query(...), in ODBC z.B. mit ODBC_FETCH_INTO()).

while(odbc_fetch_row($res)) 
{
   $name=odbc_result($res,"Name"); // angenommen die Datenbank hat eine Spalte namens Name
   echo " $name ";
}
Hinweis: ODBC_RESULT gibt Ihnen bei SQL-Anfragen wie
SELECT MAX(ID) FROM MYTABLE 
oder
SELECT AVG (INCOME) FROM HOUSEHOLDS
, also bei Anfragen in der kein Spaltenname existiert, das Ergebnis entweder in der 0ten Spalte zurück oder in dem feld mit der Arithmetischen Operation.
 $res=odbc_exec ($dbh, "SELECT MAX(name) FROM NAMEN");
 if odbc_fetch_row($res)
 {
   $name=odbc_result($res,0); 
   $name=odbc_result($res,"MAX(name)"); 

 }


ODBC_NUM_ROWS(...)

nach obenODBC_NUM_ROWS(...)  int odbc_num_rows(int result_id);

Liefert die Anzahl der Zeilen im Ergebnis zurück. Nützlich z.B. bei Selects mit einer Where-Bedingung oder eben um die Anzahl der Datenbankeintragungen zu wissen.

$zeile=odbc_num_rows($res)-10;
if ($zeile<0) $zeile=0;
while(odbc_fetch_row($res,$zeile))
{
   $zeile++;
   $name=odbc_result("Name"); // angenommen die Datenbank hat eine Spalte namens Name
   echo " $name ";  // gibt die letzten 10 Namen aus.
}


ODBC_AUTOCOMMIT(...)

nach obenODBC_AUTOCOMMIT(...)  bool odbc_autocommit(resource connection_id [,bool OnOff]);

Legt fest, ob nach jedem ODBC-Befehl automatisch ein Datenbank-Commit ausgelöst wird (Standard: On). Wenn ja, wird jeder Befehl als abgeschlossene Transaktion behandelt. Die Einstellung Off erlaubt also, mehrere ODBC-Befehle zu einer Transaktion zusammen zu fassen.

// Dieses Beispiel führt eine Überweisung über $betrag von $konto1 auf $konto2 durch.
// Wenn ein Konto nicht existiert, wird weder die Abbuchung, noch die Zubuchung durchgeführt
$dbh=odbc_connect('bank','','',SQL_CUR_USE_ODBC);
odbc_autocommit($dbh,"Off");
odbc_exec($dbh,'update konto set kontostand=kontostand-'.$betrag.' where kontonummer='.$konto1);
odbc_autocommit($dbh,"On");
// Beim nächsten ODBC-Befehl wird das DB-Commit ausgelöst
odbc_exec($dbh,'update konto set kontostand=kontostand+'.$betrag.' where kontonummer='.$konto2);
// ...


ODBC_COMMIT(...)

nach obenODBC_COMMIT(...)  bool odbc_commit(resource connection_id);

Löst das ODBC-Commit manuell (nicht-automatisch) aus. Dadurch wird die Transaktion ganz durchgeführt. Komplementär ist int odbc_rollback(int connection_id), welches das Datenbanksystem veranlasst, die Transaktion garnicht durchzuführen.

// Dieses Beispiel führt eine Überweisung über $betrag von $konto1 auf $konto2 durch.
// Wenn konto1 zu wenig Geld hat, wird nichts durchgeführt.
$dbh=@odbc_connect('bank','','',SQL_CUR_USE_ODBC);
odbc_autocommit($dbh,"Off");
odbc_exec($dbh,'update konto set kontostand=kontostand-'.$betrag.' where kontonummer='.$konto1);
odbc_exec($dbh,'update konto set kontostand=kontostand+'.$betrag.' where kontonummer='.$konto2);
$res=odbc_exec($dbh,'select kontostand from konto where kontonummer='.$konto1);
$kontostand=odbc_result($res,'kontostand');
if ($kontostand<0)
{odbc_rollback($dbh);}
else
{odbc_commit($dbh);}
// ...


Einführung

nach obenEinführung 

Counter arbeiten normalerweise mit aus Ziffern erzeugten Bildern oder als Bilder zurückgegebenen Strings. Das geht unter PHP zwar auch aber nur mit der gd-Bibliothek (die Bilder erzeugen kann). Diese Bibliothek ist allerdings nicht in unserem Testsystem vorgesehen und überhaupt (sie ist TIERISCH schwer zu installieren, zumindest unter Linux) eher selten installiert. Ausserdem ist diese Lösung irgendwie witzlos und so möchte ich einen anderen Lösungsweg vorschlagen: Wir binden als externe JavaScript-Datei eine PHP-Datei ein. Diese liefert einen Javascript-Quelltext -- und zwar die funktion getVisitors die als Integer die Anzahl der Besucher zurückliefert. Mit

<script language="JavaScript">
document.writeln(getVisitors());
</script>
kann man diese dann in der HTML-Datei ausgeben - oder auch nicht, falls nicht gewünscht. Vorteil: Extrem wenig Daten (Text statt Grafik) werden übermittelt, die Daten bleiben dynamisch (JavaScript-Funtkionen könnten die Ziffern auch mit Bildern anzeigen oder bei dem 1000sten Besucher Alarm schlagen), es funktioniert unter allen Browsern, man muss die Visitors nicht darstellen falls ein stiller Counter gewünscht ist und die Sache braucht erheblich weniger Rechenaufwand als eine Grafiklösung (für Serverbetreiber interessant).

Nachteil

bei ausgeschaltetem JavaScript funktioniert gar nichts und Netscape ist pingelig: sollte der Counter ausfallen sieht man unter netscape auch die dahinterliegende Seite nicht.

Features

Unser Counter soll neben den Anzahlen der Besuchen für den Administrator auch festhalten, zu welchem Zeitpunkt von welcher IP ein Zugriff erfolgte (um später Statistiken erzeugen zu können, teilweise müsste man dazu eben doch die GD-Bibliothek benutzen) und von welcher URL der Benutzer kam (wichtig um z.b. den Erfolg von eintragungen in Suchmaschinen zu sehen). Hierzu bieten sich (Filter, Gruppierfunktionen, Skalierbarkeit) wieder einmal Datenbanken an.


Erste Schritte

nach obenErste Schritte 

Als HTML-Datei brauchen wir wie gesagt eine Datei, die eine externe Datei mit <script src=""> einbindet.


<html>
<script language="JavaScript" src="counter.php">

</script>

<body>
Sie sind der <b><script>document.write(getVisitors());</script></b>te Besucher
</body>

</html>
und als PHP-Datei (im Besipiel angesprochen: counter.php) schreiben wir zuesrt einmal eine PHP-Datei die nur eine mit-eins-belegter Variable ausgibt.
function getVisitors()
{
  <?php  $i=1; ?>
  return <?php echo "$i"; ?>;
}
Beachten Sie dabei, dass die Variable im ersten Block <?php... ?> im zweiten unverändert vorhanden sind. So gesehen sind die Blöcke also global. Ausserdem ist es hier verlockend function getVisitors() { $i=1; <?php echo $i; ?> } zu schreiben, was aber nicht funktioniert, weil dann $i bestenfalls eine Javascript-Variable ist und PHP sie nicht einsehen, geschweige denn mit echo ausgeben kann.


Zugriff in die Datenbank schreiben

nach obenZugriff in die Datenbank schreiben 

Um den Zugriff in eine Datenbank zu schreiben, muss dort zuerst die entsprechende Tabelle vorliegen. Diese erzeugen wir mit einer Datei namens Setup.php. Die Datenbank soll enthalten:

Ein Feld für die IP (text)

Ein Feld für die Zugriffszeit (integer)

und ein Feld für die vorherige Seite (text).

Das Datumsfeld machen wir Integer um ein Unix-Timestamp einfügen zu können - mit den Dingern lässt sich i.d.R. viel leichter Rechnen als mit Formatierten Datumsangaben.

Das machen wir mit einer Datei setup.php die ähnlich des ersten Teiles der Beispieldatei im Einrichten eines eigenen Testsystems aufgebaut ist - und die gleiche Funktion hat.

<html>
Starte Setup für Counter....
<?php
$dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
echo "Connected to db $dbh<br>";

$query = "drop table counter";
$res = odbc_exec($dbh, $query);
$query = "create table counter (zeitpunkt int, ip char(15), refferer char(255))";
$res = odbc_exec($dbh, $query);
odbc_close_all();
?>
... beendet
</html>
Die Tabelle wird gedroppt bevor sie angelegt wird um Änderungen (z.b. Einfügen eines neuen Feldes) später wirklich wirksam zu machen und nicht an einer Fehlermeldung a lá die Tabelle existiert schon scheitern zu lassen.

Diese Datei sollte man allerdings nach Gebrauch in ein anderes Verzeichnis das nicht von außen angegriffen werden kann verschieben, weil ansonsten jemand auf die Idee kommen könnte pfadname/setup.php aufzurufen und die gesamten counterdaten (drop table) löscht. Ein Umbenennen in ein Format das nicht automatisch ausgeführt wird (z.b. in setup.html) wäre noch um einiges schlimmer, da im odbc-connect, das dann im Quelltext im Klartext stünde, alle Daten (Benutzername und Passwort) der Datenbank enthält und Datenbanken i.d.R. auch von außen über eine Internetverbindung abgefragt werden können - Vollzugriff mit lesen, schreiben, ändern und löschen für den Angreifer wären die Folge.

Zurück zum eigentlichen: Die Datei counter.php müsste jetzt noch folgendermaßen geändert werden:
function getVisitors()
{
<?php
  $dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
  $now = time();
  $ip = getenv("REMOTE_ADDR");
  $ref = getenv("HTTP_REFERER");
  $query = "insert into counter values ($now, '$ip', '$ref')";
  $res = odbc_exec($dbh, $query);
  $query = "select * from counter";
  $res = odbc_exec($dbh, $query);
  $numrows = odbc_num_rows($res);

  odbc_close_all();
?>
  return <?php echo "$numrows"; ?>;
}
time() gibt den schon angesprochenen Unix-Timestamp zurück (auch unter Windows ;)), REMOTE_ADDR und HTTP_REFERER sind Umgebungsvariablen die IP des Users anzeigen bzw. wo er herkommt und odbc_num_rows gibt die Anzahl der Datensätze zurück.

Zu schade, dass HTTP_REFERER immer den Namen der HTML-Datei zurückgibt, da diese die scheinbar letzte ist, die vor der PHP-Datei "angezeigt" wird. Ein weiterer Schwachpunkt meiner Counter-Einbindung, aber ich denke, die Vorteile überwiegen dennoch.


Todo's

nach obenTodo's 

Als to-do braucht der Programmierer noch unbedingt


Warum ein Form-Mailer

nach obenWarum ein Form-Mailer 

Forms mit der Methode "mailto" gelten als unprofessionell weil es sehr viele Browser gibt, deren eMail-Versand nicht funktioniert. Beispielsweise surfen Sie mit Netscape und benutzen Outlook Express zum mailen -- unter Umständen haben Sie in Netscape gar kein Konto zum Versenden eingerichtet. Oder Sie sind im Internet-Cafe, benutzen einen Browser wie Lynx, oder Sie sind bei einem Freund und möchten die Antwort auf Ihr eigenes eMail-Konto. Auch möchte man gerne dem Benutzer nach Klick auf Absenden einen Text zeigen wie "Ihre Mail wird umgehend bearbeitet".


Was uns nichts nützt

nach obenWas uns nichts nützt 

Wie in Formulardaten übernehmen gesehen verarbeitet PHP die übergebenen Variablen sehr komfortabel. Die einzige Ausnahme, wo das nichts bringt, ist die, bei der man nicht weiß wie die Variablen heißen, die übergeben werden sollen. Das ist hier leider hier der Fall, denn um den Form-Mailer so universell wie möglich zu halten, sollen alle übergebenen Variablen gemailt werden.

Wir bedienen uns also den beiden PHP-Internen Arrays die die Formulardaten speichern:

if (!empty($HTTP_POST_VARS)) {
	while(list($name, $value) = each($HTTP_POST_VARS))
		$message.="$name = $value\n";
	
}
Fragt z.b. die HTTP_POST_VARS ab. Diese werden belegt, wenn ein Formular mit der Methode "Post" übergeben wird. Um Kompatibilität zu erreichen und "weils nicht viel kost'" machen wir das auch mit den Get-Variablen (obwohl es eigentlich nicht empfehlenswert ist, irgendwelche Formulare per Get zu übermitteln: Diese werden dann in die URL kodiert und können z.B. in Logdateien zu finden sein oder im Verlaufs-Cache des Browsers).

Zur Funktion: Erst wird überprüft, ob das Array HTTP_POST_VARS leer ist ansonsten wird, solange es ein neues Element gibt (while (...=each)), die zwei Elemente die darin als Liste vorliegen in den Variablen $name und $value gepeichert. Die Nchricht wird sodann um die Zeile Name = Wert ergänzt.


Die Mail-Funkion

nach obenDie Mail-Funkion 

ist in Ausgabefunktionen genau beschrieben.


Mailen der Variablen

nach obenMailen der Variablen 

Diese Zeilen

 	$message=rawurldecode($message);
	$message=str_replace("+", " ", $message);		

    mail("vetterheinz@provinz.de", "$betreff", $message, "From: $email\nReply-To: $email\nX-Mailer: PHP/" . phpversion(). "\nCC: sie@provider.de");
Bereiten die Nachricht auf den Versand vor (sollte Sie aus Get-Variablen bestehen muss sie nämlich noch dekodiert werden, eine dekodierung von POST-Variablen macht zumindest nichts kaputt) schickt sie an vetterheinz@proinz.de. Eine Kopie geht an sie@provider.de; falls sie keine Kopien versenden wollen, löschen Sie einfach den letzten Teil der Mail-Funktion ". "\nCC: sie@provider.de"" (ohne die äußeren Hochkommata).

die Punkte fügen wie gewohnt die Strings zusammen (objektierientierte Programmierer: lasst euch hier nicht verarschen). Die Variablen betreff und email sollten im Formular definiert sein und sich selbst erklären. Betreff kann man hidden deklarieren und mit value zuweisen, email ist die Absenderadresse. Diese wird übrigens als reply-to benutzt; möchten Sie also auf eine solche Mail vom Form-Mailer im Posteingang antworten, genügt ein freundlicher Klick auf "Antworten".


Mehr Komfort

nach obenMehr Komfort 

Diese Fassung des Form-Mailers gibt noch eine Variable namens thankyou als Text aus - diese definiere ich im Formular versteckt und da kommt dass z.B. rein: Vielen Dank für Ihr Feedback. Ausserdem hat der Benutzer die Möglichkeit, per Link auf die Datei backfromform.html zu springen -- diese muss dann im entsprechenden Verzeichnis noch existieren. Ansonsten kann man das Ding tatsächlich gebrauchen. Und ehe ich es vergesse: Ändern Sie die CC-Adresse (sie@provider.de) und die Empfängeradresse (vetterheinz@provinz.de) nach Ihren Bedürfnissen.

PS: Die Absenderadresse zu ändern geht hier ganz leicht - mit entsprechender Eingabe im Formular. Das ist ein (vom mailserver abhängig) cooler Weg, Freunde glauben zu machen, jemand anders, den sie kennen, hätte ihnen die Mail geschickt.

Beispiels-HTML-Datei:

<HTML>  
<HEAD>
<TITLE>Formular</TITLE>
</HEAD>
<BODY>
<form action="formmailer.php" method="post">
  <input type="hidden" name="thankyou" value="Vielen Dank für Ihre Mail.">
  <input type="hidden" name="betreff" value="Mail vom Form-Mailer.">
  Ihre eMail-Adresse:<input name="eMail"><br>
  Straße: <input name="beliebige"><br>
  Ort: <input name="elemente"><br>
  Nachricht: <textarea name="werdenuebermittelt"></textarea><br>
  <input type="submit"><input type="reset"><br>
</form>
</BODY>
</HTML>
Beispiels-PHP-Datei:
<HTML>  
<!-- Mailbearbeitung: Angabe absender in email, betreff in betreff, nachricht dass Feedback/bestellung abgeschickt wurde in thankyou 
WICHTIG: Formular leitet zurück auf Seite backfromform.html, diese muss existieren und sollte die Ebene über dem Mailformular sein.
Alternativ ändern Sie bitte den Eintrag in javascript:history.back(); o.Ä.

Bitte Empfänger (vetterheinz@provinz.de) und CC-Empfänger (sie@provider.de) nach Bedarf ändern. 
-->
<HEAD>
<TITLE>eMail versandt</TITLE>
</HEAD>
<BODY>

  <?
        $message = "";
	if (!empty($HTTP_GET_VARS)) 
	{
		while(list($name, $value) = each($HTTP_GET_VARS))
		$message.="$name = $value\n";
	}
	if (!empty($HTTP_POST_VARS)) 
	{
		while(list($name, $value) = each($HTTP_POST_VARS))
		$message.="$name = $value\n";
	}
		
 	$message=rawurldecode($message);
	$message=str_replace("+", " ", $message);		

    mail("vetterheinz@provinz.de", "$betreff", $message, "From: $email\nReply-To: $email\nX-Mailer: PHP/" . phpversion(). "\nCC: sie@provider.de");
  
  
  ?>
  <font face="Verdana, Arial, Helvetica">
  <center><b><p/>
  <? 
  echo $thankyou;
  ?>
  <p/>
  <a href="backfromform.html">Zurück</a> zur Indexseite
  </font>
  </b>
  </center>
</BODY>
</HTML>


Eine simple HTML-Datei

nach obenEine simple HTML-Datei 

Zuerst müssen wir in einer HTML-Datei die Eingabe von Namen und Eintrag per Formular vorsehen. Die Datei bennenen wir in weise Voraussicht schonmal gaestebuch.php da wir beabsichtigen, später noch vor dem Formular die Gästebuch-Eintragungen auszugeben.

<html>
<body>
<table><!-- Hier kommt die Tabelle mit den bisherigen Einträgen rein -->
</table>
<form action="guestbook.php"><!-- Warum wir hier die eigene Datei angeben wird in Abschnitt 3 erklärt-->
Ihr Name:<input name="name"><br>
Ihr Eintrag:<textarea name="eintrag">
</textarea>        <br>
<input type=submit><input type=reset>
</form>

</body>
</html>


Der Datenbank-Aufbau

nach obenDer Datenbank-Aufbau 

Die Datenbank wird im wesentlichen dieselben Felder enthalten wie der Counter: Nämlich IP, Zeitpunkt und vorige Seite des Besuchers, zusätzlich jedoch noch Name und Eintrag des Absenders:

<html>
Starte Setup für Gästebuch....
<?php
$dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
echo "Connected to db $dbh<br>";
	
$query = "drop table guests";
$res = odbc_exec($dbh, $query);
$query = "create table guests (zeitpunkt int, ip char(15), refferer char(255), name char(50), eintrag long varchar)";
$res = odbc_exec($dbh, $query);
odbc_close_all();
?>
... beendet
</html>
  


Eintrag in die Datenbank

nach obenEintrag in die Datenbank 

Der Eintrag in die Datenbank erfolgt mir den Zeilen

  $dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
  $now = time();
  $ip = getenv("REMOTE_ADDR");
  $ref = getenv("HTTP_REFERER");
  $query = "insert into guests values ($now, '$ip', '$ref', '$name', '$eintrag')";
 if ($eintrag!="")
 {
   echo "Eingetragen";
   $res = odbc_exec($dbh, $query);
 }
Der Grund, warum ODBC_EXEC nur Aufgerufen wird ist, falls überhaupt die Variable Eintrag angegeben ist, der, dass wir so dieselbe Datei für Ein- und Ausgabe verwenden können. Ruft man gaestebuch.php ohne Parameter auf, wird nur der Inhalt der Datenbank angezeigt, trägt man einen Eintrag ein und wird geastebuch.php durch das Formular aufgerufen ist ja die Variable eintrag definiert- der neue Eintrag wird durchgeführt und gleich angezeigt. So vermeidet man Programmierfehler und fehlende Refreshs des Browsers.


Ausgabe in der HTML-Datei

nach obenAusgabe in der HTML-Datei 

Diese Aufgabe erweist sich als gewöhnliche Ausgabe einer Datenbank. Zusammen mit ein paar Tags zur Tabellenformatierung sieht das dann ungefähr so aus:

  $query = "select * from guests";
  $res = odbc_exec($dbh, $query);
  while (odbc_fetch_row($res))
  {
   $thename=odbc_result($res,"name");
   $theeintrag=odbc_result($res,"eintrag");
   echo "<tr><td>$thename</td><td>$theeintrag</td></tr>";
  }


Die Dateien in der Übersicht

nach obenDie Dateien in der Übersicht 

<html>
Starte Setup für Gästebuch....
<?php
$dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
echo "Connected to db $dbh<br>";
	
$query = "drop table guests";
$res = odbc_exec($dbh, $query);
$query = "create table guests (zeitpunkt int, ip char(15), refferer char(255), name char(50), eintrag long varchar)";
$res = odbc_exec($dbh, $query);
odbc_close_all();
?>
... beendet
</html>
<html>
<body>
<table>
<?php
  $dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
  $now = time();
  $ip = getenv("REMOTE_ADDR");
  $ref = getenv("HTTP_REFERER");
  $query = "insert into guests values ($now, '$ip', '$ref', '$name', '$eintrag')";
 if ($eintrag!="")
 {
   echo "Eingetragen";
   $res = odbc_exec($dbh, $query);
 }
  $query = "select * from guests";
  $res = odbc_exec($dbh, $query);
  while (odbc_fetch_row($res))
  {
   $thename=odbc_result($res,"name");
   $theeintrag=odbc_result($res,"eintrag");
   echo "<tr><td>$thename</td><td>$theeintrag</td></tr>";
  }

  odbc_close_all();
?>

</table>
<form action="guestbook.php">
Ihr Name:<input name="name"><br>
Ihr Eintrag:<textarea name="eintrag">
</textarea>        <br>
<input type=submit><input type=reset>
</form>

</body>
</html>


ToDo's

nach obenToDo's 

Als to-do würde ich Ihnen empfehlen,


Lizenz

nach obenLizenz 

GNU Free Documentation License

Version 1.1, March 2000

                     Copyright (C) 2000  Free Software Foundation, Inc.
                     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     Everyone is permitted to copy and distribute verbatim copies
                     of this license document, but changing it is not allowed.
                     
                  


0. PREAMBLE

nach oben0. PREAMBLE 

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.


1. APPLICABILITY AND DEFINITIONS

nach oben1. APPLICABILITY AND DEFINITIONS 

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.


2. VERBATIM COPYING

nach oben2. VERBATIM COPYING 

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.


3. COPYING IN QUANTITY

nach oben3. COPYING IN QUANTITY 

3. COPYING IN QUANTITY

If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.


4. MODIFICATIONS

nach oben4. MODIFICATIONS 

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.


5. COMBINING DOCUMENTS

nach oben5. COMBINING DOCUMENTS 

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."


6. COLLECTIONS OF DOCUMENTS

nach oben6. COLLECTIONS OF DOCUMENTS 

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.


7. AGGREGATION WITH INDEPENDENT WORKS

nach oben7. AGGREGATION WITH INDEPENDENT WORKS 

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.


8. TRANSLATION

nach oben8. TRANSLATION 

8. TRANSLATION

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.


9. TERMINATION

nach oben9. TERMINATION 

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.


10. FUTURE REVISIONS OF THIS LICENSE

nach oben10. FUTURE REVISIONS OF THIS LICENSE 

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.


Kurzreferenz

nach obenKurzreferenz 

Nach Stichwort

$_REQUEST
$_SESSION
$this
&
Alias
Aneinanderreihen von Strings
Arrays
Arrays, Ausgeben von
Arrays, assoziative
Arrays, mehrdimensionale
Ausgabe von Objekten
Authentifizierung
Dateiuploads
Eigenschaften
Eigenschaften
Funktionen, Rückgabe einer Referenz
Globale Variablen
Klasse, abstrakte
Klassen
Klassenvariablen
Konstruktoren
Link:Buch:SQL-Tutorial
Link:Dev-PHP
Link:Foren
Link:Maguma
Link:MySQL-Website
Link:PHP-Manual
Link:PHP-Website
Link:PHPTriad
Link:SQL-Tutorial
Link:SQL-Zoo
Link:XMLTutorial
Maskierung
Methoden
Methoden
Objekte, Referenzen auf
Objektorientierung
Properties
Referenzen
Referenzen, vergleichen von
Session-ID, Konstante
Sessions
Sessions, automatisches starten von
Transaktionen - Wesen und Aufgabe
Transaktionen unter MySQL
Variablen, Referenzen auf
Variablen, globale
Vererbung
Vererbung, mehrfache
abstract
alter
array_merge
array_pad
array_pop
array_push
begin
bool
call by reference
call by value
catch
char
clone
closedir
commit
comparereferences(&$a, &$b)
create
date
date
dec
decrement
delete
drop
echo
ereg
error_reporting
exceptions
explode
extends
fclose
file
finally
float
float
fopen
for
foreach
fwrite
global
header
htmlentities
if
implode
in_array
include
increment
insert
int
interface
isset
long varchar
mail
mysql_close
mysql_connect
mysql_data_seek
mysql_db_query
mysql_fetch_object
mysql_free_result
mysql_num_rows
mysql_query
mysql_result
mysql_select_db
null
odbc_autocommit
odbc_close_all
odbc_commit
odbc_connect
odbc_exec
odbc_fetch_row
odbc_num_rows
odbc_result
opendir
order by
parent::
php.ini
php.ini
preincrement
print_r
private
protected
public
rawurldecode
readdir
register_globals
require
rollback
select
session.auto_start
session_id()
session_start()
session_unset()
set_error_handler
setcookie
sizeof
sort
sprintf
str_replace
strlen
strpos
strstr
strtolower
strtoupper
substr
throw
toString, __toString
trim
try
unset
update
use strict-ähnliche Notation in PHP
var, Schlüsselwort für Klassenvariablen
varchar
while

Nach Datei

arbeiten_mit_strings.html

Aneinanderreihen von Strings
Maskierung
ereg
explode
htmlentities
implode
rawurldecode
sprintf
str_replace
strlen
strpos
strstr
strtolower
strtoupper
substr
trim

arrays.html

Arrays
Arrays, assoziative
Arrays, mehrdimensionale
array_merge
array_pad
array_pop
array_push
in_array
sizeof
sort

ausgabefunktionen.html

Authentifizierung
echo
header
mail

dateifunktionen.html

closedir
fclose
file
fopen
fwrite
opendir
readdir

debugging.html

Arrays, Ausgeben von
print_r
set_error_handler

eigene_funktionen_und_bibliotheken_benutzen.html

include
require

eine_kleine_mysql-referenz.html

Transaktionen - Wesen und Aufgabe
Transaktionen unter MySQL
begin
commit
mysql_close
mysql_connect
mysql_data_seek
mysql_db_query
mysql_fetch_object
mysql_free_result
mysql_num_rows
mysql_query
mysql_result
mysql_select_db
rollback

eine_kleine_odbc-referenz.html

odbc_autocommit
odbc_close_all
odbc_commit
odbc_connect
odbc_exec
odbc_fetch_row
odbc_num_rows
odbc_result

eine_kleine_sql-referenz.html

alter
bool
char
create
date
date
dec
delete
drop
float
float
insert
int
long varchar
order by
select
update
varchar

formulardaten_uebernehmen.html

$_REQUEST
Dateiuploads
php.ini
register_globals

klassen_und_objekte.html

$this
Eigenschaften
Eigenschaften
Klassen
Klassenvariablen
Konstruktoren
Methoden
Methoden
Properties
Vererbung
extends
parent::
var, Schlüsselwort für Klassenvariablen

objektorientierung.html

Ausgabe von Objekten
Klasse, abstrakte
Objektorientierung
Vererbung, mehrfache
abstract
catch
clone
exceptions
finally
interface
private
protected
public
throw
toString, __toString
try

operatoren.html

decrement
if
increment
preincrement

referenzen.html

&
Alias
Funktionen, Rückgabe einer Referenz
Objekte, Referenzen auf
Referenzen
Referenzen, vergleichen von
Variablen, Referenzen auf
call by reference
call by value
comparereferences(&$a, &$b)

ressourcen.html

Link:Buch:SQL-Tutorial
Link:Dev-PHP
Link:Foren
Link:Maguma
Link:MySQL-Website
Link:PHP-Manual
Link:PHP-Website
Link:PHPTriad
Link:SQL-Tutorial
Link:SQL-Zoo
Link:XMLTutorial

schleifen.html

for
foreach
while

sessions.html

$_SESSION
Session-ID, Konstante
Sessions
Sessions, automatisches starten von
php.ini
session.auto_start
session_id()
session_start()
session_unset()

variablen.html

Globale Variablen
Variablen, globale
error_reporting
global
isset
null
setcookie
unset
use strict-ähnliche Notation in PHP


copyright (c)  2003   useGroup


copyright (c)  2003   useGroup