|
copyright (c) 2003 useGroup
|
Anhang B: Kurzreferenz
|
Anhang C: Druckversion |
![]() |
Jochen Stärk
|
![]()
|
|
copyright (c) 2003 useGroup
|
![]()
|
Aktuelle Version: 3.0.3 vom 25.07.2007
![]()
|
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".
![]()
|
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).
![]()
|
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 ;)
![]()
|
F�r die freundliche Hilfe bei der Erstellung und Fehlersuche:
Josef G. Knust
Thorsten St�rk (f�r seinen Beitrag Transaktionen unter MySQL)
![]()
|
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 ) .
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
PHP hat auch Nachteile:
![]()
|
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...
![]()
|
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.
![]()
|
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.
![]()
|
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
![]()
|
#ServerName new.host.nameund ändern Sie sie in
ServerName new.host.nameAuf deutsch: machen Sie aus der auskommentierten Zeile wieder eine normale Zeile der Konfigurationsdatei.
AddType application/x-httpd-php .php AddType application/x-httpd-php .php3Diese Zeile sagt Apache, dass .php-Dateien vom Typ application/x-httpd-php4 sind.
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.exemit PHP. (im Skriptverzeichnis /php/ also in c:\apache\php)
<html> <? echo "hello world"; ?> </html>Als Dateiname wählen wir test.php und speichern es im Apache\htdocs Pfad.
![]()
|
<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.
![]()
|
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 "Schwester-Tutorial" dieses Dokuments mit dem Thema XML/XSLT finden Sie hier: http://www.usegroup.de/software/xmltutorial/.
![]()
|
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.
![]()
|
ist http://www.php.net. Dort findet man neben dem PHP-Manual auch aktuelle Informationen.
![]()
|
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 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.
![]()
|
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.
![]()
|
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
![]()
|
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. Der Schlüssel zu relationalen Datenbanken
Gregor Kuhlmann, Friedrich Müllmerstadt
19,90 DM, 10,17
317 Seiten
ISBN 3499600633
![]()
|
www.mysql.com
hat MySQL natürlich dokumentiert. Insofern eine wichtige Ressource.
![]()
|
PHPTriad bündelt MySQL, Apache und PHP für Windows, und erleichtert damit deren Installation. In die gleiche Lücke springt phpdev.
![]()
|
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. )
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.
![]()
|
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.
![]()
|
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.
![]()
|
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 (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()
![]()
|
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.
![]()
|
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,
![]()
|
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.
![]()
|
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($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.
![]()
|
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, 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
![]()
|
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]; // 6wobei $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.
![]()
|
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 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
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
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 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
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
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
![]()
|
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.
![]()
|
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ß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.
![]()
|
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).
![]()
|
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 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.
![]()
|
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.
![]()
|
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;
}
![]()
|
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));
![]()
|
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.
![]()
|
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;
![]()
|
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!
![]()
|
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");
![]()
|
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);
}
}
![]()
|
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");
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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 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.
![]()
|
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() {
// ...
}
}
![]()
|
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 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->fileAccessExceptionkann 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:
} 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;
}
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 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.
![]()
|
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.
![]()
|
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).
![]()
|
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.
![]()
|
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).
![]()
|
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 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>
![]()
|
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.
![]()
|
_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'].
![]()
|
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();
![]()
|
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 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>
![]()
|
| $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 (<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";
}
![]()
|
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. |
![]()
|
Es gibt in PHP (wie auch in C) eine Kurzform der Anweisungen
A=A <Operator> Bund zwar sieht diese so aus:
A<Operator>=BAusdrü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.
$<Variablenname>++(Erhöhen um 1) bzw.
$<Variablenname>--(um 1 erniedrigen). $i++ ist gleichbedeutend mit $i+=1 und das ist dasselbe wie $i=$i+1;
++$i;
![]()
|
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 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.
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 ";
}
![]()
|
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 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.
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.
![]()
|
Eine Kurzform von Echo binden Sie mit
<?=$VARIABLENNAMEN?>in Ihre PHP-Dateien ein. Das funktioniert allerdings nichtb bei jedem Presence Provider.
![]()
|
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.
![]()
|
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:
<?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.
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.
![]()
|
Mailen geht in PHP einfach mit
mail("Empfänger","Betreff","Nachricht");
mail("vetterheinz@provinz.de", "$betreff", $message, "From: $email\nReply-To: $email\nX-Mailer: PHP/" . phpversion(). "\nCC: sie@provider.de");
![]()
|
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" \
![]()
|
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.
![]()
|
int strlen(<String>) |
Strlen gibt Ihnen die Möglichkeit, festzustellen, wie lang der String ist, den Sie bearbeiten.
![]()
|
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);
![]()
|
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;
![]()
|
string htmlentities(<Quelle>); |
maskiert alle Umlaute und Sonderzeichen HTML-gerecht. Aus ä wird ä usw... und aus < wird < -- was z.B. wichtig ist dass keine Eingabe Javascript-Aktionen auslösen kann.
echo htmlentities ("äöüäüöß<\\"); // gibt aus: äöüäüö<\
![]()
|
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);
![]()
|
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
![]()
|
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.
$spos=strpos($haystack, $needle);
if (is_int($spos)&&($spos==0))
{
// $needle steht am Anfang von $haystack
}
![]()
|
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 ");
![]()
|
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.
![]()
|
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 |
<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.
![]()
|
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);
![]()
|
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
![]()
|
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.
![]()
|
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.
![]()
|
Geht wie Dateien lesen :)
$url=file("http://www.altavista.com/");
schreibt je eine Zeile der HTML-Quelltextes in das Array url.
Leichter gehts nicht.
![]()
|
Geht mit
int fopen(<Dateiname>,<Kommando>), fwrite (<Dateiindex>,<String>) und fclose(<Dateiindex>).
| 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) |
$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);
![]()
|
Erfolgt mit
int opendir(<Pfad>), readdir(<Pfadindex>) und closedir(<Pfadindex>)
$dir=opendir("c:/");
while ($file = readdir($dir))
{
echo "$file\n";
}
closedir($dir);
![]()
|
Diesen Abschnitt finden Sie in Formulardaten übernehmen.
![]()
|
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;
}
?>
![]()
|
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;
}
?>
![]()
|
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.
![]()
|
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.
![]()
|
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($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(" ", " ",$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($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($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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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:
![]()
|
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 <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 hauptbuchselektiert 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 hauptbuchgewährt dann nur Zugriff auf die Spalten transaktion und datum (nur wenige Spalten auszuwählen vergrößert oft übersicht und verschnellert).
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)
<selectanweisung> order by <Schlüsselwert>[ascending/descending]
select * from hauptbuch where (transaktion>40000) order by transaktion descendingselektiert z.b. bei einer Tabelle mit den Werten
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.
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.
![]()
|
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 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.
![]()
|
Zeilen löscht man in SQL mit
DELETE FROM <Tabellenname> WHERE < Bedingung >Beispiel:
DELETE FROM ANTIQUES WHERE ITEM = 'Ottoman';
![]()
|
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.
![]()
|
Mit
ALTER TABLE <Tabellenname> ADD/DROP COLUMN <Spaltendefinition>/<Spaltenname>kann man Tabellen im Nachhinein ändern -- praktisch.
ALTER TABLE ANTIQUES ADD COLUMN (PRICE INT);
ALTER TABLE ANTIQUES DROP COLUMN PRICE;
![]()
|
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';
![]()
|
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.
![]()
|
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).
![]()
|
Schließt die Verbindung zu MySQL und ist am Ende jedes Programmes, das auch MYSQL_CONNECT benutzt hat, aufzurufen.
mysql_close();
![]()
|
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')");
![]()
|
int MYSQL_DB_QUERY(String Datenbank, String query_string); |
führt eine SQL-Anweisung auf Datenbank aus. Im Unterschied zu
MYSQL_QUERYerwartet 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);
![]()
|
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.
![]()
|
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.
![]()
|
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.
![]()
|
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 ;))
![]()
|
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.
siehe MYSQL_NUM_ROWS-Beispiel.
![]()
|
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.,
insert into tabellemitautoincrement values ('Daten');
select MAX(ID) from tabellemitautoincrement;
liefert ohne Transaktion unter Umständen nicht die ID des
gerade eingefügten Datensatzes zurück. Nämlich genau dann nicht, wenn
gleichzeitig mehrere Benutzer diesen Befehl ausführen und die Datenbank
dann (von uns unbemerkt) Folgendes ausführt
insert into tabellemitautoincrement values ('Daten'); // unser User
insert into tabellemitautoincrement values ('fremde Daten'); // wird jetzt von uns
// unbemerkt von einem Dritten (für uns nicht sichtbar im Hintergrund) ausgeführt
select MAX(ID) from tabellemitautoincrement; // liefert jetzt die ID des Dritten,
// nicht unsere
In dem Fall bekommen wir die ID des fremden Zugriffs.
Sowas kommt bei großen Datenbanken häufig genug vor,
und kann unglaubliche Probleme verursachen und
// 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.![]()
|
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..
![]()
|
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.
![]()
|
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 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).
![]()
|
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.
![]()
|
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);
![]()
|
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();
![]()
|
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')");
![]()
|
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 ";
}
![]()
|
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 MYTABLEoder
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)");
}
![]()
|
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.
}
![]()
|
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);
// ...
![]()
|
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);}
// ...
![]()
|
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).
![]()
|
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.
![]()
|
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.
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.
![]()
|
Als to-do braucht der Programmierer noch unbedingt
$dbh = odbc_connect('testdb', 'aname', 'apwd', SQL_CUR_USE_ODBC);
$query = "select * from counter";
$res = odbc_exec($dbh, $query);
echo "number of visitors:".odbc_num_rows($res)." ";
odbc_close_all();
interessant wären Balkengrafiken mit den einzelnen Tagen
und den Visitors. Siehe dazu das XIIIte Kapitel im
Manual.
![]()
|
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".
![]()
|
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).
![]()
|
ist in Ausgabefunktionen genau beschrieben.
![]()
|
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).
![]()
|
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>
![]()
|
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>
![]()
|
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>
![]()
|
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.
![]()
|
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>";
}
![]()
|
<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>
![]()
|
Als to-do würde ich Ihnen empfehlen,
$eintrag=string htmlentities($eintrag); $name=string htmlentities($name);würde das übernehmen (wenn es vor der INSERT-Zeile geschieht). Das maskiert nicht nur Sonderzeichen wie äöü, sondern vor allem Tags: Einträge wie <script language="JavaScript">window.document.location.href="http://home.t-online.de/home/jens_guenther/";</script> würden ALLE BESUCHER des Gästebuchs zwangsweise auf eine fremde Seite schicken (die dann auch noch so ähnlich aussehen könnte), <img src="http://www.microsoft.com/ms.gif"> würde allen Besuchern ein u.U. mehrere Megabyte großes Bild aufdrängen.
![]()
|
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
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
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
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
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
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:![]()
|
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
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
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
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
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
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.![]()
|

|
copyright (c) 2003 useGroup
|
Anhang B: Kurzreferenz
|
Anhang C: Druckversion |
|
copyright (c) 2003 useGroup
|