Kategorien
Webdesign

AJAX: Die Zukunft des Webs? – Teil 1

Der AJAX-Ansatz ist noch jung und wird schon heiß gelobt – doch was steckt dahinter, wie funktioniert AJAX und wie kann AJAX im Web konkret Anwendung finden?

In herkömmlichen Webanwendungen werden Formulare ausgefüllt und abgeschickt. Bis dies geschieht, bleiben die Daten auf der Clientseite, der Server bekommt nicht mit, dass Daten in das Formular gefüllt werden. Stürzt nun der Browser mitten während des Ausfüllens ab, sind die Daten verloren, da sie ja nicht abgeschickt wurden.

Ein solches Szenario kann man mit dem Ansatz von AJAX verhindern. Dabei werden bereits während des Ausfüllens von Formularen asynchron die Daten zum Server über das Javascript Objekt XMLHttpRequest geschickt.

Die Kommunikation kann auch anders herum erfolgen; das heißt, wenn der Benutzer zum Beispiel Daten eingibt, wie die Postleitzahl, so kann der Server direkt nach dem Eingeben der Postleitzahl in einer Datenbank nach dem korrespondierenden Ortsnamen suchen und diese im Formular anzeigen. Das heißt man muss Änderungen im Formular nicht mehr explizit abschicken, das wird im Hintergrund erledigt.

Zum einen wird damit die Serverlast ein wenig heruntergeschraubt, da die Daten in kleinen Paketen gesendet werden, zum anderen wird das Ausfüllen und Absenden von Formularen beschleunigt. Dieser Ansatz wird als Asynchronous Javascript and XML bezeichnet – kurz AJAX; dabei ist AJAX keine eigene Technologie, sondern lediglich eine Kombination von bereits existierenden und profilierten Technologien. AJAX wird bereits ausgiebig im Internet, unter anderem von Google und Co. verwendet.

Das wohl bekannteste Beispiel ist Google Suggest. Dabei wird bereits während der Eingabe eines Suchstrings nach etwaigen Ergänzungen gesucht und die voraussichtliche Ergebniszahl zu dem Suchbegriff angezeigt. Das Ganze funktioniert, während man im Suchfeld Suchbegriffe eingibt. So kann man schon vorweg sehen, ob es sich lohnt, die Suche genauer zu spezifizieren, sollten sich etwa zu viele Suchergebnisse finden.

Google Suggests - Während der Eingabe werden von Google Vorschläge für die Suche angezeigt
Google Suggests – Während der Eingabe werden von Google Vorschläge für die Suche angezeigt

Mit dem AJAX-Ansatz könnte man das Stop-and-Go-Verhalten von Webapplikationen umgehen; der Benutzer muss nicht mehr auf den Server warten, da alles im Hintergrund geregelt wird.

Über das XMLHttpRequest Objekt ist es möglich, über JavaScript und XML mit Webapplikationen, die auf dem Server laufen, zu interagieren. Dabei werden die Daten, zum Beispiel aus Formularen in Form von HTTP-Anfragen versendet. Das funktioniert ähnlich wie mit SOAP oder generell wie bei Webservices, nur das hier über JavaScript mit einer Applikation asynchron kommuniziert wird. Die Antworten des Servers werden in Form von XML versandt.

Im Modell sieht die klassische Client-Server Interaktion im zeitlichen Verlauf etwa wie folgt aus.

Klassisches Modell der Client-Server Kommunikation; Dateneingabe und Datenverarbeitung auf dem Server verlaufen synchron
Klassisches Modell der Client-Server-Kommunikation; Dateneingabe und Datenverarbeitung auf dem Server verlaufen synchron

Dabei werden die Zeitverzögerungen, die sich aufgrund des Sendens und Empfangens von Anfragen beziehungsweise Antworten deutlich.

Mit dem AJAX-Ansatz wird eine weitere Engine eingebaut, die den Versand der Daten asynchron regelt. Wie man aus der Modellskizze erkennen kann, werden zum Beispiel die Daten aus der ersten Eingabe noch serverseitig verarbeitet, während zur selben Zeit bereits wieder neue Daten eingegeben werden können. Der Versand wird über die AJAX-Engine geregelt.

AJAX Modell – Dateneingabe und Datenverarbeitung auf dem Server verlaufen asynchron
AJAX-Modell – Dateneingabe und Datenverarbeitung auf dem Server verlaufen asynchron

Das für die Kommunikation notwendige XMLHttpRequest-Objekt wurde zunächst von Microsoft als ActiveX entwickelt, ist aber heutzutage in allen gängigen Browsern integriert. Es ist der Kern des AJAX-Ansatzes, da mit ihm die Client-Server Kommunikation, die im Hintergrund abläuft, ermöglicht wird. Jedoch liegt der Fokus bei AJAX nicht allein bei XMLHttpRequest, sondern grundsätzlich bei der Idee des Zusammenspiels der verschiedenen Technologien.

Ablauf einer Anfrage über XMLHttpRequest
Ablauf einer Anfrage über XMLHttpRequest

Im InternetExplorer wird XMLHttpRequest immer noch über ein AcitiveX-Objekt initialisiert, während es in Browsern wie Mozilla als einfaches Objekt abgeleitet werden kann.

Da AJAX verstärkt über XMLHttpRequest Javascript als Technologie integriert, ergeben sich jedoch auch Probleme. So muss, zum Beispiel gerade schon bei der Initialisierung eines Objektes auf die Besonderheiten der verschiedenen Browser geachtet werden. Dadurch können die Skripte mitunter recht komplex ausfallen und man muss sich fragen, ob sich der Aufwand überhaupt lohnt. Der „Zurück“-Button des Browsers wird ebenfalls bei der Verwendung einer solchen AJAX Technologie unnutzbar, da dieser ja nur über die direkten Anfragen des Clients funktioniert.

Des Weiteren ergeben sich mit AJAX auch Probleme etwa für die barerierearmen Gestaltung von Webformularen, da AJAX nicht den Anforderungen der Web Accesibility Initiative des W3C entspricht und auch nur schwer kompatibel gemacht werden kann.

Dennoch ergeben sich den Programmierern von Webapplikationen mit AJAX viele neue Möglichkeiten. Die Webapplikationen werden immer ähnlicher zu herkömmlichen Desktop-Applikationen, was auch zu deren Emanzipation beitragen kann. Auf jeden Fall unterstützt AJAX die weitergehende Entwicklung von Webtechnologien. ™

Erstveröffentlichung 31.08.2005

Kategorien
Webdesign

Kampf den Spambots

In den letzten Jahren wurden viele Strategien entwickelt, um gegen Spambots und Email-Harvesters vorzugehen. Mancher versuchte es, indem er die Datenbestände der Spammer mit zufällig generierten Adressen verwässert. Ob die Methode zum Ziel führt ist fraglich. Wer es tun möchte, findet hier eine einfache Anleitung.

Spambots, das sind lästige Crawler oder Robots die das Internet nach Emailadressen durchforsten, um diese dann in Datenbanken zu hinterlegen und an etwaige Kunden zu verkaufen. Es scheint ein ziemlich lukratives Geschäft zu sein, da das Spamaufkommen immer noch stetig wächst; so ergab eine Auswertung von Symantec, dass der Spamanteil bei knapp 66% liegt und dabei wurden 106 Milliarden Emails gescannt. Andere Schätzungen siedeln das Spamaufkommen auch schon mal bei 90% an.

Mittel und Wege dem Einhalt zu gebieten gibt es viele – sie reichen vom maskieren der Emailadressen über JavaScript oder Unicode bzw. der Darstellung der Emailadresse als Grafikdatei.

Auf SpamHelp.org wird ein Ansatz vorgestellt; dabei wird festgestellt, dass der Kampf gegen die Spambots durch aussperren, oder ewiges Spammailfiltern zu defensiv ist. Der Ansatz von SpamHelp.org ist darauf ausgerichtet, die Lukrativität des Geschäfts mit Spamemails zu zerstören, indem die Datenbanken der Spambots mit Emailadressen „zugespamt“ werden.

Hierzu wird schlicht ein Skript programmiert, mit dem bei jedem Seitenaufruf 100 neue Zufallsemailadressen aufgelistet werden; diese wandern bei jedem Spambotbesuch in deren Datenbank. Doch mit den Emailadressen können zumindest die Kunden von Spammern nicht viel anfangen, da die Emailadressen schlicht nicht existieren.

Ein Skript, mit dem eine solche Seite generiert werden kann, ist mit den gängigen Programmiersprachen schnell erstellt.
Es könnte wie folgt aussehen:

adressbuch.php

    <html>
    <head>
    <title>Emailadressen von Bekannten :-)</title>
    </head>
    <body>
    <h1>Adressbuch</h1>
    <?php
    function get_tld() {
    //man kann noch mehr TLDs hinzufügen
    $tld_data = array("us","ws","tf","cn","ch","at","de","info","biz","aero");

    //zufällig ausgewählte TLD wird zurückgegeben
    return $tld_data[rand(0,count($tld_data)-1)];
    }
    //Funktion zur Erzeugung von Zufallszeichenketten für Emailadresse
    function create_string() {
    $length = rand(5,20);
    $chars = "qwertzupasdfghkyxcvbnm123456789WERTZUPLKJHGFDSAYXCVBNM";
    srand ((double)microtime()*1000000);
    for($index = 0; $index < $length; $index++)
    {
    $string .= substr($chars,(rand()%(strlen ($chars))), 1);
    }
    return $string;
    }
    //Erzeugung von 100 Emailadressen
    for($i=0; $i <= 100; $i++)
    {
    echo "<a href=\"mailto:";
    echo create_string()."@".create_string().".".get_tld();
    echo "\">".create_string()."</a>";
    echo "<br>";
    }
    ?>
    <a href="http://www.ihre-seite.de/adressbuch.php?seite=<?php echo
    rand(1,1000) ?>" title="Adressbuch - Spambots only">Hier
    gibts noch mehr Email-Adressen</a>
    </body>
    </html>

Dabei besteht es schlicht aus zwei Funktionen; mit der ersten Funktion wird eine Topleveldomain für die Emailadresse zufällig ausgewählt (hier kann man natürlich weitere TLDs hinzufügen); mit der zweiten Funktion werden Zufallsstrings gebildet, die eine Länge von fünf bis zwanzig Zeichen haben.

In der For-Schleife werden daraufhin mit Hilfe dieser beiden Funktionen 100 Emailadressen samt Mailto-Links erzeugt. Den Link zu einem solchen Skript kann man jetzt auf der Webseite unterbringen.

Durch den nochmaligen Aufruf von email-liste.php mit einem zufällig angehängten Parameter wird der Spambot angeregt sich noch mehr Emailadressen zu holen. Falls der Spambot so „schlau“ sein sollte, und erkennt, dass er im Endeffekt immer auf das selbe Skript zurückgeschickt wird, könnte man den Parameter über URL Rewriting etwa durch das Modul mod_rewrite verstecken.

Der hierfür notwendige Eintrag in eine .htaccess könnte wie folgt aussehen:

    RewriteEngine on
    RewriteRule ^adressbuch/seite-(.*)\.html$ adressbuch.php?seite=

Dabei müssen sie auch den Link im Skript verändern auf:

    <a href="http://www.ihre-seite.de/adressbuch/seite-<?php
    echo rand(1,100).html ?>" title="Adressbuch - Spambots only">Hier
    gibts noch mehr Email-Adressen</a>

Um zu verhindern, dass ein „normaler“ Suchmaschinenbot auf diese Datei hereinfällt, sollte man in der Robots.txt den Bots den Besuch des Adressbuchs verbieten; das heißt nur Bots, die sich an die Robots.txt halten, fallen nicht darauf rein. In wie weit diese Strategie effizient ist oder gar Abhilfe verschafft ist fragwürdig – schließlich wird so auch die Menge der verschickten Junk-Mails in die Höhe getrieben. Jedoch ist sie ein aggressives Mittel, um einmal gegen die Spammer „zurückzuschlagen“.

Nachtrag
Von Gerhard Brauckmann gibt es Plugin für WordPress, ab Version 1.5.2, mit dem man spielend einfach „zurückschlagen“ kann.

Erstveröffentlichung 26.08.2005

Kategorien
Webdesign

Kurze, suchmaschinenfreundliche URLs

Viele Wege führen nach Rom – die einfachsten sind in der Regel die kürzesten. Hier geht es darum, mit Hilfe von Content Negotiation auf Apache Webservern kurze URLs zu realisieren – ganz ohne mod_rewrite.

Über das mod_rewrite Modul des Apache Webservers kann man auf einfache Weise kurze, suchmaschinenfreundliche URLs realisieren. Das mod_rewrite Modul wird jedoch nicht von vielen Webhostern unterstützt, da man damit auch einiges falsch machen kann (insbesondere bei Seiten mit viel Traffic kommt es schnell zu hoher Prozessorauslastung). Content Negotiation ist in der Regel jedoch harmlos und wird von vielen Hostern unterstützt – damit der hier vorgestellte Lösungsweg funktioniert, muss das Modul mod_negotiation lediglich aktiviert sein . Die grundlegende Idee bei Content Negotiation ist, dass der Server anhand der Präferenzen des Clients (Browser, etc.) entscheidet, welchen Content er an den Client liefert.

Wenn eine Anfrage etwa so aussieht:

http://www.drweb.de/news

Dann sucht der Server nach allen Dateien mit dem Namen „news“ und liefert die, dem Wünschen des Client entsprechende Version aus. Hierzu untersucht der Server das komplette Verzeichnis.

Die Anfrage könnte auch wie folgt aussehen:

http://www.drweb.de/news/2005/07/16

Existieren nun die Verzeichnisse 2005, 07 und 16 nicht, dann erkennt dies der Apache Server bei aktivierten Content Syndication und arbeitet den URL so lange ab, bis er zu einem existierenden Verzeichnis oder Datei kommt. Diese kann z.B. die Datei news.php sein. Der Server sucht daraufhin nach alternativen Versionen von „news“, sind keine vorhanden, so wird die Datei news.php angesprochen – der Suffix im URL bleibt beim Aufruf erhalten, sodass das Skript auf die Daten im URL zurückgreifen kann.

Damit wird etwa eine URL wie

http://www.drweb,.de/news.php?jahr=2005&monat=07&tag=16 ersetzt.

Mit ein paar Codezeilen PHP kann man dann die URL auslesen und die Parameter für das Skript verwenden; in unserem Fall könnte mit der Datumsangabe nach den Nachrichten des aktuellen Tages etwa in einer Datenbank gesucht werden.

news.php

 <?php
    //die einzelnen Parameter liegen jetzt im numerischen Array $parameter vor
    $parameter = explode("/",$_SERVER['REQUEST_URI']);
    ?> 

Kategorien
Webdesign

Automatische Auswahl mit Content Negotiation

Ein einfaches Apache Modul, das häufig unbeachtet bleibt, bietet auf Grundlage des http-Protokolls interessante Möglichkeiten. Etwa bei der Erstellung internationalisierter Webseiten, bei denen Inhalte in mehreren Sprachen zur Verfügung gestellt werden. Die grundlegende Idee der Content Negotiation ist, dass der Server anhand der Präferenzen des Browsers entscheidet, welchen Content er ausliefert.

Grundsätzlich ist Content Negotiation in HTTP integriert. Der Client – also Browser, Bot oder ähnliches – kann den Server entscheiden lassen, welchen Inhalt und welche Form dieser an den Client zurücksendet. Dabei folgt die Auswahl des Servers den Präferenzen des Clients. Das heißt, man kann über die HTTP-Anfrage des Clients zum Beispiel angeben, welches Format man bevorzugt, .etwa wenn ein Bild sowohl in JPEG als auch in GIF Formatierung vorliegt, In den Anfragen gibt der Client hierzu die akzeptierten MIME Typen an. Ein älterer Browser, etwa der Internet Explorer 3, sendet in der Anfrage nicht, dass er etwa Daten des MIME Typs text/xml empfangen und verwerten kann.

Der Accept-Header, über den eben in der Anfrage spezifiziert wird, welche Inhalte der Browser annimmt, sieht im Idealfall etwa so aus:

 Accept: text/xml, application/xml, application/xhtml+xml, text/html;q=0.9,
    image/png, image/jpeg, image/gif;q=0.2, text/plain;q=0.8, text/css, */*;q=0.1

Dabei handelt es sich um den Accept-Header des MozillaBrowsers. In ihm werden alle gängigen Technologien angegeben. Das q-Attribut gibt den Qualitätswert des bestimmten Inhaltstyps an. Der Wert liegt zwischen 0 und 1. Je höher der Wert, desto höher liegt die Präferenz für einen bestimmten Inhaltstyp. Standardmäßig, also wenn nicht angegeben, ist der q-Wert 1. In dem Fall des Mozilla Browsers ist liegt die Präferenz bei Bildern deutlich bei Bildern des MIME Tpys image/png und image/jpeg. In dem vorher genannten Beispiel würde der Server folglich eher PNGs oder JPEGs zurücksenden als etwa Gifs. Über den Wildcard-Mime-Typ */* bringt der Client zum Ausdruck, dass er auch mit anderen Formaten zurechtkommt beziehungsweise diese zumindest empfängt (auch wenn er dann nicht unbedingt wissen muss, was er mit ihnen anfängt).

Der Accept-Header des Internet Explorers ist für Content Negotiation nicht unbedingt hilfreich:

      Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint,
    application/vnd.ms-excel, application/msword, */*

In dem Header werden weder die Contenttypen HTML, noch XML oder XHTML erwähnt. Das erschwert Content Negotiation auf der Serverseite ganz erheblich. Nehmen wir etwa an, dass der Server die Daten in XML Dateien vorliegen hat, die über ein XSL Stylesheet im Idealfall auf Clientseite dargestellt werden. Sollte der Browser kein XML unterstützen, so soll der Server die Aufgabe der Formatierung von XML und XSL in HTML über einen XSLT-Prozessor übernehmen.

Eine solche Negotiation würde erlauben, dass die XSLT Transformation über den Server nur dann vorgenommen wird, wenn diese auch wirklich benötigt wird. Da der Internet Explorer in seinem Accept keine Angaben zu XML oder gar HTML macht, wüsste der Server, der mit einem solchen Accept umzugehen hat, nicht, was er mit einer solchen Anfrage machen soll. Den Accept-Header, den der Internet Explorer sendet, kann man nur mit Mühe über die Registry verändern.

Wie in der Einleitung schon angesprochen, gibt es auch die Möglichkeit, die Sprachauswahl des Servers über die HTTP – Anfrage direkt zu beeinflussen. In der Anfrage sendet der Browser nämlich auch einen Teil, der etwa wie folgt aussehen könnte.

      Accept-Language: de, en; q=0.5

Das heißt, der Client kann sowohl mit Englisch als auch Deutsch umgehen, wobei die Präferenz bei Deutsch liegt. Die Einstellung der akzeptierten Sprachen kann beim Internet Explorer über das Menü „Internetoptionen – Sprachen“ verändert werden.

Sprachauswahl im Internet Explorer

Nachdem die grundlegenden Ideen von Content Negotiation auf Clientseite erläutert wurden, nähern uns wir jetzt der konkreten Anwendung – d.h. wie man Content Negotiation auf der Serverseite nutzen kann.

Grundsätzlich gibt es mit dem Apache Webserver, bei dem das Modul Content Negotiation aktiviert ist, zwei Möglichkeiten dieses zu nutzen.

Zum einen kann man spezifisch einen Fall untersuchen und alle Alternativen beispielsweise für verschiedene Sprachen angeben; zum anderen kann man generell den Server die Aufgabe übertragen, nach Alternativen zu suchen und die für den Client passende zurückzugeben.

Zunächst soll die explizite Variante vorgestellt werden; dabei werden Type Maps verwendet. Eine Type Map ist eine Datei, die genaue Angaben zu den vorhandenen Alternativen für ein Dokument oder generell eine Datei hat. Um Type Maps verwenden zu können, müssen diese in der Server-Konfiguration aktiviert sein. In der Regel ist die Dateiendung für Type Maps *.var. Eine einfache Type Map könnte wie folgt aussehen:

Beispiel: diagramm.var

      URI: diagramm
    URI: diagramm.jpeg
    Content-type: image/jpeg; qs=0.9
    URI: diagramm.gif
    Content-type: image/gif; qs=0.4

Die Anfrage an die Quelldatei könnte wie folgt aussehen:

http//www.drweb.de/artikel/diagramm

Die Type Map besagt, dass es von dem angeforderten „diagramm“ zwei Versionen gibt – zum einen ein GIF, zum anderen ein JPEG. Der Wert „qs“ beschreibt auf der Serverseite die Qualität der Quelle. Liegt nun die Präferenz des Browsers (wie bei Mozilla) bei JPEGs, so liefert der Browser die Datei diagramm.jpeg zurück

Falls verschiedene Sprachen für das Dokument vorliegen, so könnte die Anfrage wie folgt aussehen:

Beispiel: text.var

      URI: text
    URI: text.html.de
    Content-type: text/html
    Content-language: de
    URI: text.html.en
    Content-type: text/html
    Content-language: en

Die Anfrage an den Server erfolgt wiederum einfach über den Dateinamen. Anhand der vorhandenen Type Map erkennt der Server die Anzahl der Varianten und liefert als Ergebnis die zu den Präferenzen des Clients passende Seite.

Falls es keine passende Seite zu den Wünschen des Clients gibt, so generiert Apache eine Fehlermeldung 406 und listet alle vorhandenen Alternativen auf; unter diesen kann der Besucher sich dann entscheiden.

Die Verwendung von Type Maps ist darauf ausgerichtet, ein explizites Problem zu lösen. Wenn man jedoch generell mehrere Versionen eines Dokumentes vorliegen hat, zum Beispiel in verschiedenen Sprachen, ist es einfacher mit der zweiten Variante das Problem zu lösen – mit den Multiviews. Dabei generiert sich der Webserver quasi selbst eine Type Map für jede Anfrage.

Hierzu muss die Option „MultiViews“ aktiviert werden. Dies kann mit einer .htaccess-Datei geschehen. Der Eintrag in der .htaccess Datei sieht wie folgt aus:

Beispiel .htaccess

      Options +MultiViews

Wird diese Option aktiviert, wird bei einer Anfrage nach „test“ generell genau so verfahren, wie bei den Type Maps. Der Server erkennt die vorhandenen Varianten und liefert diese nach den Präferenzen des Clients. Wichtig ist dabei, dass die Varianten die Dateinamen gemäß der Form test.html.de bzw. test.html.en tragen müssen; anhand des Sprachsuffixes erkennt der Server den Sprachtyp. Dieser richtet sich nach den internationalen Sprachcodes (nach dem RFC 1766 Standard).

Der Vorteil dabei ist, dass man, wenn man etwa Links setzt, nicht immer den Sprachsuffix hinzufügen muss. Gelinkt wird schlicht auf die Seite „test“, der Server erkennt dann automatisch an den Browsereinstellungen, welche Seite er zu liefern hat. Das verringert den Pflegeaufwand deutlich, da man für alle Sprachversionen die gleichen Links verwenden kann. ™

Erstveröffentlichung 22.07.2005

Kategorien
Webdesign

PHP – PRADO Component Framework

Das ereignisbasierte PRADO Framework hat den von Zend ausgelobten PHP5 Wettbewerb gewonnen. Was steckt hinter PRADO? Wie kann man mit PRADO Webseiten entwickeln?

PRADO, steht für PHP Rapid Application Development Object-oriented, ist eine ereignis- und komponentenorientierte Framework für die Entwicklung von PHP5 Applikationen, die die Interaktion mit dem Benutzer im Zentrum sieht.

Die Entwicklung von PHP Applikationen auf Basis von PRADO besteht grundsätzlich daraus, vorgefertigte Komponenten wiederzuverwenden und sie in das Programm und den Datenfluss zu integrieren. Dabei soll PRADO insbesondere die lästige Arbeit, wie etwa die Verifizierung von Benutzereingaben, erleichtern. Des weiteren gibt PRADO dem Programmierer die Möglichkeit eigene Komponenten in das Framework zu integrieren und PRADO so für den eigenen Bedarf zu gestalten.

Der ereignis- und komponentenorientierte Aufbau PRADOs erinnert stark an Delphi oder Visual Basic – bisher gibt es noch keine GUI für PRADO, doch wird das wohl der nächste große Entwicklungsschritt sein.

PRADO zeichnet sich durch eine kaum zuvor gesehene Wiederverwendbarkeit von Programmiercode aus, da die geschriebenen Komponenten in jeweils anderen Zusammenhängen verwendet werden können. Die Wiederverwendbarkeit von Code war der Hauptanlass für die PRADO Entwickler an dem Projekt zu arbeiten – denn ähnliche Projekte, wie zum Beispiel Mojavi oder phrame gibt es schon – der Wunsch, insbesondere des Ideengebers Qiang Xue war es, einen Standard für die Wiederverwendbarkeit von Code zu entwickeln.

In Sachen Formularhandling ist PRADO bisher am weitesten ausgebaut – für eine Formularkomponente muss man lediglich die Ereignisse- und die Eigenschaften einstellen. Mehr dazu wird im Beispiel gezeigt.

Die Vermutung liegt nahe, dass mit der Komplexität des Frameworks die Performance schlechter wird. Doch auch gerade in dieser wichtigen Disziplin ist PRADO Spitze, da es eine komplexe Caching-Technik anwendet.

Einige PRADO Beispiele
PRADO basiert auf PHP5 und macht extensiven Gebrauch von SimpleXML, sowie von der Zlib-Bibliothek. Die Installation ist in wenigen Schritten erledigt – das Paket muss lediglich heruntergeladen, entpackt und auf den Server geladen werden.
Die Standardinstallation kommt bereits mit einigen kleinen Beispielen, hier wollen wir uns zunächst einmal das Beispiel „helloworld“ anschauen.

Wenn Sie sich das Verzeichnis der Beispiele anschauen, finden Sie bei dem „helloworld“ Beispiel eine Anzahl von Dateien. Im Stammverzeichnis ist die Datei „helloworld.php“; über sie wird das eigentliche Programm aufgerufen und das Framework eingebunden.

helloworld.php

 <?php
//Einbinden des Frameworks
require_once(dirname(__FILE__).
'/../framework/TApplication.php');
//Verweis auf die Spezifikationsdatei und erzeugen eines neuen Objektes
$app=TApplication::getInstance
('helloworld/application.spec');
$app->run(); //Applikation wird gestartet
?>

In dem Unterverzeichnis helloworld befinden sich drei Dateien. Da ist zum einen die Datei „application.spec“, über die das Framework und die Applikation selbst konfiguriert wird. Die application.spec ist eine XML-Datei, über default-page=“HomePage“ wird auf die Klasse HomePage verwiesen, die in der Datei HomePage.php definiert ist – des weiteren kann beispielsweise die Caching-Funktion eingeschaltet werden.

application.spec

<?xml
version="1.0" encoding="UTF-8"?>
<application default-page="HomePage" cache-path="">
<alias name="Pages" path="." />
<using namespace="System.Web.UI.WebControls" />
<using namespace="Pages" />
</application>

In der HomePage.php Datei befindet sich die PHP-Klasse, über die dem Programm „leben eingehaucht“ wird. Die Datei „HomePage.tpl“ ist die Template-Datei, über die das aussehen der Webseite bestimmt wird. Die Trennung zwischen Programmlogik und Design ist hier vollzogen.

HomePage.tpl

<html>
<head>
<title>Hello, world!</title>
</head>
<body>
<com:TForm>
<com:TButton Text="Click me" OnClick="clickMe" />
</com:TForm>
</body>
</html>

Betrachtet man sich die HomePage.tpl-Datei, so fällt gleich die verwendete Komponente auf. Über den Namespache com: wird eine Komponente vom Typ TForm, also einem Formular und eine Buttonkomponente TButton erzeugt. Auf dem Button soll der Text „Click me“ stehen und bei dem Event OnClick soll die Funktion „clickMe“ aufgerufen werden.

Genau das ist die Stelle, an der PRADO richtig interessant wird. Ein ereignisorientiertes Formularhandling der Form kennt man so bisher nur von JavaScript, VBScript oder ähnlichem.

Die Funktion „clickMe“ wird in der PHP-Klasse aus der Datei HomePage.php genauer spezifiziert.

HomePage.php

<?php
class HomePage extends TPage
{
function clickMe($sender,$param)
{
$sender->Text="Hello, world!";
}
}
?> 

Dabei ist die Klasse HomePage eine Erweiterung der PRADO Komponente Tpage. Die Funktion macht nichts anderes, als den Text, der auf dem Button steht, über die $sender->Text=“Hello, world!“; zu verändern.

Mit diesen paar Dateien hat man bereits eine kleine PRADO-Applikation entwickelt. Natürlich geht das ganze mit JavaScript in einer einzigen Zeile – die Ereignisorientierung in PHP ist jedoch neu.

PRADO kommt mit einer ganzen Menge von vorgefertigten Komponenten; im nächsten Beispiel-Formular sind einige weitere vorgestellt. Dabei handelt es sich um ein typisches Formular, wie es für die Registrierung von Benutzern oder Newsletter-Abonennten verwendet wird.

<com:TForm>
Username <br/> <com:TTextBox ID="username" /><br />
<com:TRequiredFieldValidator
ControlToValidate="username"
ErrorMessage="Bitte einen Benutzernamen angeben" />
<com:TRegularExpressionValidator
ControlToValidate="username"
RegularExpression="[\w]{3,}"
ErrorMessage="Benutzernahme muss mindestens aus drei Zeichen bestehen."
/>
<br />
Email <br /> <com:TTextBox ID="email" /><br />
<com:TRequiredFieldValidator
ControlToValidate="email"
ErrorMessage="Bitte Emailadresse angeben!" />
<com:TEmailAddressValidator
ControlToValidate="email"
ErrorMessage="Gueltige Emailadresse angeben!" /><br />
Password:<br />
<com:TTextBox ID="password" TextMode="Password" />
<com:TRequiredFieldValidator
ControlToValidate="password"
ErrorMessage="Sie muessen ein Passwort angeben." />
<com:TRegularExpressionValidator
ControlToValidate="password"
RegularExpression="[\w]{6,}"
ErrorMessage="Passwort muss 6 Zechen haben" />
<br/>
Passwort wiederholen: <br />
<com:TTextBox ID="password2" TextMode="Password" />
<com:TRequiredFieldValidator
ControlToValidate="password2"
ErrorMessage="Bitte Passwort ein zweites mal eingeben." />
<com:TCompareValidator
ControlToValidate="password2"
ControlToCompare="password"
ErrorMessage="Die Passwoerter stimmen nicht ueberein!" />
<br/>
<com:TButton Text="Registrieren" OnClick="registerAccount"
/>
</com:TForm>

Beispiel: PRADO – Formular Verifizierung

Das Formular greift auf eine weitere vorgefertigte Komponente zurück, nämlich die TTextBox Komponente, die einem einfachen Eingabefeld entspricht. Über

<com:TRequiredFieldValidator
ControlToValidate="username"
ErrorMessage="Bitte einen Benutzernamen angeben" />

Wird direkt mit der Eingabe überprüft, ob das Feld mit der ID „username“ auch nicht leer steht.

Über die Komponente TRegularExpressionValidator wird das Feld mit der ID „username“ zudem noch auf die Gültigkeit entsprechend des Regulären Ausdrucks überprüft.

<com:TRegularExpressionValidator
ControlToValidate="username"
RegularExpression="[\w]{3,}"
ErrorMessage="Benutzernahme muss mindestens aus drei Zeichen bestehen."
/>

Eine andere Komponente, die hier in dem Beispiel verwendet wird ist die TEmailAdressValidator, über die eingegebene Emailadressen nach Gültigkeit überprüft werden.

<com:TEmailAddressValidator
ControlToValidate="email"
ErrorMessage="Gueltige Emailadresse angeben!" /><br />

Die lästige Arbeit des Überprüfens von Eingaben in Formularen kann direkt bei der Definition des Formulars an die entsprechende Komponente weitergegeben werden. Die Komponentenentwicklung für PRADO läuft zur Zeit auf Hochtouren, da insbesondere nach dem Gewinn der ZEND Auszeichnung die Motivation der Entwickler sehr groß ist.
Wir werden sehen, was PRADO wohl in Zukunft noch alles bieten wird – insbesondere eine GUI wird von vielen PRADO Fans erwartet.

Kategorien
Webdesign

Diese Seite weiterempfehlen

Wer seinen Besuchern die Möglichkeit geben will, die gerade von ihm gelesene Seite an einen Freund weiterzuempfehlen, kann das auf vielerlei Weise tun. Wir machen das im Einbau und können mit zusätzlichen Features das Skript noch komfortabler machen. Zum Beispiel mit einem Virenschutz oder dem automatischen Auslesen der Meta-Description.

Eine solche Funktion gehört beinahe schon zu guten Ton. Sie wird häufig eingesetzt. In wie weit sie jedoch benutzt werden, hängt vom Profil der Besucher ab. Auf Spiegel-Online wird ein solches Tool stark benutzt. Zumindest lässt es jede Website komfortabler erscheinen.

Es funktioniert relativ einfach. Ein Besucher schaut sich eine Seite an, klickt auf den Link „Diese Seite einem Freund weiterempfehlen“ oder ähnlich – ein Fenster öffnet sich und eine Empfehlung kann ausgesprochen werden.

Der Empfehlungslink sieht wie folgt aus:

 <a href="#" onClick="window.open('empfehlen.php?ref
='+location.href,'NewWindow','width=400, height=600')">Diese Seite weiterempfehlen?</a>

Dabei wird in einem neuen Popup-Fenster das Skript empfehlen.php aufgerufen. An dieses wird der Parameter „ref“ weitergegeben. Der Parameter bekommt mit „location.href“ die aktuelle Adresse übergeben.

Möchte man auf das Popup-Fenster verzichten, lässt sich die Sache auch über einen einfachen Link regeln.

<a
href="javascript: window.location.href='empfehlen.php?ref='+location.href">Diese
Seite weiterempfehlen?</a>

Das Empfehlen-Skript besteht zum einen aus einer Email-Funktion und zum anderen aus einem Formular. Des Weiteren müssen die Eingaben der Benutzer vor dem Absenden der Email auf Ihre syntaktische Korrektheit hin untersucht werden.

Das Skript dazu:

empfehlen.php

<html><head><title>Diese
Seite weiterempfehlen...</title></head>
<body>
<div align="center">
<?php
$url = $_GET['ref'];
if (isset($_POST['submit'])) { //ist das Formular abgesendet worden?
$error = false;
$regex ="/^"."[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*"."@"
."([a-zA-Z0-9-]+\.)+"."([a-zA-Z]{2,4})"."$/";
if (!preg_match($regex, $_POST['s_email'])) {
echo "Geben Sie eine gültige Sender-Emailadresse ein!<br />";
$error = true;
}
if (!preg_match($regex,$_POST['e_email'])) {
echo "Geben Sie eine gültige Empfänger-Emailadresse ein!<br
/>";
$error = true;
}
if ($_POST['s_email'] == $_POST['e_email']) {
echo "Geben Sie zwei verschiedene Emailadressen an<br />";
$error = true;
}
if ($_POST['s_name'] == "" or $_POST['e_name'] == "") {
echo "Geben Sie sowohl ihren Namen als auch den Empfängernamen an<br
/>";
$error = true;
}
if ($error == true) {
echo "<a href=\"javascript:history.back()\">Zur&uuml;ck</a>";

}
else { //Email versenden
//im folgenden wird die Nachricht generiert.
$nachricht = "Hallo ".$_POST['e_name'].", \n";
$nachricht .= $_POST['s_name'] ." hat gerade die Seite deineseite.de besucht
und meint, dass eventuell auch ".$ref." für dich interessant sein
könnte. \n";
$nachricht .= "Er hinterließ dir folgende Nachricht: \n";
$nachricht .= $_POST['nachricht'];
$nachricht .= "\nEs würde uns freuen wenn du mal vorbei schaust!\n";
$nachricht = strip_tags($nachricht); //nur zur Sicherheit!

if (!mail($e_email,"Surftipp von ".$_POST['s_name'],$nachricht, "From:
".$s_email)) {
echo "Mail konnte nicht versand werden!";
}
else {
echo "Die Email wurde erfolgreich versendet!";
echo "<a href=\"javascript:window.close()\">Fenster schließen</a>";

}
}
}
else {
?>
<p>Hier können Sie eine kurze Nachrichte an
Freunde, Bekannte oder Kollegen senden und den eben gelesenen Artikel weiterempfehlen,
die entsprechende URL wird automatisch übermittelt. Die Eingabe von Emailadressen
und Namen ist notwendig, sonst kann es nicht funktionieren. Adressen werden von
uns nicht weitergegeben.</p>
<form method="post"
action="empfehlen.php">
Ihr Name: <input type="text" name="s_name" /> <br
/>
Ihre Emailadresse: <input type="text" name="s_email" />
<br />
Name ihres Freundes: <input type="text" name="e_name" />
<br />
Email ihres Freundes: <input type="text" name="e_email"
/> <br />
<textarea name="nachricht" cols="34" rows="4">Hey
ich habe eine interessante Webseite unter <?php echo $ref ?> gefunden! Schau
sie dir mal an!</textarea> <br />
<input type="submit" name="submit" />
</form>
<?php
}
?>
</div>
</body>
</html> 

Der Aufbau des Skriptes ist relativ simpel. Zunächst wird durch isset($_POST[’submit‘]) überprüft, ob das Formular abgesendet wurde. Ist dies der Fall, wird geprüft, ob alle notwendigen Angaben in dem Formular gemacht wurden. Bei der Kontrolle der Emailadressen wird auf reguläre Ausdrücke zurückgegriffen. Sofern keine Fehler bei der Eingabe der Daten entdeckt wurden, wird die Email versendet.

Zuvor wird die Nachricht an den Empfänger noch etwas modifiziert. Sicherheitshalber werden über strip_tags($nachricht); sämtliche HTML Tags aus der Nachricht entfernt. Über ein IMG-Tag könnten nämlich Viren an den Empfänger verschickt werden. Würde der Benutzer die Email dann öffnen und die Quelle auf die der IMG-Tag verweist geladen, könnte sich der Benutzer infizieren.

Nun so ist das Skript noch wenig spektakulär. Man kann dem Empfänger der Email jedoch weitere Informationen über die Seite übermitteln, die er besuchen soll. Es wäre doch praktisch wenn der Empfänger den Titel der Seite zu lesen bekäme, oder vielleicht eine kurze Beschreibung. Auch das ist ohne Probleme mit ein wenig Javascript möglich.

Übergabe des Titels
Den Titel können sie mit der JavaScript-Eigenschaft „document.title“ ebenfalls als Parameter übergeben. Der Link zu der Seite würde dann wie folgt aussehen:
<a href=“javascript:window.location.href=’empfehlen.php?
ref=’+location.href+’&titel=’+document.title“>Diese Seite weiterempfehlen?</a>

Übergabe der Meta-Description
Über das DOM kann auf die Meta-Tags zugegriffen werden; so kann über

document.getElementsByTagName
("meta").description.content 

auf den Inhalt der Meta-Description zugegriffen werden. Diese kann dann ebenfalls über den URL an das Empfehlungsskript übermittelt werden.

<a href="javascript:window.location.href='empfehlen.php?
ref='+location.href+'&titel='+document.title+'&desc
='+document.getElementsByTagName
('meta').description.content"> Diese Seite weiterempfehlen?</a>

Wem das zu umständlich erscheint, der kann mit PHP vorgehen. Es gibt die Funktion get_meta_tags(), über die sämtliche Metatags einer Datei eingelesen werden und in ein assoziatives Array überführt werden. Dabei parst er PHP Interpreter die Datei nur bis zum letzten MetaTag-Eintrag. Es wird also nicht die vollständige Datei geladen.

Da über den ref-Parameter in unserem Skript die URL der zu empfehlenden Seite übertragen wird, können anhand dieser die MetaTags aus der Datei eingelesen werden. Das funktioniert auch bei dynamischen Webseiten.

Kategorien
Webdesign

Die Shoutbox als Website Feature

Man sieht sie so mancherorts auf Webseiten – kleine Pinnwände oder neudeutsch: Shoutboxes. Über sie kann der Besucher ähnlich wie in einem Chat eine kleine Nachricht hinterlassen, die dann für alle Besucher sichtbar ist.

Angefangen hat die Mode, wie so vieles, in den USA. Kleine Bereiche, über die ihre Besucher Nachrichten auf der Homepage hinterlassen können. Auf www.shoutboxes.de und anderen Websites lässt sich kostenlos eine solche Shoutbox für die eigenen Seiten mieten – doch, das Ganze ist auch im Eigenbau möglich – mit PHP und MySQL.

Screenshot
Kostenlose Shoutbox von Shouti.de

Unsere Shoutbox soll im Großen und Ganzen die gleichen Features haben, wie das Beispiel von Shouti.de. Die Daten werden in einer MySQL-Datenbank hinterlegt.

Diese Datenbank hat folgende Struktur:

 CREATE TABLE `shoutbox` (
`id` SMALLINT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR( 60 ) NOT NULL ,
`nachricht` VARCHAR( 120 ) NOT NULL ,
`email` VARCHAR( 80 ) NOT NULL ,
`date` VARCHAR( 30 ) NOT NULL ,
`ip` VARCHAR( 15 ) NOT NULL ,
PRIMARY KEY ( `id` ) 
);

Das Basisskript sieht wie folgt aus:

      <?php 
// Daten für die Datenbankverbindung 
$db_host = "localhost"; // Host 
$db_user = "tfetzer"; // User 
$db_password = "tfetzer"; // Passwort
$db_name = "ju"; // Datenbank 
//Aufbau der Datenbankverbindung
mysql_connect($db_host,$db_user,$db_password) or die(mysql_error()); 
mysql_select_db($db_name) or die(mysql_error());
      if(isset($_POST['submit']))
{ 
if(!$_POST['name']) { 
echo "Fehler: Bitte geben Sie ihren Namen ein"; 
die; 
} 
if(!ereg("^.+@.+\..+$", $_POST['email'])) { 
echo "Fehler: Bitte geben Sie eine gültige Emailadresse ein"; 
die; 
} 
if(!$_POST['nachricht']) { 
echo 'Fehler: Bitte geben Sie eine Nachricht ein.'; 
die; 
} 
       $nachricht = strip_tags($_POST['nachricht'], ''); 
$email = strip_tags($_POST['email'], ''); 
$name = strip_tags($_POST['name'], ''); 
       if(strlen($nachricht)
> 120) { 
echo "Ihre Nachricht darf nicht größer als 120 Zeichen sein!";

die; 
} 
if(strlen($name) > 60) { 
echo "Ihr Name ist zu lang!"; 
die; 
} 
       $date = date("d.m.y H:i"); 
      
$query = "INSERT INTO shoutbox (name, nachricht, email, date, ip) 
VALUES ('$name','$nachricht','$email',
'$date','$_SERVER[REMOTE_ADDR]')"; 
       if (!mysql_query($query))
{ echo "Fehler"; } 
       mysql_close(); 
      
echo "Danke für den Beitrag<BR>"; 
echo "<a href=\"shoutbox.php\">Beitrag anschauen</a>";
      } else { 

$query = "SELECT nachricht, name, email, date, ip FROM shoutbox order by
id DESC LIMIT 10"; 
$erg = mysql_query($query); 
echo "<table>"; 
while($erg2=mysql_fetch_array($erg)) 
{ 
       echo "<tr> 
<td>$erg2[date] von <a href=\"mailto:$erg2[email]\"> 
$erg2[name]</a></td> 
</tr> 
<tr> 
<td>$erg2[nachricht]</td> 
</tr> 
<tr> 
<td><hr></td> 
</tr>"; 

} 
echo "</table>"; 
      ?> 
<form method="post" action="shoutbox.php"> 
<table> 
<tr> 
<td>Name :</td> 
<td><input type="text" name="name"></td>

</tr> 
<tr> 
<td>Email :</td> 
<td><input type="text" name="email"></td>

</tr> 
<tr> 
<td>Nachricht :</td> 
<td><textarea name="nachricht"></textarea></td>

</tr> 
<tr> 
<td>&nbsp;</td> 
<td><input type="submit" name="submit" value="Senden"></td>

</tr> 
</table> 
</form> 
<?php 
}
//mysql_close(); 
?>

Das Skript erkennt ob das Skript abgesendet wurde anhand des $_POST[’submit‘] Buttons. Daraufhin werden zunächst alle HTML Tags aus den Eingaben über den strip_tags() Befehl entfernt – dann wird überprüft ob die Nachricht nicht die vorgegebene Länge von 120 Zeichen überschreitet, oder ob die Emailadresse und der Name eingegeben wurden.

Bei der Überprüfung der Emailadresse wird auf Reguläre Ausdrücke zurückgegriffen. Sind die Eingaben des Benutzers in Ordnung, werden diese in die Datenbank geschrieben. Daraufhin kann der Besucher zur Shoutbox zurückkehren und seine Nachricht sehen.

Über die MySQL Abfrage „SELECT nachricht, name, email, date, ip FROM shoutbox order by id DESC LIMIT 10“ werden die zehn neuesten Nachrichten ausgegeben.

Weitere Features…
Eine alternative Lösung statt des Links zurück zur Shoutbox wäre die automatische Weiterleitung des Besuchers durch die header-Funktion.

Anstatt:

      echo "Danke für den Beitrag<BR>"; 
echo "<a href=\"shoutbox.php\">Beitrag anschauen</a>";

würde nur

      @header("location:shoutbox.php");

dastehen. Der Benutzer würde dann direkt seine Nachricht in der Shoutbox sehen. Wer es raffiniert machen will, kann über den header-Befehl noch einen Parameter in der URL übergeben, der dem Skript dann mitteilt, dass soeben eine Nachricht geschrieben wurde. Mit einem dezenten Hinweis, könnten Sie so dem Autor der Nachricht noch einen kleinen Hinweis geben.

Möchten Sie ihren Besuchern auch anbieten Smileys in die Nachrichten mit aufzunehmen? Dann müssen wir noch ein wenig mit JavaScript arbeiten. Das Formular müsste dann wie folgt erweitert werden:

      <script language="Javascript">
function SetSmiley(emo) {
document.shoutbox.nachricht.value+=''+emo;
document.shoutbox.nachricht.focus();
}
</script>
<form method="post" name="shoutbox" action="test.php">

<table> 
<tr> 
<td>Name :</td> 
<td><input type="text" name="name"></td>

</tr> 
<tr> 
<td>Email :</td> 
<td><input type="text" name="email"></td>

</tr> 
<tr> 
<td>Nachricht :</td> 
<td><textarea name="nachricht"></textarea></td>

</tr> 
<tr> 
<td>Smileys:</td> 
<td>
<a HREF="javascript:SetSmiley(' :-)')"><img SRC="smile.gif"
alt="" border="0" align="absmiddle"></a>
<a href="javascript:SetSmiley(' 8-)')"><img src="cool.gif"
alt="" border='0' align="absmiddle"></a> <a href="javascript:SetSmiley('
:-0')"><img src="asthonished.gif" alt="" border="0"
align="absmiddle"></a><a href= "javascript:SetSmiley('
:angry:')"><img src="angry.gif" alt="" BORDER="0"
align="absmiddle"></a>
</td> 
</tr>
<tr> 
<td>&nbsp;</td> 
<td><input type="submit" name="submit" value="Senden"></td>

</tr> 
</table> 
</form>

Dabei wird über <a href= „javascript:SetSmiley(‚ :-)‘)“> die Funktion setSmiley aufgerufen und die jeweilige Abkürzung für den Smiley dem Text in dem Textfeld hinzugefügt.

Nun sollte, um das ganze ein wenig zu vereinfachen, die Abkürzung für den jeweiligen Smiley noch bevor die Nachricht in die Datenbank geschrieben wird mit dem entsprechenden Verweis auf die Bilddatei über den <img> Tag ersetzt werden.

Dazu wird vor dem Speichern der Nachricht diese auf Abkürzungen untersucht und mit der str_replace Funktion werden diese ersetzt.

Das könnte so aussehen:

      $nachricht=str_replace(":-)", "<img src=smile.gif>",
$nachricht);
$nachricht=str_replace("8-)", "<img src=cool.gif>",
$nachricht);
$nachricht=str_replace(":angry:", "<img src=angry.gif>",
$nachricht);
$nachricht=str_replace(":-0", "<img src=asthonished.gif>",
$nachricht);

Für Interessierte gibt es das Skript samt Installationsanleitung zum Herunterladen. Am Besten ist es übrigens die Shoutbox über ein iFrame einzubinden. Wird dann eine neue Nachricht eingegeben, wird nur das iFrame selbst neu geladen, nicht die gesamte Seite.

Kategorien
Webdesign

Wer da? – Besucher online ermitteln

Ungebrochen populär sind Scripts die über die derzeitige Auslastung einer Website Auskunft geben. S ie zeigen an, wie viele Besucher sich gerade auf einer Webseite aufhalten. Doch wie funktioniert das, welche Möglichkeiten gibt es?

Die einfachste Lösung funktioniert über eine Textdatei auf dem Server. In dieser wird die IP des Besuchers sowie ein Zeitstempel hinterlegt wird. Mit jedem Aufruf des Skriptes wird untersucht, ob die IP des Besuchers bereits in der Textdatei eingetragen ist und ob der Eintrag veraltet ist. Ist der Besucher neu auf der Seite, wird er neu in die Datei eingetragen – ist er bereits eingetragen gewesen, wird der Zeitstempel bei seinem Eintrag in der Datei aktualisiert.

Man kann also niemals wirklich genau sagen, wie viele Benutzer Online sind – man kann jedoch feststellen, dass in einem bestimmten Zeitraum so und soviel Benutzer Online waren – bis die Einträge eben veraltet sind und gelöscht werden.

Hier sehen Sie ein kleines PHP-Skript, über welches genau diese Funktion ermöglicht wird. Es gibt jedoch mitunter Probleme, wenn PHP im Safemode läuft. Es kann dann zu Konflikten kommen, wenn die Datei zum Beispiel gleichzeitig vom Skript bearbeitet wird. Das Skript erzeugt die Datendatei besucher.txt automatisch beim Aufruf.

<?php
$daten = "besucher.txt";
$time = time();
$ip = $_SERVER['REMOTE_ADR'];
$ablaufzeit = $time - "300"; //Ablaufzeit - die Bedingung, die für
veraltete Einträge gelten muss

$pruefung = @file($daten); //Überprüfung der Zeit
while (list ($line_num, $line) = @each ($pruefung)) {
$zeiten = explode("&&",$line);
if($zeiten[0] <= $ablaufzeit) {
$fp = fopen( "$daten", "r" );
$contents = fread($fp, filesize($daten));
fclose($fp);
$line=quotemeta($line);
$string2 = "";
$replace = ereg_replace($line, $string2, $contents);
$fh=fopen($daten, "w");
@flock($fp,2);
fputs($fh, $replace);
@flock($fp,3);
fclose($fh);
}
}
$ippruefung = @file($daten); //Überprüfung der IPs
while (list ($line_num, $line) = @each ($ippruefung)) {
$ips = explode("&&",$line); 

if($ips[1] == $ip) {
$fp = fopen( "$daten", "r" );
$contents = fread($fp, filesize($daten));
fclose($fp);
$line=quotemeta($line);
$string2 = "";
$replace = ereg_replace($line, $string2, $contents);
$fh=fopen($daten, "w");
@flock($fp,2);
fputs($fh, $replace);
@flock($fp,3);
fclose($fh);
}
}
$fp = fopen("$daten", "a+"); 

flock($fp,2);
fputs ($fp, "$time&&$ip&&\n"); //neuer Eintrag
flock($fp,3);
fclose ($fp);
$anzahldaten = file($daten);
$anzahl = count($anzahldaten);
echo "User online $anzahl";
?>

Die sicherste Lösung erhält man über Verwendung einer MySQL-Tabelle. Dort werden jeweils die Daten der Benutzer hinterlegt, die sich gerade auf der Seite befinden. Dazu muss nur eine einfache Tabelle angelegt werden – user_online zum Beispiel.

In ihr wird die IP und ein Zeitstempel hinterlegt – die Tabelle sieht wie folgt aus:

Screenshot

Eine solche Tabelle können Sie über das SQL Statement

CREATE
TABLE `useronline` (
`ip` VARCHAR( 15 ) NOT NULL ,
`zeit` INT NOT NULL
);

erzeugen.

Bei diesem Skript wird die ganze Arbeit eigentlich über einfache SQL-Statements erledigt. Zunächst werden veraltete Besucher, die innerhalb der definierten Zeitspanne keine Anfrage an den Server gesendet haben aus der Datenbank gelöscht. Dies geschieht über die DELETE-Anweisung. Über den UPDATE-Befehl wird der in der Datenbank hinterlegte Timestamp für einen Benutzer der innerhalb der Zeitspanne eine andere Seite besucht hat erneuert. Sollte der Besucher neu auf der Seite sein, wird über die INSERT-Anweisung ein neuer Datenbankeintrag erzeugt.

Über die SELECT-Anweisung schließlich wird die Anzahl der Einträge in der Datenbank ermittelt und dann ausgegeben.

<?php

$dbhost="host";
$dbuser="benutzername ";
$dbpass="passwort";
$dbname="datenbank name"; 

mysql_connect($dbhost,$dbuser,$dbpass);
mysql_select_db($dbname);

$zeitspanne = 300; //Sekunden
$ip = $_SERVER['REMOTE_ADDR'];

//veraltete Einträge löschen
mysql_query("DELETE FROM useronline WHERE zeit < ".time()."");

//Zeitpunkt erneuern
mysql_query("UPDATE useronline SET zeit = '".(time()+$zeitspanne)."'
WHERE ip='".$ip."'"); 

// ist der Besucher noch nicht eigetragen, so wird ein neuer Eintrag erzeugt.

if(!mysql_affected_rows()) {
mysql_query("INSERT INTO useronline (ip,zeit) VALUES ('$ip ','".(time()+$zeitspanne)."')");

}
// die Zahl der Online-User ermitteln.
$result = mysql_query("SELECT count(*) FROM useronline");
echo mysql_result($result,0)." User online";
?>

Kategorien
Webdesign

Auf dem richtigen Pfad: XPath für XSLT

XPath als W3C Standard ist ein Teil der XML-Zukunft. Mit XPath können alle Elemente eines XML Dokumentes adressiert werden und auf diese Weise über ein XSL-Stylesheet bestimmte Teile eines XML-Dokumentes ausgegeben werden.

XSL ist der Überbegriff für ein komplexes, von XML abgeleitetes System. Es besteht im wesentlichen aus drei Teilen:

  1. XSLT für die Transformation von XML Dokumenten
  2. XPath für die Definition von Teilen eines XML Dokuments
  3. XSLT-FO für das formatieren von XML Dokumenten

In diesem Bereich wollen wir uns insbesondere mit XPath und seiner Funktionalität beschäftigen. Als W3C Standard ist XPath eines der wichtigsten Elemente von XSLT; anders wie XSL ist es nicht in XML geschrieben. XPath verfügt über eine Bibliothek an Funktionen, über die die einzelnen Bereiche eines XML Dokumentes definiert werden.

XPath verwendet für die Definition einzelner Knoten (Elemente) eines Dokumentes Pfadausdrücke, wie wir sie aus dem Dateisystem erkennen. Das folgende Beispiel soll dies verdeutlichen:

artikel.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<artikel>
<titel>Der Titel</titel>
<teaser>Der Teaser, eine kurze Beschreibung</teaser>
<inhalt>Der Artikelinhalt an sich</inhalt>
<preis>20</preis>
</artikel>

Mit folgenden XPath Ausdruck wird auf das Wurzelelement, den Wurzelknoten <artikel> zugegriffen.

/artikel

Wohingegen der folgende Ausdruck auf das <titel> Element zugreift.

/artikel/titel

In einem XSL Stylesheet möchte man nur den Inhalt des Titel-Elementes ausgeben. Das hierzu notwendige Stylesheet sieht dann so aus:

artikel.xsl

<?xml
version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/artikel">
<b><xsl:value-of select="artikel/titel"/></b>
</xsl:template>
</xsl:stylesheet>

In das XML-Dokument muss nur noch das Stylesheet eingebunden werden. Dies geschieht über die XSL Anweisung:

<?xml-stylesheet
type="text/xsl" href="artikel.xsl"?>

Das Ergebnis im Browser sieht so aus, dass nur das Titel-Element fett ausgegeben wird.

Das XSL Stylesheet kann man bei der Pfadanweisung noch vereinfachen, indem bei dem

      <xsl:value-of select="artikel/titel"/> 

Element einfach nur

  <xsl:value-of select="/titel"/> 

geschrieben wird. Dies ist möglich, da auf den Wurzelknoten “artikel” ja bereits über

  <xsl:template match="/artikel">

zugegriffen wurde.

XPath ist ein relativ mächtiges Sprachkonstrukt für XSL, da es über eine eingebaute Funktionsbibliothek verfügt. Erweitert man das obere XML-Dokument und macht daraus eine Datenbank mit Attributen kann man XPath und seine Funktionen einsetzen.

artikel.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<content>
<artikel>
<titel autor="sl">>Der Titel</titel>
<teaserDer Teaser, eine kurze Beschreibung</teaser>
<inhalt>Der Artikelinhalt an sich</inhalt>
<preis>20</preis>
</artikel>
<artikel id="2">
<titel autor="tf"> Der 2. Titel</titel>
<teaser> Der 2. Teaser, eine kurze Beschreibung</teaser>
<inhalt>Der 2. Artikelinhalt an sich</inhalt>
<preis>15</preis>
</artikel>
</content>

Möchte man über XSL lediglich das letzte Element des XML Dokumentes auslesen, so ist auch das möglich:

/content/artikel[last()]

Die Funktion last() ist eine in der XPath Programmbibliothek mitgelieferte Funktion. Das XSL-Stylesheet würde dann so aussehen:

artikel.xsl

<?xml
version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/content/artikel[last()]">
<b><xsl:value-of select="titel"/></b>
</xsl:template>
</xsl:stylesheet>

Hierbei wird nur der Titel des letzten Artikel Elementes im XML Dokument fett ausgegeben. Die Liste aller in XPath integrierten Funktionen sind beim W3C einsehbar. Es gibt noch viel mehr Funktionen, mit denen insbesondere logische Operationen durchgeführt werden können. Um XPath effektiv einsetzen zu können, muss man XSL beherrschen.

Möchte man nur auf das erste Element vom Typ <artikel> zugreifen, ist das auf diese Weise machbar:

/content/artikel[1]

Anhand des Indexes kann auf alle Elemente einzeln zugegriffen werden.

Man kann die Pfade auch mit // starten lassen. Hierbei werden auf alle Elemente des XML-Dokumentes, welche das Kriterium erfüllen, zugegriffen – selbst wenn sie sich in unterschiedlichen Elementverschachtlungen befinden.

Über

/content/artikel[preis]

wird nur auf die <artikel> Elemente zugegriffen, die ein Preis-Element als Kind haben. Da dies in unserem Beispiel bei beiden der Fall ist, wird auch auf beide zugegriffen. Das Ergebnis könnte über ein xsl-for each Konstrukt ausgegeben werden.

Es können auch logische Operationen durchgeführt werden.

Operator Beschreibung
= Gleich
!= Ungleich
< Kleiner
<= Kleiner gleich
> Größer
>= Größer gleich
Boolsche Operatoren
or Oder
and Und

So könnte man mit folgendem Pfad auf alle Artikel mit einem Preis kleiner als 20 zugreifen.

      /content/artikel[preis<20]

Wie in einem Dateisystem können über XPath auch unbekannte XML Elemente gesucht werden. Dies erfolgt über sogenannte Wildcards. Folgende Pfadanweisung greift auf alle <artikel> Elemente zu:

      /content/artikel/*

Anders sieht es bei diesem Konstrukt aus:

      /*/*/preis

Hierbei werden auf alle Preis-Elemente in der dritten Ebene zugegriffen. Das heißt, dass in unserem Dokument <artikel.xml> auf alle Preiselemente zugegriffen wird.

Über //* wird auf alle Elemente des XML Dokuments zugegriffen.

Es können natürlich auch auf einzelne Attribute zugegriffen werden; alle Attribute werden in XPath über @ angesprochen. Der folgende Pfad greift nur auf die <artikel> Elemente zu, die sowohl ein <preis> Element haben, also auch ein id-Attribut.

      /content/artikel[preis and @id] 

In diesem Fall ist das der zweite Artikel; der erste verfügt über kein id-Attribut. Auch hierbei könne Wildcards verwendet werden. So ergibt der Pfad in unserem Beispiel das gleiche Ergebnis, jedoch wird hierbei auf alle <artikel> Elemente zugegriffen, die über irgendein Attribut verfügen:

      /content/artikel[preis
and @*]

Möchte man nur auf die Artikel, bei denen der Autor „sl“ (Attribut des titel-Elementes) ist, so sieht das XSL Stylesheet dann so aus:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/content/artikel/titel[@autor='sl']">
<b><xsl:value-of select="//titel"/></b><br/>
<b><xsl:value-of select="//teaser"/></b><br/>
</xsl:template>
</xsl:stylesheet>

Wichtig ist, dass bei der Ausgabe über das XSL Konstrukts

<xsl:value-of select="//titel"/>

der Pfad des auszugebenden Elementes mit // angegeben wird. Dadurch werden alle Elemente, die das im Pfad definierte Kriterium erfüllen ausgeben. Es ist also kein absoluter Pfad.

XPath liefert noch viele weitere interessante Funktionen und Anwendungsmöglichkeiten, die XSL noch deutlich dynamischer lassen werden.

Kategorien
Webdesign

Javascript: Pfad anzeigen…

Über ein einfaches JavaScript kann man die Navigation durch eine Webseite ein wenig benutzerfreundlicher gestalten. Dabei wird der URL zu der jeweiligen Seite untersucht und die einzelnen Unterordner ermittelt und verlinkt. Eine so genannte Brotkrumennavigation entsteht.

Nach diesem Prinzip wird über ein wenig Javascript die Struktur der Webseite angezeigt. Liest man bei Dr.Web beispielsweise den Artikel unter http://www.drweb.de/html/html_kurs.shtml, würde der Pfad, welcher über das Javascript angezeigt wird wie folgt aussehen:

Startseite / html / html_kurs.shtml

Dabei sind die einzelnen Ordner beziehungsweise Dateien jeweils verlinkt, so dass der Benutzer per Klick den vorhergegangenen Index-Ordner erreichen kann. Das Skript muss dazu nur leicht modifiziert werden. Lediglich die Variable „domain“ muss verändert werden, und schon ist die Seite etwas benutzerfreundlicher. Die Anwendung empfiehlt sich jedoch nur, wenn die Ordnerstruktur auch der Menüführung entspricht.

Das einzubindende Skript sieht wie folgt aus:

  <script type="text/javaScript">
<!-- 
var path = "";
var domain = "www.drweb.de";
var href = document.location.href;
var s = href.split("/");
for (var i=2;i<(s.length-1);i++) {

if (s[i] == domain) {
path+="<A HREF=\""+href.substring(0,href.indexOf("/"+s[i])+s[i].
length+1)+"/\">Startseite </a>/ ";
}

if(s[i] == domain.substring(4,domain.length)) {
path+="<A HREF=\""+href.substring(0,href.indexOf("/"+s[i])+s[i].
length+1)+"/\">Startseite </a>/ ";
}
else {
path+="<A HREF=\""+href.substring(0,href.indexOf("/"+s[i])+s[i].
length+1)+"/\">"+s[i]+"</A> / ";
}
}

i=s.length-1;
path+="<A HREF=\""+href.substring
(0,href.indexOf(s[i])+s[i]. length)+"\">"+s[i]+"</A>";
document.writeln(path);
//-->
</script>

Das Skript funktioniert recht simpel – es wird einfach über

      document.location.href;

der URL zu der aktuell aufgerufenen Seite ermittelt. Dieser wird über die split-Funktion nach den Slashes geteilt – das Ergebnis ist ein nummeriertes Array.

In der folgenden Schleife wird das Array ausgelesen – dabei wird jeweils überprüft ob der Wert zu dem dazugehörigen Index gleich der Domain ist. Dabei ist egal ob diese ein www aufweist oder nicht. Ist dies der Fall, wird anstatt der Domain als Link, „Startseite“ angezeigt.

Kategorien
Programmierung Sonstige Programmiersprachen

PHP und XML: Einfach einfach!

Die neue PHP-Version 5 enthält neben zahlreichen anderen Neuerungen auch die Erweiterung SimpleXML. Mit ihr kann XML auf erstaunlich einfache Art in ein PHP Objekt überführt und verarbeitet werden.

Release Candidate 3 von PHP5

artikel.xml:

 <?xml version="1.0" encoding="ISO-8859-1" ?>
  <artikel>
     <titel>Der Titel</titel>
     <teaser>Der Teaser, eine kurze Beschreibung</teaser>
     <inhalt>Der Artikelinhalt an sich</inhalt>
  </artikel>

simplexml_1.php:

      <?php

if (file_exists('artikel.xml')) {
   $xml = simplexml_load_file('artikel.xml');

echo $xml->teaser[0];

} else {
   exit('Konnte Datei nicht laden.');
}
?>

direkt in ein Objekt überführt

Dabei erfolgt der Zugriff auf die Elemente jeweils über Arrays. Da in unserem Fall nur ein Element innerhalb des Wurzelelementes <artikel> als <teaser> Element verwendet wird, geschieht der Zugriff auf den Inhalt dieses Elementes mit dem Arrayindex 0. Wären zwei <teaser> Elemente verwendet, würde man über den Index 1 auf das zweite Element zugreifen.

Um zu verstehen, wie bei SimpleXML die Daten in Objekten gespeichert werden, kann man sich mit der var_dump() Funktion von PHP schlicht den gesamten Inhalt des Objektes ausgeben lassen. Man ändert den Code einfach wie folgt ab:

simplexml_1.php:

      <?php

if (file_exists("artikel.xml")) {
   $xml = simplexml_load_file("artikel.xml"');

var_dump($xml);
} 
else {
   exit("Konnte Datei nicht laden. ");
}
?>

Die Ausgabe nach Aufruf des Skriptes sieht wie folgt aus:

      object(SimpleXMLElement)#1 (3) 
{ 
["titel"]=> string(9) "Der Titel" 
["teaser"]=> string(35) "Der Teaser, eine kurze Beschreibung" 
["inhalt"]=> string(25) "Der Artikelinhalt an sich" 
}

Wenn man bedenkt, dass zum Parsen eines XML Dokumentes mit dem ereignisorientierten Parser Expat mindestens drei Funktionen definiert werden mussten, ist der hierzu notwendige Aufwand mit SimpleXML nicht vergleichbar.

Komplexere XML Dokumente und SimpleXML?
Auch komplexe XML Dokumente lassen sich mit SimpleXML rasch bearbeiten. Folgendes XML Dokument ist fast identisch mit dem Beispiel oben, es wurde jedoch mit Attributen und weiteren Elementen versehen.

artikel_2.xml:

      <?xml version="1.0" encoding="ISO-8859-1" ?> 
 <content>
 <artikel id="1">
 <meta>
  <keywords>ein,netter,artikel</keywords> 
  <description>ein,lustiger,artikel</description> 
  </meta>
  <titel>Der Titel</titel> 
  <teaser>Der Teaser, eine kurze Beschreibung</teaser> 
  <inhalt>Der Artikelinhalt</inhalt> 
  </artikel>
 <artikel id="2">
 <meta>
  <keywords>ein,zweiter,netter,artikel</keywords> 
  <description>ein,zweiter,lustiger,artikel</description> 
  </meta>
  <titel>Der zweite Titel</titel> 
  <teaser>Der zweite Teaser, eine kurze Beschreibung</teaser> 
  <inhalt>Der zweite Artikelinhalt</inhalt> 
  </artikel>
  </content>

XPath Prozessor

Dies soll im folgenden Beispiel geschehen – es soll lediglich der vom Besucher angefragte Artikel angezeigt werden. Der gewünschte Artikel wird durch die ID identifiziert, die im <artikel> Element als Attribut definiert ist. Diese ID wird über den URL an das PHP Programm übergeben.

simplexml2.php

      <?php
      $id = $_GET['id'];
      if (file_exists("test.xml")) {
         $xml = simplexml_load_file("test.xml");
         $path ="/content/artikel[@id=".$id."]";
         if (!$res = $xml->xpath($path)) {
   echo "Artikel nicht vorhanden!";
   }
   else {
   echo "<h1>".$res[0]->titel."</h1>";
   echo "<p><b>".$res[0]->teaser."</b></p>";
   echo "<p>".$res[0]->inhalt."</p>";
   }
}
 else {
   exit("Konnte Datei nicht laden.");
}
?>

Artikel-Objektes

Screenshot
Das Ergebnis beim Aufruf mit id=1

Natürlich kann man ein solches Problem auch ohne XPath lösen, SimpleXML stellt die dazu notwendigen Funktionen zur Verfügung – doch bietet SimpleXML gerade durch die Schnittstelle zu XPath viele weitere Möglichkeiten für die dynamische Programmierung. SimpleXML sollte also nicht unterschätzt werden.

Weitere Informationen zu SimpleXML und PHP5.

Kategorien
Webdesign

Javascript: Dynamische Dropdown-Menüs

Mehrere Dropdown-Menüs in einem Formular? Kein Problem. Wer will, kann die Inhalte der beiden Menüs dynamisch voneinander abhängig machen.

Insbesondere in Linkverzeichnissen mit vielen Kategorien macht ein solches Javascript Sinn. Beim Eintrag eines Linkes, zum Beispiel in die Kategorie Internet, soll die Auswahl im zweiten Dropdown-Menü auf die Kategorien „Internetzugang“ und „Webseiten erstellen“ beschränkt werden.

Live-Demo:

Das folgende Formular ist in die Seite aufzunehmen:

 <form name="verzeichnis">
    <select size="1" name="kategorie" onchange="update_auswahl()">

    <option value="Email" selected>Email</option>
    <option value="Internet">Internet</option> </select>

    <select size="1" name="unterkategorie">
    <option selected>Software</option>
    <option>Anbieter</option>
    </select></form>

Die beiden Dropdown-Menüs sind bereits angelegt. Sie haben die haben die Namen „kategorie“ und „unterkategorie“. Soll sich die Auswahl im ersten DropDown-Menü ändern, wird über den onchange-Eventhandler die Javascript-Funktion „update_auswahl()“ aufgerufen.

Diese Funktion sollte im <head> Bereich der Seite stehen:

    <script language="Javascript">
    <!-- Start
    function update_auswahl()
    {
    var kategorieAuswahl = document.forms.verzeichnis.kategorie;
    var unterkategorieAuswahl = document.forms.verzeichnis.unterkategorie;
    unterkategorieAuswahl.options.length = 0; // DropDown Menü entleeren
       if (kategorieAuswahl.options
    [kategorieAuswahl.selectedIndex].
    value == "Email")
    {
    unterkategorieAuswahl.options[0] = new Option("Software");
    unterkategorieAuswahl.options[1] = new Option("Anbieter");
    }
    else if (kategorieAuswahl.options
    [kategorieAuswahl.selectedIndex].
    value == "Internet")
    {
    unterkategorieAuswahl.options[0] = new Option("Internetzugang");
    unterkategorieAuswahl.options[1] = new Option("Webseiten erstellen");
    }
    }
    // Ende -->
    </script>

Hier werden innerhalb der Funktion zunächst die Variablen kategorieAuswahl und unterkategorieAuswahl definiert und ihnen die Werte der Auswahl in dem Formular zugewiesen. Gleichzeitig wird das zweite DropDown-Menü entleert, so dass neue Optionsfelder erzeugt werden können.

Die erste if-Abfrage überprüft, ob der Wert der Auswahl des ersten DropDown-Menüs „Email“ ist. Ist dies der Fall, werden dem zweiten DropDown-Menü „unterkategorie“ die Optionen „Software“ und „Anbieter“ hinzugefügt. Ebenso wird mit der zweiten if-Abfrage verfahren. Die Prozedur kann über mehrere DropDown-Menüs erfolgen.

Kategorien
Webdesign

Suchmaschinenfreundliche URLs

Dynamische Webseiten können durch mod_rewrite auf einfache Art und Weise suchmaschinenfreundlich gemacht werden. Leider steht das mächtige Modul aus Sicherheitsgründen nicht jedem zur Verfügung. Es gibt eine andere Möglichkeit.

Dynamische Anwendungen schicken in der Regel Anfragen an die serverseitigen Programme über Parameter in dem URI. Jeder kennt Adressen wie diese:

http://www.shop-was.de/katalog.php?kategorie=buch&isbn=12021002

Diese Form von URIs werden von den Suchmaschinen „skeptisch“ betrachtet und zum Teil gar nicht indiziert. Hier soll es um eine einfache Lösung des Problems gehen. Dabei wird nicht auf das Modul mod_rewrite vom Apache Webserver zurückgegriffen, sondern auf einfache htaccess Funktionen. Man möchte die oben angegebene URL beispielsweise in die folgende umwandeln:

http://www.shop-was.de/katalog/buch/12021002

Um dies zu erreichen, muss man dem Webserver klar machen, dass die Datei katalog.php auch als php-Datei ausgeführt wird, sollte diese nicht mit der PHP-Dateiendung aufgerufen werden. Des Weiteren muss in der PHP-Anwendung eine Funktion eingefügt werden, welche die Variablen aus der Anfrage-URL extrahiert und an die Anwendung übergibt.

Ersteres Problem kann mit der forcetype-Direktive des Apache-Moduls mod_mime gelöst werden. Dieses Modul ist auf allen Apache-Servern verfügbar.

 <Files
katalog>
ForceType application/x-httpd-php
</Files>

Dieser Code muss in eine htaccess-Datei eingefügt werden, die wiederum in jedem Verzeichnis liegt, in welchem das Programm katalog.php liegt.

Das zweite Problem kann ebenfalls einfach gelöst werden. Durch eine einfache PHP-Funktion, die direkt nach dem Aufruf des Programms ausgeführt wird.

      function URL_aufsplitten() {
global $REQUEST_URI;
$erg = explode("/",$REQUEST_URI);
$num = count($erg);
$parameter = array();
for ($i = 2 ; $i < $num ; $i++) {
$parameter[$i] = $array[$i];
}
return $parameter;
}

Dabei wird zunächst auf die Variable REQUEST_URI, die eine von PHP vordefinierte Variable ist, zugegriffen. Sie enthält bei unserer Beispiel URL nun

/katalog/buch/12021002

Über diese URL müssen nun die einzelnen Parameter ermittelt werden. Dies geschieht durch das „aufsplitten“ der URL durch die Funktion explode(), welches als Trennzeichen den „/“ nimmt. Das Ergebnis landet in dem Array $erg.

Durch count() wird die Anzahl der Elemente des Arrays bestimmt. In unserem Beispiel sind es vier Elemente. Das erste Element ist ein leeres Element, der Inhalt vor dem ersten Slash.

Das zweite Element ist „katalog“, das dritte „buch“ und das vierte „12021002“, die ISBN. Für das Programm sind nur die Parameter wichtig, deshalb wird in der for-Schleife erst ab dem 2. Element gezählt (bei Arrays fängt man bei der Zahl 0 an zu zählen). Das „0“-te und das „1“-te Element werden also nicht beachtet.

Im Rahmen der Programmierung kann man die Funktion direkt zu Beginn des Codes ausführen und die Parameter über das zurückgegebene Array $parameter ermitteln.

Kategorien
Webdesign

XML mit PHP und Expat parsen

Mit der PHP-Erweiterung Expat lässt sich XML durch PHP parsen. Damit eröffnen sich neue Möglichkeiten. Denn XML ist als reine Datensprache mächtiger als einfache SQL basierende Datenbanken, die sich auf dem zweidimensionalen Rahmen beschränken. Mit XML sind vielfach ineinander verschachtelte Datensysteme möglich.

Die Erkennung einzelner Daten aus XML-Dateien, sowie deren dynamische Veränderung ist ungemein wichtig, da man ohne diese Funktionalität nicht effektiv auf XML-Datenbanken zurückgreifen kann. Speziell für Webanwendungen ist es in dieser Hinsicht schwierig mit XML umzugehen.

Eine einfache Möglichkeit XML-Daten zu handeln bietet Expat. Expat ist seit der PHP-Version 4 voll integriert und kann bei der Installation durch die Option „-with-xml“ installiert werden.

Expat ist ereignisorientiert. Das bedeutet, dass zunächst einzelne Funktionen für verschiedene Ereignisse definiert werden. Ein Ereignis kann zum Beispiel sein: ein öffnendes Element, ein schließendes Element oder der Inhalt, der von einem öffnenden und einem schließendem Element eingeklammert ist.

Für folgende Elemente können Funktionen definiert werden.

  • Startendes Element, mit Attributen (<inhalt nr=“10″>)
  • Normaler Text „Character Data“, zwischen den öffnendem und schließendem Element
  • Abschließendes Element (</inhalt>)
  • Kommentare
  • DTD Anweisungen
  • Procession Instructions – eingebettete PHP Anweisungen

Wichtig ist, die Arbeitsweise von Expat zu verstehen. Expat untersucht ein XML Dokument zeilenweise. Darum muss das Dokument mit einer Schleife Zeile für Zeile geparset werden.

Expat erkennt das öffnende Element, zum Beispiel <inhalt> und übergibt es an die definierte Funktion für öffnende Elemente. Daraufhin entdeckt er den Inhalt dazwischen. Ist dies einfacher Text, wird dieser an die Funktion überreicht, die diese Inhalte behandelt. Inhalte, die nicht weiter in andere Tags verschachtelt sind, nennt man CDATA – Character Data. Zum Abschluss wird das schließende Element gefunden und entsprechend über die definierte Funktion behandelt.

Einen XML-Parser erzeugen
Ein XML-Parser wird immer für das XML Dokument individuell erzeugt.

 $parser
= xml_parser_create();

Der Parser wird erzeugt. Die Variable $parser verweist auf den erzeugten Parser, so dass dieser einmalig gekennzeichnet ist.

      xml_set_element_handler($parser,"startElement","endElement");

Hiermit werden die Funktionen für das öffnende und das schließende Element angegeben. „startElement“ verweist auf die entsprechende Funktion startElement(), und endElement verweist auf die entsprechende Funktion „endElement“.

      xml_set_character_data_handler($parser, "characterdata");

Hiermit wird auf die Funktion für CDATA (normaler Text) verwiesen.

      xml_set_processing_instruction_handler($parser, "pi");

Hier wird auf die Funktion verwiesen, die Processing Instructions behandelt.

      xml_set_default_handler($parser, "default");

Diese Funktion wird aufgerufen, wenn für das Ereignis keine andere Funktion definiert ist. Man kann sagen, dass die default-Funktion aufgerufen wird, wenn ein Fehler aufgetreten ist.

Der erste eigene Parser
Hier nun unser erster kleiner Parser, der auf das folgende XML-Dokument inhalt.xml zugeschnitten ist.

      <?xml version="1.0"?>
<inhalt>
Dies ist ein Platzhalter-Text.
</inhalt> 

Diese XML Datei wird von dem Parser in der Datei inhalt.php geparsed.

      <?php
function startElement($parser, $element_name, $element_attribute) {
global $ausgabe;
//Umwandeln in Kleinbuchstaben
$element_name = strtolower($element_name);
//Überprüfung des Elementnames
if ($element_name=="inhalt") {
    $ausgabe .= "<h3>Inhalt</h3><p>";
}
}

function endElement($parser, $element_name) {
global $ausgabe;
// in Kleinbuchstaben umwandeln
$element_name = strtolower($element_name);
// Überprüfung des Names eines Elementes
if ($element_name=="inhalt") {
    $ausgabe .= "</p>";
}
}

function cdata($parser, $element_inhalt) {
global $ausgabe;
// Der normale Text wird an $ausgabe angehängt
$ausgabe .= $element_inhalt;
}
$xmlFile = file("inhalt.xml");
$parser = xml_parser_create();
xml_set_element_handler($parser, "startElement", "endElement");
    xml_set_character_data_handler($parser, "cdata");

foreach($xmlFile as $elem) 
{
xml_parse($parser, $elem);
}
xml_parser_free($parser);
echo $ausgabe;
?>

Die Ausgabe nach dem Aufruf des Skriptes sieht wie folgt aus:

Inhalt
Dies ist ein Platzhalter-Text

Um die Arbeitsweise des Skriptes zu verstehen, betrachten wir jede Funktion einzeln. Zunächst behandeln wir die Funktion, die aufgerufen wird, wenn Expat ein öffnendes Element findet. In unserem Beispiel ist das <inhalt>.

An die Funktion wird die Variable $parser übergeben. In ihr wird auf den erzeugten XML Parser verwiesen. Über die Variable $element_name wird der Names des Elementes übergeben. In unserem Beispiel ist der Name des Elementes „inhalt“. Zudem können Attribute übergeben werden. Wie Sie diese mit XML Parsen erfahren Sie im zweiten Teil des Workshops.

In der ersten Zeile der Funktion selbst wird auf die Variable $ausgabe verwiesen und ihr globale Eigenschaften zugewiesen. Das heißt, die Variable ist nicht nur in der Funktion vorhanden, sondern auch außerhalb. So kann man direkt auf den Wert der Variablen zugreifen, ohne diesen an die Funktion zu übergeben. Zudem kann der Wert direkt aus der Funktion heraus verändert werden.

      function startElement($parser,
$element_name, $element_attribute) {
global $ausgabe;
//Umwandeln in Kleinbuchstaben
$element_name = strtolower($element_name);
//Überprüfung des Elementnames
if ($element_name=="inhalt") {
    $ausgabe .= "<h3>Inhalt</h3><p>";
}
}

Die nächste Anweisung wandelt den Namen des Elementes in Kleinbuchstaben um, da Expat die Namen eines Elementes immer in Großbuchstaben übergibt. Dies muss nicht gemacht werden, dient aber zu einem einfacheren Verständnis des Codes.

Im nächsten Teil wird überprüft, ob der Name des Elementes „inhalt“ ist. Ist dies der Fall, wird an die Globale Variable $ausgabe der Inhalt „<h3>Inhalt</h3><p>“ über den Stringoperator „.“ angehängt.

Eigentlich ist eine Überprüfung auf den Namen des Elementes nicht notwendig, da in der XML Datei sowieso nur ein Element vorkommt. Da wir jedoch in den nächsten Teil des Workshops mit etwas komplexeren Beispielen in die Materie einsteigen, sollte auf diese zwei Zeilen Code nicht verzichtet werden.

Nun, wollen wir uns mit der Funktion für den normalen Fließtext, CDATA kümmern. Diese hat nur eine Aufgabe. Sie muss den Inhalt des Elements an die Globale Variable $ausgabe anhängen.

      function cdata($parser, $element_inhalt)
{
global $ausgabe;
// Der normale Text wird an $ausgabe angehängt
$ausgabe .= $element_inhalt;
} 

Der Inhalt des aktuell geparten Element wird über die Variable $element_inhalt übergeben. Dieser wird an die Variable $ausgabe angehängt.

Die letzte Funktion ist für die schließenden Elemente zuständig. Diese Funktion arbeitet auf der gleichen Weise, wie die Funktion für das öffnende Element.

      function endElement($parser,
$element_name) {
global $ausgabe;
// in Kleinbuchstaben umwandeln
$element_name = strtolower($element_name);
// Überprüfung des Names eines Elementes
if ($element_name=="inhalt") {
    $ausgabe .= "</p>";
}
}

Das sind die Funktionen, die in diesem Beispiel benötigt werden. Nun muss die XML-Datei eingelesen werden und daraufhin der Parser definiert, beziehungsweise erzeugt werden.

      $xmlFile = file("inhalt.xml");
$parser = xml_parser_create();
xml_set_element_handler($parser, "startElement", "endElement");
xml_set_character_data_handler($parser, "cdata");
      foreach($xmlFile
as $elem) 
{
xml_parse($parser, $elem);
}
xml_parser_free($parser);
echo $ausgabe;

In der ersten Zeile wird die Datei über die file() Funktion in ein Array gelesen. In der zweiten Zeile wird der Parser erzeugt. Darauf werden die einzelnen Handler festgelegt. Der letzte und wichtigste Teil des Skriptes sorgt dafür, dass der Inhalt des Arrays über eine foreach-Schleife einzeln über die Funktion xml_parse() geparsed wird.

In der vorletzten Zeile wird der Parser wieder „zerstört“, das heißt alle belegten Resourcen werden wieder frei gegeben. In der letzten Zeile wird nun der Inhalt der Variablen $ausgabe ausgegeben. In ihr befindet sich der Inhalt, der über die einzelnen Funktionen dynamisch aus der XML Datei extrahiert wurde.

Kategorien
Webdesign

Logfiles: Auf Spurensuche im Internet

Wer seine Logfiles genau analysiert, kommt schnell zu aussagekräftigen Ergebnissen über seine Kunden im Web. Richtig eingesetzt, wird die Logfile-Analyse zu einem wichtigen Marketingtool für Ihren Web-Erfolg, mit dem Sie Ihre Zielgruppen ermitteln und Ihr Webangebot optimieren können.

Jeder Hit wird aufgezeichnet, dabei wird nicht nur die IP des Besuchers gespeichert, auch der verwendete Browser kann ermittelt werden. Logfiles sind die Speicherplätze dieser Benutzerdaten. In ihnen wird jede Anfrage an den Server protokolliert und abgespeichert. Wer auf diese Statistiken Zugriff hat, kann sehr viel über die Entwicklung des Besucherstroms und sogar die Zielgruppe ermitteln. Logfiles sind meist im selben Verzeichnis wie der ROOT selbst. Es handelt sich um einfache Textdateien, die mit jedem Editor ausgelesen werden können.

Jeder Aufruf an den Server wird protokolliert. So kann bestimmt werden, von wo der Besucher kam, welchen Browser er verwendet und wie viele Seiten er sich angesehen hat. Jeder Aufruf steht im Logfile in einer eigenen Zeile. Ein Beispiel:

 192.168.156.36
- [20/Jan/2002:19:35:09 +0100] "GET / HTTP/1.1" 200 25641 www.devmag.net
"http://www.devmag.net/" "Mozilla/4.0 (compatible; MSIE 5.5; Windows
ME; DigExt)"

Diese Zeile beschreibt den kompletten Aufruf einer Seite. Auch wenn der „Code“ eher ungeordnet erscheint, besteht er doch aus einer festen Struktur. Teil 1 enthält die IP-Adresse des Rechners, welcher den Aufruf getätigt hat. Die IP-Adresse ist eine wandelnde Nummer. Mit jeder neuen Internetverbindung erhält der Computer vom jeweiligen Internetanbieter eine neue IP-Adresse aus einem Pool. Diese IP-Adresse ist innerhalb dieser Session einmalig und erlaubt die Kommunikation zwischen den verschiedenen Rechnern.

Auf die IP-Adresse folgt ein ein Bindestrich. Weiter geht es mit näheren Informationen zum Aufruf der Seite. Zunächst kommen Datum und Uhrzeit in eckigen []-Klammern. Die Angabe folgt dem amerikanischen Standard Tag/Monat/Jahr. Getrennt von einem Doppelpunkt ist der genaue Zeitpunkt des Aufrufs im GMT Zeitformat (Greenwich Mean Time).

In unserem Beispiel stammt der Besucher aus dem Raum, in dem die MEZ gilt, deshalb muss eine Stunde addiert werden, dies geschieht durch das +0100. Die konkrete Zeit des Aufrufs war also 20:35:09 Uhr. Zur Sommerzeit kann in unseren „Gefilden“ die Zeitverschiebung auch +0200, also zwei Stunden betragen.

Die nächste Angabe spezifiziert den Aufruf. Die Methode GET legt fest, dass die Daten vom Server an den Client gesendet werden, nach dieser Angabe folgt das Protokoll, mit welchem die Daten kodiert wurden, hier also das HTTP-Protokoll.

Es kann vorkommen, dass als Methode in den Logfiles auch ein HEAD auftaucht. Diese Methode wird vor allem von Suchmaschinen verwendet, die so nur Daten zu der angeforderten Datei erhalten. Dies können das letzte Änderungsdatum des Dokumentes sein. Mit diesem Datum wird dann abgewägt, ob die Seite neu indexiert wird. Nach der Angabe der Methode und des Protokolls folgt der Rückgabecode des Servers. Ist der Seitenaufruf geglückt, dann wird als Rückgabecode 200 zurückgegeben. Hier eine Übersicht zu weiteren Rückgabecodes:

200 OK
Der Request wurde erfolgreich durchgeführt.

204 No Content
Das angeforderte Dokument enthält keine Daten.

206 Partial Content
Die Übertragung wurde unterbrochen. Dies kann vom Browser aus geschehen – oder bei einem Update der Seite.

300 Multiple Choices
Es sind mehrere (ähnliche) Dateien vorhanden. Der Server kann die Datei nicht eindeutig ermitteln und bietet mehrere Auswahlmöglichkeiten.

301 Moved Permanently
Die Datei wurde an einen anderen Ort verschoben.

304 Not Modified
Die Datei wird komplett aus dem Cache (Server und/oder Client seitig) geladen.

400 Bad Request
Der Webserver „versteht“ Ihre Anfrage nicht.

401 Unauthorized
Sie sind nicht autorisiert, diesen Bereich zu betreten.

403 Forbidden
Der Zugriff auf die angeforderte Datei wird verweigert

404 Not Found
Die Datei wurde nicht gefunden (ist nicht vorhanden), oder der URL wurde falsch eingegeben.

500 Internal Server Error
Ein unbekannter Server Fehler ist aufgetreten. Oftmals entstehen diese durch falsche Anwendung von .htaccess Dateien oder durch Fehler im CGI.

503 Service Unavailable
Der Server kann die Anfrage zeitweilig nicht bearbeiten, z.B. bei Wartungsarbeiten.

Auf den Rückgabecode folgt eine Zahl, sie gibt die genau übertragene Datenmenge in Bytes an. Diese Zahl entspricht also der Dateigröße. Danach folgt der URL zu dem Dokument, welches aufgerufen wurde. Der URL weißt auf den Root, es war also ein direkter Request. Auf diese URL folgt die URL der Seite, auf der sich der Besucher zuletzt befand. Bei einer direkten Anfrage entfällt diese Angabe. Bei einer indirekten Anfrage kommt man z.B. über einen Link einer anderen Seite zu der Seite, hier steht dann der URL der Seite, von welcher man auf die andere Seite gekommen ist. Diese Seite bezeichnet man als Referer-Seite.

Die folgenden Angaben geben nähere Informationen zu dem Client bzw. zu dem System, von welchem der Aufruf getätigt wurde. Die Angaben erstrecken sich von dem verwendeten Browser bis zu dem Betriebssystem. In dem Beispiel oben verwendet der Besucher, leicht zu erkennen, den Internet Explorer in der Version 5.5. Zudem arbeitet er mit Windows ME als Betriebssystem. Kommt der Request von einem Spider, oder von einem Robot, stünde hier der Name des jeweiligen Spider oder Robots.

Da jeder Hit nach diesem oder einem ähnlichen Muster aufgebaut ist, wird auch die Analyse fast zu einem Kinderspiel. Es gibt Analyse-Programme bzw. Skripte, die jeden Hit auslesen und in seine Bestandteile auseinandernehmen, um ihn dann in einer hübschen, übersichtlichen Statistik wieder zusammen zu setzen. Komplexere Statistiksysteme ermitteln zudem oftmals mit JavaScript von dem Besucher weitere Daten, wie beispielsweise die Bildschirmauflösung oder ähnlich. Somit lässt sich leicht ermitteln, für welche Besuchergruppe eine Seite optimiert werden sollte.

Kategorien
Webdesign

Website-Caching

Nicht nur Webseiten mit langer Ladezeit haben Probleme, ihr Traffic Limit einzuhalten. Damit es nicht zu sehr ins Geld geht, hilft eine geschickte Manipulation der Datei .htaccess auf Apache Webservern.

Im Bereich Ladezeit bietet der Apache Webserver klare Vorteile. Mit ihm kann man die Dauer für das Zwischenspeichern eines Dokuments genau festlegen, was zu einem guten Zusammenspiel zwischen dem Besucher und den gespeicherten Daten führt.

Bei dem Besuch eines Benutzers auf einer Seite, nehmen wir an, dort ist ein Artikel vorhanden, wird dieser auf dem Rechner des Benutzers zwischengespeichert. Ähnlich wie bei einem Cookie ist auch hier ein Verfallsdatum festgelegt. Besucht der Benutzer genau diese Seite innerhalb eines festgelegten Zeitraums ein zweites Mal, so wird die Seite aus dem Cache geladen, was die Ladezeit deutlich verkürzt. Ist dieser Zeitraum abgelaufen, müssen die Daten erneut angefordert werden.

Das Gute am Apache Server ist, dass der Benutzer einzelne MIME-Typen definieren kann. So ist beispielsweise eine Definition möglich, nach der alle Dateien vom Typ image/gif erst nach 30 Tagen wieder angefordert müssen. Ein klarer Vorteil.

Benötigt wird hierfür eine .htaccess Datei, die dem Server diese Vorgaben mitteilt. Wichtige Voraussetzung: Auf dem Server muss das Modul „mod_expires“ installiert bzw. einkompiliert sein, was aber bei den meisten Servern der Fall ist.

Zum Einsatz kommen drei Befehlsformen für den Apache Server. Diese sind in einer .htaccess – Datei auf dem Server zu hinterlegen. Die erste Form lautet:

 ExpiresActive On
 ExpiresDefault
"access plus 5 days" 

Die erste Zeile aktiviert die Cache-Kontrolle des Servers. Die folgende Zeile gibt einen Default-Wert an. Die Seite soll genau fünf Tage nach dem Besuch im Cache verbleiben. Diese Definition wäre mit der Eingabe dieses Befehls für alle Dokumente auf dem Server oder in dem Verzeichnis gültig. Diese Lösung ist natürlich zu allgemein, da eine html-Seite wahrscheinlich öfters verändert wird als eine Grafik-Datei. Hier greift die zweite Variante:

      ExpiresByType image/gif A864000

Wie sich vermuten lässt wird hier, wie oben schon angesprochen der einzelne Dateityp angesprochen. image/gif steht hier für alle *.gif Dateien. Hinter dem MIME-Typ steht die Zeitangabe in Sekunden, nach der die Datei erneut vom Server geladen werden soll. Das A ist fest. Es steht für „Anforderung“ (engl. access). In diesem Fall werden die gif-Dateien nach 10 Tagen wieder neu vom Server geladen.

Weitere MIME-Typen

      image/jpeg *.jpg Grafiken 
 image/png
*.png Grafiken 
 text/css *.css Dateien 
 text/html *.html Dateien 

text/plain *.txt Dateien (bzw. Fließtext) 

Ein Problem ist allerdings noch nicht gelöst, denn die Startseite wird mit gecacht und verliert somit ihre Aktualität. Eine Ausnahme für die Startseite finden wir mit den folgenden 4 Codezeilen:

      <Files index.htm>
 Header append
Cache-Control "public, must-revalidate"
 </Files>

Mit dieser Anweisung wird die Seite als öffentlich („public“) festgelegt. Durch den Zusatz must-revalidate erzwingen Sie, dass die Seite neu geladen werden muss. Dies gilt grundsätzlich für alle Dateien mit dem Dateinamen index.htm.

Wer tiefer einsteigen will, dem sei dieser englischsprachige Aufsatz empfohlen

Cache Control Online Tool