CREATE TABLE vaterzeiger ( ID int not null primary key, Name varchar(100), VID int ); INSERT INTO vaterzeiger VALUES (1,'Root',0); INSERT INTO vaterzeiger VALUES (2,'A',1); INSERT INTO vaterzeiger VALUES (3,'B',1); INSERT INTO vaterzeiger VALUES (4,'C',1); INSERT INTO vaterzeiger VALUES (5,'A1',2); INSERT INTO vaterzeiger VALUES (6,'B1',3); INSERT INTO vaterzeiger VALUES (7,'B2',3); INSERT INTO vaterzeiger VALUES (8,'C1',4); INSERT INTO vaterzeiger VALUES (9,'A1I',5); INSERT INTO vaterzeiger VALUES (10,'C1I',8); INSERT INTO vaterzeiger VALUES (11,'C1II',8);
Nachdem nun die Tabelle existiert, die einzelnen Abfragen:
mysql> SELECT * FROM vaterzeiger -> WHERE VID = 0; +----+------+------+ | ID | Name | VID | +----+------+------+ | 1 | Root | 0 | +----+------+------+ 1 row in set (0.00 sec)
mysql> SELECT count(*) FROM vaterzeiger -> WHERE VID = 1; +----------+ | count(*) | +----------+ | 3 | +----------+ 1 row in set (0.00 sec)Wenn man die ID der Wurzel nicht kennt, wird die Abfrage etwas länger:
mysql> SELECT count(*) -> FROM vaterzeiger V, vaterzeiger S -> WHERE V.ID = S.VID -> AND V.VID = 0; +----------+ | count(*) | +----------+ | 3 | +----------+ 1 row in set (0.01 sec)Hier wird ein Self-Join gemacht, da MySQL keine Unterabfragen unterstützt.
mysql> SELECT V.Name, ' ist Vater von ', S.Name -> FROM vaterzeiger V, vaterzeiger S -> WHERE V.ID = S.VID -> AND V.VID = 0; +------+-----------------+------+ | Name | ist Vater von | Name | +------+-----------------+------+ | Root | ist Vater von | A | | Root | ist Vater von | B | | Root | ist Vater von | C | +------+-----------------+------+ 3 rows in set (0.00 sec)Viel schöner finde ich aber folgende Ausgabe:
mysql> SELECT S.VID != 0 AS Tiefe, S.Name -> FROM vaterzeiger V, vaterzeiger S -> WHERE (V.ID = S.VID OR S.VID = 0) -> AND V.VID = 0; +-------+------+ | Tiefe | Name | +-------+------+ | 0 | Root | | 1 | A | | 1 | B | | 1 | C | +-------+------+ 4 rows in set (0.00 sec)
mysql> SELECT l,r -> FROM NestedSet -> WHERE Name = "C"; +------+------+ | l | r | +------+------+ | 14 | 21 | +------+------+ 1 row in set (0.00 sec) mysql> SELECT s.Name, count(*) AS Level -> FROM NestedSet v, NestedSet s -> WHERE s.l BETWEEN v.l AND v.r -> AND s.l BETWEEN 14 AND 21 -> GROUP BY s.l; +------+-------+ | Name | Level | +------+-------+ | C | 2 | | C1 | 3 | | C1I | 4 | | C1II | 4 | +------+-------+ 4 rows in set (0.00 sec)Mit Hilfe der ersten Abfrage holen wir uns die Werte für l und r, die wir dann bei der eigentlichen Abfrage verwenden.
Wenn du nur eine Anweisung haben willst, sieht eine mögliche Abfrage so aus:
mysql> SELECT s.Name, count(*) AS Level -> FROM NestedSet v, NestedSet s, NestedSet s_id -> WHERE s.l BETWEEN v.l AND v.r -> AND s_id.Name = "C" -> AND s.l BETWEEN s_id.l AND s_id.r -> GROUP BY s.l; +------+-------+ | Name | Level | +------+-------+ | C | 2 | | C1 | 3 | | C1I | 4 | | C1II | 4 | +------+-------+ 4 rows in set (0.00 sec)
mysql> SELECT s.Name, -> (s.r-s.l-1)/2 AS Nachfolger, -> count(*)+(s.l>1) AS Tiefe, -> ((min(v.r)-s.r-(s.l>1))/2) > 0 AS Bruder -> FROM NestedSet v, NestedSet s -> WHERE s.l BETWEEN v.l AND v.r -> AND (v.Name != s.Name OR s.l = 1) -> GROUP BY s.Name -> ORDER BY s.l; +------+------------+-------+--------+ | Name | Nachfolger | Tiefe | Bruder | +------+------------+-------+--------+ | Root | 10.00 | 1 | 0 | | A | 2.00 | 2 | 1 | | A1 | 1.00 | 3 | 0 | | A1I | 0.00 | 4 | 0 | | B | 2.00 | 2 | 1 | | B1 | 0.00 | 3 | 1 | | B2 | 0.00 | 3 | 0 | | C | 3.00 | 2 | 0 | | C1 | 2.00 | 3 | 0 | | C1I | 0.00 | 4 | 1 | | C1II | 0.00 | 4 | 0 | +------+------------+-------+--------+ 11 rows in set (0.00 sec)Eigentlich gar nicht so schwierig, oder? ;-)
function print_result_table($result){ // Tabellenanfang echo "<table>\n"; // Tabellenzeilen-Anfang echo " <tr>\n"; // Zeile aus DB-Anfrage holen $row = mysql_fetch_row($result); // erstes Feld der Zeile ausgeben echo " <td>$row[0]</td>\n"; // Tabellenzeilen-Ende echo " </tr>\n"; // Tabellenende echo "</table>\n"; }
function print_result_table($result){ // Tabellenanfang echo "<table>\n"; // Alle Ergebniszeilen durchgehen while ($row = mysql_fetch_row($result)){ // Tabellenzeilen-Anfang echo " <tr>\n"; // erstes Feld der Zeile ausgeben echo " <td>$row[0]</td>\n"; // Tabellenzeilen-Ende echo " </tr>\n"; } // Tabellenende echo "</table>\n"; }
function print_result_table($result){ // Tabellenanfang echo "<table>\n"; // Alle Ergebniszeilen durchgehen while ($row = mysql_fetch_row($result)){ // Tabellenzeilen-Anfang echo " <tr>\n"; // Alle Spalten durchgehen for ($i = 0; $i < mysql_num_fields($result); $i++){ echo " <td>$row[$i]</td>\n"; } // Tabellenzeilen-Ende echo " </tr>\n"; } // Tabellenende echo "</table>\n"; }
function print_result_table($result){ // Tabellenanfang echo "<table>\n"; // 1. Tabellenzeile Anfang echo " <tr>\n"; for ($i = 0; $i < mysql_num_fields($result); $i++){ echo " <th>".mysql_field_name($result,$i)."</th>\n"; } // 1. Tabellenzeile Ende echo " </tr>\n"; // Alle Ergebniszeilen durchgehen while ($row = mysql_fetch_row($result)){ // Tabellenzeilen-Anfang echo " <tr>\n"; // Alle Spalten durchgehen for ($i = 0; $i < mysql_num_fields($result); $i++){ echo " <td>$row[$i]</td>\n"; } // Tabellenzeilen-Ende echo " </tr>\n"; } // Tabellenende echo "</table>\n"; }
Kleiner Tipp vorweg: Um zu testen, ob die eine oder andere Abfrage sauber funktioniert, kann man einfach mal einen Fehler provozieren, also z.B. einen falschen Namen bei $db_user eintragen oder die Abfrage zu einem ,,SELECT * FROM Mitarbeiter WHERE MNr = 0`` erweitern, damit keine Mitarbeiter gefunden werden.
<?php $db_host = "localhost"; $db_user = "cr"; $db_pass = "123"; $datab = "cr"; function print_result_table($result){ // s.o. } // Hauptprogramm /* Verbindung zur Datenbank aufbauen */ $db = @mysql_connect($db_host,$db_user,$db_pass) OR die(mysql_error()); @mysql_select_db($datab,$db) OR die(mysql_error()); /* HTML-Startcode ausgeben */ echo "<html>\n<body>\n"; /* SQL-Abfrage */ $result = @mysql_query("SELECT * FROM Mitarbeiter"); /* Wenn die Fehlernummer != 0 ist, dann gab es einen Fehler => Fehlermeldung ausgeben */ if (mysql_errno() != 0){ echo mysql_error(); } // es gab keine Fehler => Ergebnis ausgeben else { // Wie viele Datensätze wurden gefunden? // Bei 0 Meldung ausgeben if (mysql_num_rows($result) == 0){ echo "Keine Datensätze gefunden!"; } // sonst die Funktion aufrufen else{ print_result_table($result); } } /* HTML-Endcode ausgeben */ echo "</body>\n</html>\n"; ?>
Die vier Fehler sind im Einzelnen:
Um das gewünschte zu erreichen, sind zwei Tricks nötig: Zum einen muß das Erzeugen der MNr MySQL überlassen werden und zum anderen muß diese ID abgefragt werden, um die VNr für die zweite Einfügeoperation zu haben. Ersteres erreicht man durch Weglassen der MNr bei der Liste der Felder (oder bei Angabe der MNr Setzen des Wertes auf NULL), letzteres durch mysql_insert_id().
Folgender Code implementiert dies:
$db_host = "localhost"; $db_user = "cr"; $db_pass = "123"; $datab = "cr"; /* Verbindung zur Datenbank aufbauen */ $db = @mysql_connect($db_host,$db_user,$db_pass) OR die(mysql_error()); @mysql_select_db($datab,$db) OR die(mysql_error()); // Jens einfügen mysql_query("INSERT INTO Mitarbeiter (VNr,AbtNr,Name,GebDat) VALUES (1, 2, 'Jens', '1981-05-26')"); echo mysql_error(); $vnr = mysql_insert_id(); // Kile einfügen und Jens unterstellen mysql_query("INSERT INTO Mitarbeiter (VNr,AbtNr,Name) VALUES ($vnr, 1, 'Kile')"); echo mysql_error();
/^[\w.-]+ [-\w]+ [-\w]+ \[\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} [+-]\d{4}\] "\w+ [\/.\w]+ HTTP\/\d\.\d" [1-5]\d{2} [-\d]*$/Was macht der Ausdruck? Als erstes soll er die gesamte Zeile überprüfen, deshalb das Dach ^ am Anfang und das Dollarzeichen $ am Ende. Am Anfang der Zeile soll der Name bzw. die IP des Anfragenden kommen. Das habe ich an dieser Stelle mit einem [\w.-]+ erschlagen. Da der Punkt und der Bindestrich vorkommen darf, aber nicht bei den ``Wort``-Zeichen enthalten ist, müssen sie hier extra aufgeführt werden. Dieser Teil ist nicht optimal, weil ,,1.1``, ,, keine.gueltiger.name`` oder ,,rechnername`` keine gültigen Namen oder IP-Adressen sind, trotzdem aber auf den Ausdruck passen.
Die nächsten beiden Felder sind einfacher, weil hier entweder normale Wörter oder ein Minus für ,,nicht vorhanden`` stehen. Die eckigen Klammern müssen mit einem Backslash escaped werden, weil ich hier keine Menge von Zeichen definieren will, sondern die Zeichen ,,eckige Klammer auf`` und ,,eckige Klammer zu`` haben will. Ähnliches gilt für den Slash: Wenn ich ihn nicht mit einem Backslash escaped hätte, wäre der Ausdruck an der Stelle zu Ende.
Der Ausdruck in den eckigen Klammern müßte eigentlich klar sein. Da die Anzahl der einzelnen Zeichen feststehen, habe ich sie hier explizit angegeben.
In den Anführungszeichen steht das nächste nicht-Optimum. Das erste Wort in den Anführungszeichen ist entweder ,,GET`` oder ,,POST``; trotzdem muß ich beliebige Wörter erlauben, weil ich noch nicht geschrieben habe, wie man Oder realisiert. Der Punkt bei HTTP muß auch mit einem Backslash escaped werden, damit nur der Punkt paßt und nicht ein beliebiges Zeichen.
Da der Statuscode immer eine dreistellige Zahl zwischen 100 und 505 ist, muß auch hier explizit die Anzahl der Ziffern angegeben. Ist auch nicht ganz optimal, weil nicht weit genug geprüft wird. Die Größenangabe (das letzte Feld) kann auch nichts (d.h. ein Minus -) enthalten, wenn es keinen Sinn machen würde. Daher dieser Ausdruck.
Das Programm gibt folgendes ohne Fehlermeldung aus: ,,Die Variable 1 hat den Wert 1``. Der erste ist ein beliebter Fehler: statt des Vergleichsoperators == wurde der Zuweisungsoperator = benutzt. Damit wurde nicht die Variable mit dem Wert verglichen, sondern der Variablen wurde der Wert ,,1`` zugewiesen. Anschließend wurde dieser Wert genommen, so daß alle Werte, die nicht ,,0`` waren, als ,, true`` interpretiert wurden. Somit wurde in der IF-Anweisung der 1. Anweisungsblock ausgeführt.
Der zweite Fehler steckt in der Ausgabe. Ausgegeben werden sollte ,,Die Variable $i ...`` und nicht ,,Die Variable 1 ...``. Es gibt zwei Lösungen für das Problem. Entweder benutzt man die einfachen Anführungszeichen und verhindert damit das Ersetzen von Variablennamen durch deren Werte oder man escaped das $, dazu muß man statt $i ein \$i schreiben.
Solche Fehler könnte man vermeiden, würde man statt if ($i == 1) ein if (1 == $i) schreiben. Dann erhielte man nämlich eine Fehlermeldung, wenn man nur 1 = $i schriebe, weil man dem Wert 1 nicht den Wert der Variablen zuweisen kann.
1 2 3 4 5 Das waren die Zahlen 1-5
Das Programm gibt aber unter PHP3 folgendes aus:
1 Das waren die Zahlen 1-5 Parse error: parse error in /home/httpd/html/test.php3 on line 6Und bei PHP4 folgendes:
Parse error: parse error in /home/httpd/html/test.php4 on line 6
Die unterschiedlichen Ausgaben kommen vom unterschiedlichen Design von PHP3 und PHP4. Die Fehlermeldung ist aber in beiden Fällen dieselbe: In Zeile 6 gibt es einen parse error. Zeile 6 ist allerdings leer. Der Fehler ist, daß in Zeile 2 ein Anweisungsblock mit einer { angefangen, aber nicht mehr beendet wurde. PHP kann das erst am Ende der Datei merken, daher wird in der Fehlermeldung Zeile 6 angegeben.
Vermeiden kann man solche Fehler, indem man richtig einrückt. Bei folgender Schreibweise fällt leichter auf, daß die (geschweifte Klammer zu) fehlt. Auf jeden Fall läßt sich so der Fehler leichter finden. Vorteilhaft in solchen Situationen ist es auch, einen Editor zu benutzen, der zueinander gehörige Klammern farblich hervorhebt.
<?php for ($i=1; $i<=5; $i++){ echo "$i<br>\n"; echo "Das waren die Zahlen 1-5"; ?>
Manche schreiben es noch deutlicher.
<?php for ($i=1; $i<=5; $i++) { echo "$i<br>\n"; echo "Das waren die Zahlen 1-5"; ?>
Ausgabe von PHP3 (die letzten beiden Zeilen sind eigentlich eine):
1 ; } echo Parse error: parse error, expecting `','' or `';'' in /home/httpd/html/test.php3 on line 5Ausgabe von PHP4:
Parse error: parse error, expecting `','' or `';'' in /home/httpd/html/test.php4 on line 5
Wie im obigen Fall ist auch hier die Fehlermeldung bei beiden Versionen dieselbe; PHP erwartet in Zeile 5 ein ,,;``. Bei genauerem Betrachten sieht diese aber richtig aus. Der Fehler steckt in Zeile 3. Dort werden die Anführungszeichen nicht wieder geschlossen. Daher auch die Ausgabe bei PHP3. Nach dem Anführungszeichen in Zeile 3 wird alles bis zum nächsten Anführungszeichen ausgegeben, in unserem Fall in Zeile 5. Damit wird aber das, was eigentlich in den Anführungszeichen stehen sollte, als Reihe von PHP-Befehlen interpretiert. Und mit einem ,,Das`` nach einer Ausgabe kann PHP eben nicht viel anfangen und will, daß wir erstmal mit einem ,,;`` die Anweisung beenden.
An diesem Beispiel sieht man, daß der Fehler nicht immer in der gemeldeten Zeile liegen muß. In diesem Fall hilft nur konzentriertes Programmieren und ein Editor mit Syntax-Highlighting.
Der folgende Code erzeugt transparente Bilder im PNG-Format. Wohlgemerkt, es werden Bilder erstellt, kein HTML!
Als erstes die Image-Klasse:
/** * Image Klasse * Legt ein Bild (default: 100x100) an * und gibt es bei Bedarf aus */ class Image { /** Image stream */ var $im; /** Breite */ var $width; /** Höhe */ var $height; /** Malfarbe */ var $dcolor; /** * Konstruktor * Bild anlegen und Standardfarben setzen * * @param int Breite * @param int Höhe */ function Image($width=100, $height=100) { if ($width<1 || $height<1) $width = $height = 100; $this->width = $width; $this->height = $height; $this->im = @ImageCreate($width, $height) or die ("Konnte keinen Image stream anlegen"); // transparenter Hintergrund, schwarzer "Stift" $whitecol = ImageColorAllocate($this->im, 0, 0, 0); ImageColorTransparent($this->im, $whitecol); $this->dcolor = ImageColorAllocate($this->im, 0, 0, 0); } /** * Bild anzeigen (PNG) */ function show() { Header("Content-type: image/png"); ImagePNG($this->im); } /** * Breite des Bildes zurückliefern * * @return int Breite */ function getWidth() { return $this->width; } /** * Höhe des Bildes zurückliefern * * @return int Höhe */ function getHeight() { return $this->height; } }
Nun die Punkt-Klasse:
/** * Point Klasse * Kann einen Punkt malen und ausgeben */ class Point extends Image { /** X-Koordinate */ var $x = 0; /** Y-Koordinate */ var $y = 0; /** * Koordinaten und sonstiges setzen * * @param int X-Koordinate * @param int Y-Koordinate */ function set($x=0, $y=0) { $this->x = $x; $this->y = $y; ImageSetPixel($this->im,$this->x,$this->y,$this->dcolor); } }
Und schließlich die Ausgabe:
$mypoint = new Point(); $mypoint->set(50, 50); $mypoint->show();
Als nächstes der Kreis:
/** * Circle Klasse * Kann einen Kreis malen und ausgeben, * erfordert aber die GD-Lib 2.0 */ class Circle extends Point { /** Radius */ var $r = 0; /** * Koordinaten und Radius setzen * * @param int X-Koordinate * @param int Y-Koordinate * @param int Radius */ function set($x=0, $y=0, $r=10) { parent::set($x, $y); $this->r = $r; ImageEllipse($this->im, $this->x, $this->y, $this->r, $this->r, $this->dcolor); } }
Kreis-Ausgabe (funktioniert nur mit GD-Lib 2.0!):
$mycircle = new Circle(); $mycircle->set(50, 50, 30); $mycircle->show();
Da ich die GD-Lib 2.0 nicht habe, fehlt hier das entsprechende Bild ...
Ein Rechteck ist auch nicht schwer zu erstellen:
/** * Rectangle Klasse * Kann ein Rechteck malen und ausgeben */ class Rectangle extends Point { /** zweite X-Koordinate */ var $x2; /** zweite Y-Koordinate */ var $y2; /** * Koordinaten und Ausmaße setzen; * ggf. zentrieren * * @param int X-Koordinate * @param int Y-Koordinate * @param int Breite * @param int Höhe * @param boolean Um X/Y-Koordinaten zentrieren */ function set($x=0,$y=0,$width=10,$height=10,$center=false){ if ($center) { parent::set($x - $width/2, $y - $height/2); $this->x2 = $x + $width/2; $this->y2 = $y + $height/2; } else { parent::set($x, $y); $this->x2 = $x + $width; $this->y2 = $y + $height; } // (x,y) ist links oben, (x2,y2) rechts unten ImageRectangle($this->im, $this->x, $this->y, $this->x2, $this->y2, $this->dcolor); } }
Beispiel-Rechteck:
$myrect = new Rectangle(); $myrect->set(50, 50, 30, 40, true); $myrect->show();
Zum Schluß noch das Quadrat:
/** * Square Klasse * Kann ein Quadrat malen und ausgeben */ class Square extends Rectangle { /** * Koordinaten und Ausmaße setzen; * ggf. zentrieren * * @param int X-Koordinate * @param int Y-Koordinate * @param int Seitenlänge * @param boolean Um X/Y-Koordinaten zentrieren */ function set($x=0, $y=0, $length=10, $center=false) { parent::set($x, $y, $length, $length, $center); } }
Und die obligatorische Ausgabe:
$mysquare = new Square(); $mysquare->set(50, 50, 30); $mysquare->show();
class MyArray { var $array; function MyArray() {} function add(&$val) { $this->array[] =& $val; } } class Test { var $var; function Test($var, &$array) { $this->var = $var; $array->add($this); } function setVar($var) { $this->var = $var; } function getVar() { return $this->var; } }
$array = new MyArray(); $test =& new Test(42, $array); $test2 =& $test; $test->setVar(0); echo $test->getVar()." ".$test2->getVar()."\n"; var_dump($array);