Man kann keine schummelsichere Abstimmung im Web realisieren, die nicht unfair berechtigte Abstimmende ausschließt, außer man treibt sehr großen Aufwand.
Typische Schutzmechanismen, die jedoch nur Pseudosicherheit geben, sind: Die Seite nimmt Parameter per POST, prüft den Referer und versucht einen Cookie zu setzen. Wer den Cookie hat, kann nicht mehr abstimmen. Zum Mogeln muss man der Seite also die richtigen Parameter aus dem Formular mit einem POST füttern, darf den Referer nicht vergessen und darf den Cookie nicht annehmen. Das war alles.
Eine etwas kompliziertere Absicherung wäre ein Formular, das als hidden -Parameter eine Challenge hat, die auch auf dem Server als Session-Variable verbleibt. Ein Abstimmungsergebnis wird dann für die aktuelle Session nur angenommen, wenn die richtige Challenge mit in den Parametern ist. Das macht das Schummeln per Script ein wenig schwieriger, aber nicht unmöglich (mit LWP in Perl oder libwww in C problemlos möglich, mit PHP ein Code mit den preg_*-Funktionen.
Mit SSL-Browser-Zertifikaten auf der Clientseite wäre es noch schwieriger zu fälschen, aber da gelangt man langsam in Regionen, wo es für den Wahlveranstalter wirklich teuer wird.
Auf der Basis der IP-Nummern kann man nicht arbeiten, da viele Benutzer dieselbe IP-Nummer zu haben scheinen, wenn sie über Proxy-Server hereinkommen oder Benutzer auf einem Mehrbenutzer-Rechner sind (unfairer Ausschluss). Andererseits ist es für einen geübten Hacker oder einen Provider sehr leicht, von einer ganzen Reihe unterschiedlicher IP-Nummern mehrfach abzustimmen.
Das Script muss einen Socket mit der Funktion fsockopen() zum Zielserver öffnen und auf diesem Socket dann einen HTTP POST-Request simulieren.
Anbei ein vollständiges Beispiel, das mit CGI-PHP auf der Unix-Kommandozeile verwendet werden kann. Das Script fälscht Einträge in einer Abstimmung auf dem Host www.linux.com , wo es für PHP als beste Scriptsprache stimmt.
#! ./php -q
<?php
function PostToHost($host, $path, $referer, $data_to_send) {
$fp = fsockopen($host, 80);
printf("Open!\n");
fputs($fp, "POST $path HTTP/1.1\r\n");
fputs($fp, "Host: $host\r\n");
fputs($fp, "Referer: $referer\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: ". strlen($data_to_send) ."\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $data_to_send);
printf("Sent!\n");
while(!feof($fp)) {
$res .= fgets($fp, 128);
}
printf("Done!\n");
fclose($fp);
return $res;
}
$data = "pid=14&poll_vote_number=2";
printf("Go!\n");
$x = PostToHost(
"www.linux.com",
"/polls/index.phtml",
"http://www.linux.com/polls/index.phtml?pid=14",
$data
);
Das Script muss einen Socket mit der Funktion fsockopen() zum Zielserver öffnen und auf diesem Socket dann einen HTTP POST-Request mit Datei-Upload simulieren.
Das Beispiel baut eine HTTP-Anfrage konform der multipart/form-data Beschreibung auf und sendet diese dann ab. Dabei wird eine ASCII-Datei eingelesen und übergeben.
Bei multipart/form-data werden die Daten so übergeben:
... Content-type: multipart/form-data; boundary=---------------------------255141413922088 Content-Length: 6881 -----------------------------255141413922088 Content-Disposition: form-data; name="disk" off -----------------------------255141413922088 ... -----------------------------255141413922088--
Dieses Stück Beispielcode sendet einen solchen Request ab:
#!/usr/local/bin/php -q
<?php
// Andreas Bohne-Lang / a.bohne@dkfz.de / 22.10.2001 / GPL
// Ergaenzt: 19.9.2003
function PostToHost($host, $port, $path, $referer, $data_to_send)
{
$dc = 0;
$bo="-----------------------------305242850528394";
$fp = fsockopen($host, $port, $errno, $errstr);
if (!$fp) {
echo "errno: $errno \n";
echo "errstr: $errstr\n";
return $result;
}
fputs($fp, "POST $path HTTP/1.0\n");
fputs($fp, "Host: $host\n");
fputs($fp, "Referer: $referer\n");
fputs($fp, "User-Agent: Mozilla/4.05C-SGI [en] (X11; I; IRIX 6.5 IP22)\n");
fputs($fp, "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\n");
fputs($fp, "Accept-Charset: iso-8859-1,*,utf-8\n");
fputs($fp, "Content-type: multipart/form-data; boundary=$bo\n");
foreach($data_to_send as $key=>$val) {
$ds =sprintf("--%s\nContent-Disposition: form-data; name=\"%s\"\n\n%s\n", $bo, $key, $val);
$dc += strlen($ds);
}
$dc += strlen($bo)+3;
fputs($fp, "Content-length: $dc \n");
fputs($fp, "\n");
foreach($data_to_send as $key=>$val) {
$ds =sprintf("--%s\nContent-Disposition: form-data; name=\"%s\"\n\n%s\n", $bo, $key, $val);
fputs($fp, $ds );
}
$ds = "--".$bo."--\n";
fputs($fp, $ds);
while(!feof($fp)) {
$res .= fread($fp, 1);
}
fclose($fp);
return $res;
}
// Fuer eine ASCII-Datei kann man es so machen
// bei bin-Daten ueber fread gehen
$fa = @file("http://www.dkfz.de/spec/fix/file.pdb");
// Konkretes Beispiel - Eine Chemie-Datei
$xf="Content-Type: chemical/x-pdb\n\n". implode("", $fa);
$data["disk"] = "on";
$data["file\"; filename=\"irgendwas.pdb"] = $xf;
$data["smiles"] = "";
$data["hadd"] = "add";
$data["aroresolver"] = "on";
$data["format"] = "gif";
$data["interlace"] = "1";
$data["width"] = "600";
$data["height"] = "400";
$data["atomcolor"] = "Black";
$data["asymbol"] = "xsymbol";
$data["hcolor"] = "";
$data["csymbol"] = "special";
$data["hsymbol"] = "special";
$data["bondcolor"] = "Black";
$data["bgcolor"] = "White";
$data["border"] = "12";
$data["bonds"] = "8";
$data["wedges"] = "1";
$data["dashes"] = "1";
$data["crop"] = "2";
$data["align"] = "none";
$data["coord"] = "0";
$data["imagemap"] = "none";
$data["headercolor"] = "Black";
$data["header"] = "";
$data["footercolor"] = "Black";
$data["footer"] = "";
$data["commenttype"] = "none";
$data["comment"] = "";
$data["structure"] = "none";
$x = PostToHost (
"www2.chemie.uni-erlangen.de",
80,
"/cgi-bin/services/gifcreator.tcl",
"http://www2.chemie.uni-erlangen.de/services/gifcreator/index.html",
$data
);
// Diesen Teil kann man bestimmt noch optimieren ;-)
$fp = fopen("struktur.gif", "wb");
$tok = 0;
for ($i=20; $i<strlen($x); $i++){
if ((substr($x,$i,1)=="\n") || (substr($x,$i,1)=="\r")) {
$tok++;
if($tok>=3) break;
} else {
$tok=0;
}
}
$i++; $i++;
printf(" %d %d %d \n", $i, strlen($x), strlen($x)-$i);
fwrite($fp, substr($x,$i,strlen($x)-$i));
fclose($fp);
$fp = fopen("struktur.xxx", "wb");
fwrite($fp, $x, strlen($x));
fclose($fp);
?>
In der Umgebungs-Variablen REMOTE_ADDR steht die IP-Adresse des Rechners , der die Anfrage sendet. Dies ist nicht zwangsläufig der Rechner, an dem der User sitzt - es kann genausogut ein Proxy sein. Wenn der Benutzer in einer Firma mit mehr als 2-3 PCs sitzt, ist letzteres sogar sehr wahrscheinlich, aber es kann auch bei ganz normalen Provider-Endkunden so sein.
Folgendes Skript verwendet die Umgebungs-Variable REMOTE_ADDR und versucht, den Hostnamen zur IP mit der Funktion gethostbyaddr() zu ermitteln (sog. reverse lookup).
<?php
// IP bestimmen
$ip = getenv('REMOTE_ADDR');
// IP auflösen und Host bestimmen
$host = gethostbyaddr($ip);
?>
Kommt die Verbindung über einen Proxy zustande, kann es sein, dass dieser die IP "seines" Clients im HTTP-Header weitergibt. Der verbreitete Proxy squid beispielsweise nennt diesen Header X-Forwarded-For , dessen Inhalt dann (wie andere HTTP-Header auch) in einer Umgebungsvariablen zur Verfügung gestellt wird ( HTTP_X_FORWARDED_FOR ).
<?php
function start_timer($event) {
printf("timer: %s<br>\n", $event);
list($low, $high) = explode(" ", microtime());
$t = $high + $low;
flush();
return $t;
}
function next_timer($start, $event) {
list($low, $high) = explode(" ", microtime());
$t = $high + $low;
$used = $t - $start;
printf("timer: %s (%8.4f)<br>\n", $event, $used);
flush();
return $t;
}
$t = start_timer("start Befehl 1");
/* Hier den ersten Befehl einfuegen */
$t = next_timer($t, "start Befehl 2");
/* Hier den zweiten Befehl einfuegen */
$t = next_timer($t, "finish");
?>
Möchte man zum Beispiel den Performance-Unterschied zwischen mysql_fetch_row und mysql_fetch_array bestimmen, fügt man die beiden Befehle an die beiden mit Kommentaren versehenen Stellen im Skript ein.
Antwort von Sebastian Bergmann:Eine Alternative zur oben dargestellten Methode der Performance Messung bieten die PEAR Klassen Benchmark_Timer und Benchmark_Iterate.
Die Benchmark_Timer Klasse stellt die benötigte Funktionalität zur Verfügung, um Marken zu setzen, und die zwischen zwei Marken verstrichene Zeit zu berechnen.
<?php
// PEAR::Benchmark_Timer inkludieren
require_once "Benchmark/Timer.php";
// Timer Instanz erzeugen
$Timer = new Benchmark_Timer;
// Marke "Beginn For-Schleife" setzen
$Timer->setMarker("Beginn For-Schleife");
for ($i = 0; $i < 10000; $i++) { }
// Marke "Ende For-Schleife" setzen
$Timer->setMarker("Ende For-Schleife");
// Zeit zwischen den beiden Marken berechnen
print $Timer->timeElapsed("Beginn For-Schleife","Ende For-Schleife");
?>
Die Benchmark_Iterate Klasse leitet sich von der Benchmark_Timer Klasse ab und ermöglicht die wiederholte Ausführung einer Funktion, um so die mittlere Ausführungsgeschwindigkeit derselben zu ermitteln.
<?php
// Funktion, die wir benchmarken wollen
function foo()
{
print "bar<br>";
}
// PEAR::Benchmark_Iterate inkludieren
require_once "Benchmark/Iterate.php";
// Benchmark Instanz erzeugen
$Benchmark = new Benchmark_Iterate;
// Benchmark ausführen
$Benchmark->run(100, "foo");
// Ergebnis ausgeben
$result = $Benchmark->get();
print "Mittlere Ausführungsgeschwindigkeit: " . $result["mean"];
?>
Die run()-Methode erlaubt einen dritten (vierten, fünften, ...) Parameter, welcher der zu testenden Funktion als Argument übergeben wird.
Um nicht nur den Inhalt des aktuellen Verzeichnisses, sondern auch den Inhalt aller Unterverzeichnisse ausgeben zu können, muss man eine rekursive Funktion verwenden. Diese ruft sich bei Bedarf selbst auf. Im nachfolgenden Beispiel durchläuft die Funktion show_dir jeweils das aktuelle Verzeichnis. Wird Datei gefunden, wird der Dateiname ausgegeben. Findet die Funktion ein Verzeichnis, dann wird der Verzeichnisname fett ausgegeben und die Funktion ruft sich mit dem Unterverzeichnis als Parameter selbst wieder auf.
<?PHP
function show_dir($dir, $pos=2)
{
if($pos == 2)
{
echo "<hr><pre>";
}
$handle = @opendir($dir);
while ($file = @readdir ($handle) !== false)
{
if (preg_match("=^\.{1,2}$=", $file))
{
continue;
}
if(is_dir($dir.$file))
{
printf ("% ".$pos."s <b>%s</b>\n", "|-", $file);
show_dir($dir.$file."/", $pos + 3);
}
else
{
printf ("% ".$pos."s %s\n", "|-", $file);
}
}
@closedir($handle);
if($pos == 2)
{
echo "</pre><hr>";
}
}
show_dir("special/");
?>
Mit der Funktion array_rand() . In PHP3 steht diese Funktion nicht zur Verfügung. Nachfolgende Funktion liefert hier bessere Ergebnisse, als shuffle() auf das Array anzuwenden und dann die n Zahlen zu verwenden.
<?PHP
function generate_numbers($min, $max, $anz)
{
$array = range($min, $max);
srand ((double)microtime()*1000000);
for($x = 0; $x < $anz; $x++)
{
$i = rand(1, count($array))-1;
$erg[] = $array[$i];
array_splice($array, $i, 1);
}
return $erg;
}
// 5 eindeutige Zahlen im Bereich von 1 bis 100 ermitteln
$zufalls_array = generate_numbers(1, 100, 5);
echo join("; ", $zufalls_array);
?>
Dem Skript liegt folgende Struktur der MySQL-Tabelle zugrunde:
mysql> describe counter; +-------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | | PRI | 0 | auto_increment | | url | char(255) | | | | | | count | int(11) | | | 0 | | +-------+------------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)
Das Feld url enthält die URL, die aufgerufen wird. Das Feld count enthält die Anzahl der Klicks auf url .
<?php
// Zugangsdaten fuer die Datenbank
// Diese sollten der Sicherheit halber
// in ein Verzeichnis außerhalb des
// Document-Root ausgelagert werden.
$host = "localhost";
$user = "user";
$pass = "demo_password";
$datab = "demo_db";
$table = "counter";
// Die per GET übergebene URL einlesen und datenbanksicher machen
$url = addslashes($_GET['url']);
// Verbindung zum MySQL-Server aufbauen
$db = @mysql_connect($host, $user, $pass);
if ($db) {
if (@mysql_select_db($datab, $db)) {
// Eintrag fuer die übergebene URL um 1 erhöhen.
$query = "UPDATE $table SET count = count + 1 WHERE url = '$url'";
$result = @mysql_query($query);
// Noch kein Eintrag für die URL vorhanden?
if (mysql_affected_rows() == 0) {
$sql_insert = "INSERT INTO $table (url, count) VALUES ('$url', '1')";
@mysql_query($sql_insert);
}
}
}
// Auf übergebene URL weiterleiten
Header("Location: " . $_GET['URL']);
?>
Anwendungsbeispiel:
<a href="http://www.martin-jansen.de">Link</a>
Als Parameter für die Datei count.php wird die URL übergeben, auf die weitergeleitet werden soll. In count.php wird nun der Datensatz in der Tabelle, der $url als Wert für das Feld url enthält um 1 erhöht und es wird auf die neue URL weitergeleitet.
<?php
// Modifikationszeit von (Wie heiße ich?)
$unixTime = filemtime($HTTP_SERVER_VARS["PATH_TRANSLATED"]);
if ($unixTime) {
echo "Letzte Änderung: ". date("d M Y, H:i:s", $unixTime);
} else {
echo "Datei existiert nicht! Falscher Pfad?";
}
?>
Möchte man unter UNIX-Derivaten das Datum der letzten Änderung der zugehörigen Inodes (z.B. Änderungen der Filesystem-Rechte) erfahren, so ist die Funktion filectime() zu benutzen.
Auf der Seite http://www.php-center.de/artikel/i18n.php3 findet man einen Artikel, der sich mit der Internationalisierung von PHP-generierten Seiten befasst.
Es gibt keine zuverlässige Methode, dies herauszufinden, da HTTP ein zustandsloses Protokoll ist und daher keine Stati "eingeloggt, ausgeloggt" wie bei FTP, Telnet etc. vorhanden sind. Der Client (Browser) baut eine Verbindung zum Webserver auf, fordert eine Seite mit all ihren Elementen (Bildern, JavaScript etc.) an und beendet danach die Verbindung wieder. Es entsteht also immer nur eine punktuelle, temporär andauernde Verbindung zwischen dem Webserver und dem Client des Anwenders.
Mit Hilfe von PHPlib und den sess_*-Funktionen von PHP4 kann man einen Besucher zwar wiedererkennbar machen, man kann damit jedoch nur feststellen, wann ein Besucher zuletzt eine Seite angefordert hat.
Wie lange er diese liest und daher noch "auf der Seite online" ist, kann man nicht herausfinden. Er könnte 30 Minuten an dem Text einer Seite lesen oder aber auch das Browserfenster direkt nach Anforderung der Seite schließen; der Server weiss nur, wann er die Seite wem ausgeliefert hat.
Es gibt einige Denkansätze, wie man diese fehlende Funktionalität nachbauen könnte, welche jedoch alle von der Zusammenarbeit mit dem Browser des Besuchers abhängig sind, um halbwegs zuverlässig zu arbeiten und/oder nur Näherungsweise an die reellen Gegebenheiten herankommen.
Ein paar davon bauen auf Javascript auf, welches man von vornherein ausklammern sollte, da nicht jeder Browser Javascript beherrscht und auch angeschaltet hat. Diese Methode ist also unzuverlässig.
Eine weitere Idee basiert darauf, einen kleinen "Blindframe" auf der Seite zu platzieren, welcher regelmäßig automatisch per META-Tag neugeladen wird. Auch wird darauf vertraut, dass der automatische Refresh wirklich bei jedem Besucher ausgeführt wird und man verpflichtet seine Besucher dazu, einen Browser zu benutzen, der Frames beherrscht. Darüberhinaus vergrault man sich auf Dauer einige Besucher, die die ständige Netzaktivität des Browsers irritiert oder nervt.
Zuletzt könnte man noch auswerten, wieviele Benutzer innerhalb der letzten x Minuten eine Seite angefordert haben und daher Annahmen darüber treffen, ob diese Besucher noch da sind. Auch dies ist keine zuverlässige Aussage.
Zusammenfassend lässt sich also sagen, dass die Aussage "x User online" (wie sie auf vielen Sites zu finden ist) reines Blendwerk sind. Es ist technisch nicht möglich, diese Aussagen zu treffen.
Indem man mit fsockopen() eine Verbindung zum Webserver herstellt, einen HTTP-HEAD-Request auf die zu überprüfende Ressource absetzt und die Antwort auswertet.
Wer dies nicht selber programmieren will, kann Scripts wie z.B. die Funktion phpLinkCheck benutzen.
"Richtige" Excel-Dateien lassen sich kaum erzeugen, da .xls ein binäres, undokumentiertes proprietäres Microsoft-Format ist, das zudem von Version zu Version verändert wird.
Stattdessen kann man aber Textdateien erzeugen, deren Werte (Spalten) mit Tabulatoren und deren Zeilen mit CRLF ( \r\n ) getrennt sind, und diese Excel als .xls-Datei "unterschieben". Wichtig ist hierbei, als Dateityp tatsächlich eine Excel-(.xls)-Datei zu deklarieren - obwohl Excel diverse Importformate wie .csv (comma separated value) kennt, funktioniert es damit, vermutlich mangels definiertem Content-Type, nicht wie gewünscht.
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=\"excel.xls\"");
readfile($filename); // Datei 1:1 durchreichen
// echo "Titel (A1)\r\nA2\tB2\r\nA3\tB3"; // ODER mit PHP erzeugen
Anmerkung: Trotz der Angabe inline bietet der Browser i.d.R. einen Dialog an, der das direkte Öffnen oder aber das Speichern der Datei ermöglicht. Die Angabe attachment dagegen führte zu kuriosen, nicht brauchbaren Ergebnissen...
Ein gänzlich anderer Weg, Excel-Dateien zu erzeugen, führt über die COM-Funktionen von PHP4. Damit kann man eine Excel-Instanz direkt ansteuern und so u.a. eine echte .xls-Datei generieren. Wer das ausprobieren will, der lese diesen Artikel und besorge sich die Excel Class von Alain Samoun.
Wer auch auf Unix-Systemen echte Excel-Dateien erstellen will, sollte sich die Klasse Biff von Christian Novak ansehen. Hiermit ist es möglich direkt das binäre Excel-Format zu schreiben. Verschiedene Schriftformatierungen, sowie Zellenformatierungen, Kommentare und sogar Kopf- und Fusszeile werden unterstützt.
Im PEAR-Projekt entsteht eine Klasse zum Erzeugen von Excel-Dokumenten. Sie steht unter PEAR::Package::Spreadsheet_Excel_Writer zum Download verfügbar.
PHP4 bietet die Funktion ip2long() , mit der man eine IP-Adresse in Punktschreibweise ("dotted quad") in die numerische Form umrechnen kann, was einen einfachen größer/kleiner-Vergleich erlaubt. Der Haken daran: Bei PHP beträgt der Maximalwert eines Integer-Typen etwas über 2 Milliarden (genau: 2.147.483.647, vorzeichenbehafteter 32-Bit-Wert), was für die Umrechnung von IP-Adressen nicht ausreicht; bei 127.255.255.255 ist Schluss, danach wechselt das Vorzeichen.
Wandelt man die IP dagegen in einen String um, gibt es dieses Limit nicht. Dann kann man einfach mittels if() oder strcmp() vergleichen:
if (ip2str($start) <= ip2str($ip)
AND
ip2str($ip) <= ip2str($ende))
...
function ip2str($ip) {
$ip = preg_replace("/(\d{1,3})\.?/e",
'sprintf("%03d", \1)',
$ip);
return (string)$ip;
}
Der einfachste Weg dies zu testen, ist die Zahl durch 2 zu teilen und zu überprüfen, ob die Division einen Rest zurückgibt.
In PHP kann man dies wie folgt realisieren:
<?php
if ($zahl % 2 != 0) {
echo "Der Wert der Variablen \$zahl ist ungerade";
} else {
echo "Der Wert der Variablen \$zahl ist gerade";
}
?>
Normalerweise ist für die Umsetzung von Sekunden in ein "lesbares Format" der Befehl date() zuständig, der einen Unix-Timestamp (vergangene Sekunden seit dem 1.1.1970) verarbeitet. Möchte man jedoch eine Anzahl Sekunden nicht als absolutes Datum, sondern als Intervall in Tagen, Stunden, Minuten und Sekunden darstellen, hilft folgende Funktion:
function intervall($sek) {
$i = sprintf('%d Tag%s, %d Stunde%s,'.
' %d Minute%s und %d Sekunde%s',
$sek / 86400,
floor($sek / 86400) != 1 ? 'e':'',
$sek / 3600 % 24,
floor($sek / 3600 % 24) != 1 ? 'n':'',
$sek / 60 % 60,
floor($sek / 60 % 60) != 1 ? 'n':'',
$sek % 60,
floor($sek % 60) != 1 ? 'n':''
);
return $i;
}
echo intervall(99114);
In der folgenden Funktion bgcolor() kann man beliebig viele Farben im Array $col definieren, die bei jedem Aufruf der Reihe nach berücksichtigt werden. Optional kann die Funktion mit einem Integer-Wert aufgerufen werden ( bgcolor(n) ), um immer n aufeinander folgende Zeilen derselben Farbe zu erhalten.
function bgcolor($row = 1) {
static $i;
static $col = array('#FFDDDD',
'#DDFFDD',
'#DDDDFF'
); // etc.
$bg = $col[(int)($i + .00000001)];
$i += 1 / $row;
if ($i >= count($col)) $i = 0;
return $bg;
}
// Ausgabe einer Tabellenzeile (in einer Schleife):
printf("<tr bgcolor='%s'><td>...</td></tr>\n", bgcolor(2));
Besonders, wenn man die Zeilen sowieso mitzählt, kann man den gleichen Effekt leicht mittels des Modulo-Operators ( % ) und CSS erreichen.
printf("<tr class='row%s'><td>...</td></tr>\n", $line % 2);
Und im Stylesheet:
row0 {background-color:#FFDDDD}
row1 {background-color:#DDFFDD}
Zur Erklärung: Der Modulo-Operator gibt den Rest der Division der zwei Werte zurück. 13 % 5 ist also zum Beispiel gleich 3 (Mathematisch korrekt ausgedrückt: 13 ist kongruent 3 modulo 5). $line % 2 gibt also abwechselnd 0 und 1 zurück.
Es gibt einige fertige Scripte, die mit Hilfe von PHP testen können, ob eine bestimmte UIN online ist. Die meisten dieser Scripte funktionieren allerdings nicht mehr, weil ICQ (wieder einmal) etwas verändert hat. Der Server liefert auch weiterhin Redicts auf die Bildchen, allerdings hat sich die URL geändert.
Das folgende Script wertet die Redirects aus:
function GetICQ($uin) {
if (!is_numeric($uin)) return FALSE;
$fp = fsockopen('status.icq.com', 80, &$errno, &$errstr, 8);
if (!$fp) return FALSE;
$request = "HEAD /online.gif?icq=$uin HTTP/1.0\r\n"
."Host: web.icq.com\r\n"
."Connection: close\r\n\r\n";
fputs($fp, $request);
do {
$response = fgets($fp, 1024);
}
while (!feof($fp) && !stristr($response, 'Location'));
fclose($fp);
if (strstr($response, 'online1')) return 'online';
if (strstr($response, 'online0')) return 'offline';
if (strstr($response, 'online2')) return 'disabled';
// disabled meint, dass der Benutzer eingestellt hat, dass sein
// Status im Web nicht angezeigt wird.
return FALSE;
}
// Aufruf:
echo GetICQ(12423456);
Der Check, ob ein User des Yahoo! Messengers online ist, funktioniert analog zu dem Check auf ICQ UINs.
// Contributed by Maik Große, http://about-php.de/
function GetYahoo($_yahoo = '') {
if (empty($_yahoo)) return true;
$response = "";
if ($fp = fsockopen('opi.yahoo.com', 80, &$errno, &$errstr, 8)) {
$request = "HEAD /online?u=".$_yahoo."&m=t&t=0 HTTP/1.0\r\nHost: opi.yahoo.com\r\nConnection: close\r\n\r\n";
fputs($fp, $request);
do {
$response = fgets($fp, 1024);
}
while (!feof($fp) && !stristr($response, 'Location'));
fclose($fp);
}
if (strstr(strtoupper($response), 'NOT ONLINE')) return false;
if (strstr(strtoupper($response), 'ONLINE')) return true;
return false;
}
// Aufruf:
echo "Yahoo User: 12423456 is ".((GetYahoo('12423456')) ? "online" : "not online")." ...";
Immer wieder kommt es vor, dass man die Buchstaben des Alphabets in einem Array benötigt. Natürlich muss man das nicht von Hand machen:
for ($i = 65; $i <= 90; $i++) {
$alphabet[] = chr($i);
}
Für Großbuchstaben lässt man die for-Schleife von 65 bis 90 laufen, für Kleinbuchstaben von 97 bis 122. Die Zahlen (ASCII-Codes) werden durch die Funktion chr() in die entsprechenden Buchstaben umgewandelt und im Array $alphabet abgelegt.
Es geht aber auch noch kürzer - seit PHP 4.1.0 akzeptiert die Funktion range() auch Buchstaben als Bereichsangabe:
$alphabet = range('A', 'Z');