!init OPT_STYLE="paper" !define DOC_NAME "PApp - Perl-Anwendungen für die Zukunft des WWW" !define DOC_AUTHOR "Marc Lehmann " !build_title H1: PApp - Wie beschreibt man ein Chamäleon? PApp ist schwer zu beschreiben, da es sich nicht auf ein spezielles Problem spezialisiert sondern möglichst eine offene Universallösung sein will. Unter anderem ist PApp: - ein Embedded-Perl-Dialekt. Es gibt verschiedene Formen: Ein "literal programming style", der nicht XML-konform ist sondern beliebige Daten ausgeben kann und viele Möglichkeiten des Einbettens von Perl in XML. Schliesslich gibt es noch den Standard-XML-Dialekt, der eine Mischung aus beidem darstellt. - Perl-Module, die das Arbeiten in einer CGI-artigen Umgebung erlauben. Die Umgebung hält sich mehr an Apache, ist jedoch unabhängig davon, ob die PApp-Anwendung in einer CGI-Umgebung, unter mod_perl oder unter CGI::SpeedyCGI ausgeführt wird. - Eine Bibliothek (aus vielen Modulen), die das Arbeiten mit HTML, XML, SQL und anderen, häufig benutzten Umständlichkeiten vereinfacht. - Eine grosse "State-Maschine", die technisch getrennte Programmteile (z.B. CGI) logisch zu {{einer}} Applikation zusammenfasst. - Ein XSLT-Stylesheet-Prozessor. Damit kann man Layout und Inhalt voneinander trennen. Oder auch Ausgabeformat (PDF/HTML/XML etc.) vom Layout. Oder vom Inhalt... H2: Warum PApp geschrieben wurde Grosse und kleine Applikationen für das WWW zu erstellen ist sehr arbeitsaufwendig. Session-Variablen kennt Perl nicht, Session- und Usertracking natürlich auch nicht. Es gibt sehr gute Module, die einem einen Teil der Arbeit abnehmen, aber es ist immer sehr aufwendig und umständlich. Dies führt zu unübersichtlichen Programmen, die noch dazu auf viele Dateien aufgeteilt sind, die nicht unbedingt der inneren Logik des Programmes entsprechen. Auch Sicherheitsfehler (wie das Übergeben von sicherheitsrelevanten Daten über {{hidden-fields}} moder ähnliches) passieren schnell, schliesslich muss man sich bei jeder Seite überlegen, wie man seine Daten weiterreicht. Datenbankabfragen, das Einbauen des Layouts sind sehr umständlich. Internationalisierung existiert praktisch nicht. PApp vereinfacht alle diese Probleme, indem es sie weitestgehend automatisiert. H1: Grundlegende PApp-Features oder "Nie wieder CGI" Zunächst möchte ich einige der vielen Features von PApp vorstellen (natürlich die wichtigsten ;). Wem das zu langweilig ist und erstmal eine richtige PApp-Applikation sehen will, sollte zum nächsten Abschnitt gehen, "Eine einfache Anwendung mit PApp". H2: Applikationen statt Einzelseitenverwaltungsmonstren Bei CGI oder anderen Schnittstellen ist die Sicht auf die Anwendung protokollbedingt seitenbasiert - jede "logische" Seite ist eine Datei oder eine Fallunterscheidung innerhalb der Datei - gemeinsame Komponenten müssen in Bibliotheken (oder auch nur Include-Dateien; ist im Prinzip das gleiche) abgelegt werden die auf jeder Seite erneut geladen, konfigurietr und eingebaut werden müssen. Perl-Programme funktionieren jedoch anders, da gibt es keine Zustände, die beim anklicken wechseln, sondern ein lineares Programm. Dies läßt sich zwar auch mit PApp nicht verwirklichen, aber es tut sein Bestes, diese Einschränkungen wegzuheben, indem es ganze Anwendungen bearbeitet: Ob mehrere Seiten in einer Datei oder eine Seite auf mehrere Dateien verteilt wird ist in PApp egal. H2: development/maintainance: Aktivposten statt Bremsklötzen H3: Entwicklungshilfe Programme - auch solche, die mit PApp geschrieben werden - müssen erst entwickelt werden. Dabei werden Fehler gemacht. Da bei PApp sehr viele Komponenten zusammenwirken können die Fehler entsprechend komplex sein. Eine gute Unterstützung bei der Fehlersuche ist also wichtig. Dies fängt damit an, dass die Zeilennummern des Quelltextes auch nach der Kompilierung von Perl/XML zu reinem Perl oder z.B. in ein XSLT erhalten bleiben. Sollte ein Fehler auftreten, so hat man sofort Zugriff auf die Qeulldateien, einen kompletten Backtrace und natürlich die State-Daten. Das Exception-System von PApp ist einfach zu benutzen (C genügt, will man es schöner haben benutzt man C ;) und erweiterbar. Im Betrieb will man natürlich keine ausführlichen Fehlermeldungen, komplett mit Passwort der Datenbank usf... Deshalb protokolliert PApp alle Fehler in eine SQL-Datenbank zusammen mit der State-ID, so dass nur die Fehlerkategorie (plus ein nettes Textfeld, in dem der Benutzer zusätzliche Informationen eingeben kann). Solange die Seite nur wiederholbare Aktionen enthält, kann der Entwickler den Fehler jederzeit reproduzieren. H3: Datenbanken Wie soll man ohne auskommen? Natürlich garnicht. In Perl gibt es ein ganz wunderbares System (DBI!) um mehr-oder-weniger genormt auf SQL-Datenbanken zugreifen zu können. Jetzt ist "nacktes" DBI recht umständlich, zumindest für mich, für den vier Aufrufe pro SQL-Befehl entschieden zu viel sind. Vor allem, wenn man diese über das Programm verstreuen muss. In PApp erledigt man die meisten Aufgaben mit der C-Funktion (den Rest erledigt man mit C). Der folgende Aufruf ersetzt C, C, C und C: !block perl my $st = sql_exec \my($p, $w), "select purzel, wurzel from table where name like ?", $S{search}; while ($st->fetch) { echo "$p, $w\n"; } !endblock Der besondere Clou: Die SQL-Statements werden gecached, d.h. im Allgemeinen nur ein einziges mal Cd. Bei Datenbanken, die einiges an Aufwand in diesem Schritt investieren (Stichwort Query-Optimizer) ist das ein grosses Plus. Sogar bei Datenbanken, die dies nicht tun (z.B. MySQL) steigt die Geschwindigkeit merklich. Zudem sind das C und das C in normalen Programmen getrennt voneinander (die Befehle stehen weit auseinander). Bei Datenbanken, die eine Art "insertid" unterstützen (zum Glück fast alle), kann man diese nach einem C gleich mit abfragen: !block perl my $id = sql_insertid sql_exec "insert into tiere (name) values (?)", "hase"; !endblock Den Database-Handle habe ich übrigens nicht vergessen: Wenn ich keinen angebe, wird ein Default-Handle benutzt. Geschickterweise wird dieser von PApp selbst gesetzt, so dass man sich im Normalfall noch nicht einmal um die Datenbankverbindung kümmern muss (weil diese auf dem Produktionssystem meistens andere Passwörter hat als auf dem Entwicklungssystem stellt man diese geschickterweise bei der Konfiguration der Applikation ein). <> H2: Trennung von Quelltext und Sprache H3: XML XML wird in mehreren Teilen von PApp unterstützt bzw. gefordert: Der Standard-Dialekt von PApp ist in XML geschrieben, d.h. um "normale" PApp-Applikationen zu schreiben muss man sich XML bedienen. Die Ausgabe von PApp (meistens Text) ist beliebig, sofern man keine Stylesheets benutzt. Tut man das, muss die Ausgabe natürlich in XML-Syntax sein. Und last-not-least gibt es ein PApp-Modul, mit dem man beliebige XML-Fragmente mit eingebettetem Perl-Quelltext bearbeiten/ausführen/anzeigen kann. Die Entscheidung, XML so zentral einzusetzen, fiel mir nicht leicht: Meiner Meinung nach ist XML wunderbar dafür geeignet, Daten innerhalb von Programmen zu verarbeiten und vor allem auszutauschen. Toll ist, dass Menschen XML notfalls lesen und auch schreiben können. Ansonsten ist doch die Mächtigkeit von SGML (oder etwas anderem) vorzuziehen. XML jedoch hat bestechende Vorteile: HTML ist eine XML-Applikation (und HTML ist wichtig ;); es existiert eine breite Unterstützung für XML; es gibt bestehende Standards für Stylesheets, Layout, Metadaten und mehr. Schliesslich ist XML auch noch sehr schnell. Nun ja. Das hat mich trotzdem nicht davon abgehalten, etwas oben draufzusetzen: Fast alle Dokumente können in einem "Meta-Format" mit eingebettetem Perl-Quelltext geschrieben werden. Die Standard-Syntax dafür bedient sich vier sog. "Modus-Umschalter", die jeweils vom "Verbatim" in den "Perl"-Modus bzw. zurück schalten: !block verbatim <: schalte von HTML in Perl um schalte in den HTML-Modus ?> schalte in den interpolierten HMTL-Modus !endblock Diese Modus-Umschalter habe ich in den Beispielen schon verwendet, z.B. kann man eine HTML-Seite so schreiben: !block perl

Ein bisschen HTML

<: echo "mit eingebautem perl" :> <: print "so gehts auch, ist aber unendlich wenig langsamer ;)" :> <:for my $text (qw(Noch eine komische Methode)) { ?>$text <: }:> !endblock Die Modus-Umschalter verhalten sich dabei wie eine Statement-Grenze in Perl (also C<;>). H3: I18n I18n steht kurz für Internationalisierung: Das englische Wort {{Internationalization}} hat 18 Buchstaben zwischen dem 'I' und dem 'n', und da das Wort für den Durchschnittsamerikaner viel zu lang und kompliziert ist, hat man diese 18 Buchstaben einfach durch "18" ersetzt. In Kontext von PApp bedeutet dies, dass beinahe überall Textmeldungen übersetzt werden können (wer das C-Paket kennt, mit dem üblicherweise C-Programme internationalisiert werden, wird sich fast sofort Zuhause fühlen). Hier ist ein Beispiel: !block verbatim

__"Contents"

<:for (1..10) {:> $i:> <:}:> !endblock Wie man sieht, kann man C<__"text"> sowohl im HTML/XML/wasauchimmer-Quelltext benutzen, als auch auf Perl-Ebene aufrufen. Die C<__>-Funktion erledigt dabei zwei Aufgaben: Zunächst werden damit Textkonstanten zum übersetzen {{markiert}}. Zur Laufzeit wird dann in der Übersetzungstabelle nachgesehen und der (evt.) übersetzte String zurückgeliefert. Wenn man Daten z.B. aus einer Datenbank in eine Variable holt, darf man diese natürlich {{nicht}} markieren, da sie ja nur zur Laufzeit einen String {{enthält}}, selbst aber keine Textkonstante ist. In solchen Fällen verwendet man C: !block perl my $st = sql_exec \my($id, $name), "select id, name from table"; while ($st->fetch) { echo "
  • ", gettext$name, "

  • "; } !endblock In welche Sprache jeweils übersetzt wird, entscheidet bei HTTP die Einstellung des Browsers, wobei man zweckmässigerweise ein Menü anbietet, in dem der Benutzer dies überschreiben kann: !block perl "en":> "de":> !endblock Auch die Spracheinstellung ist eine Preferences-Variable. Im Gegensatz zum C-Paket müssen die Quelltext nicht alle in einer Sprache sein. Bei C ist das auch weniegr ein Problem, da der Quellcode eines Programms meistens in einer (natürlichen) Sprache geschrieben wurde, in PApp kann man aber Seiten dynamisch einfügen oder Datenbanken übersetzen. Da diese meist nicht vom selben Team geschrieben werden, ist es nützlich, dort unterschiedliche Sprachen verwenden zu können. <> <> H3: Unicode / Zeichensatzunabhängigkeit Intern unterstützt PApp genau zwei Datentypen (genau wie Perl selbst): {{Binärdaten}} und {{Text}}. Binärdaten verwendet man üblicherweise für Bilder, Datei-Downloads und ähnliches. Diese Daten werden von PApp nicht angerührt. Ganz anders Text: Perl arbeitet intern mit Unicode, das z.Zt. entweder in ISO-8859-1 oder UTF-8 gespeichert wird. Dieser interne Zeichensatz ist völlig unabhängig von der Ausgabe, d.h. der Text wird bei der Ausgabe automatisch in den gewünschten Zeichensatz kodiert (das geschieht wieder Browser/Benutzer/Programmabhängig wobei von den gebräuchlichen Browsern nur Netscape und Lynx die entsprechenden Header liefern). Umgekehrt werden Formulardaten u.ä. automatisch in Unicode gewandelt, d.h. in C<%P> steht Unicode auch wenn der Browser ein Formular in ISO-2022-JP zurückgeschickt hat. Ein JPEG-Bild kann man z.B. so ausgeben: !block perl content_type "image/jpeg", undef; $gd->jpeg(80); !endblock Während man eine japanische Benutzerin vielleicht mit ISO-2022-JP zufriedenstellen kann: !block perl content_type "text/html", "iso-2022-jp"; echo __"Hoffentlich war der Übersetzer fleissig"; !endblock H2: Trennung von Daten und Layout/Protokoll Sprache und Quelltext trennt PApp ja schon. Jetzt muss man noch das Layout vom eigentliche Programm bzw. das Protokoll von den eigentlichen Daten trennen. Mit XSLT-Stylesheets geht das. Und mehr: "Browser XYZ hat ein Problem? Ich fix' es im Stylesheet und vergesse es dann einfach." (z.B. sollte man leere HTML-Tabellenzellen für Netscape lieber mit einem C< > füllen). "Es soll WML (oder CHTML) sein? Ich weiss zwar nicht, wozu das ganze WML-Zeugs sinnvoll sein soll, aber dann mache ich halt' ein Stylesheet dafür." Und eine der wichtigsten Anwendungen: "Wir brauchen eine Version zum drucken. Dafür wandeln wir unser XML-Dokument in XSL" (und dann nach Latex, PDF...). Das bedeutet, dass man - Planung natürlich vorausgesetzt - einen hohen Grad an Protokollunabhängigkeit erreichen kann, ohne den Quelltext zu sehr mit Layout-Entscheidungen o.ä. belasten zu müssen. Leider gibt es noch keine Web-Designer, die XSLT statt HTML/CSS liefern, aber (meine) Vision des Webs der Zukunft sieht so aus: Der Server liefert XML zusammen mit einem XSLT (vom Designer), welches XSL erzeugt. Dieses wird dann angezeigt/gedruckt etc.a.. Metadaten (mit dem etwas besseren Nachfolger von RDF) gestatten dann Fragen wie: "Wer hat dieses Dokument geschrieben?", "Wozu dient es?" aber auch: "Wieso ist das Ding so bunt?". H2: Trennung von Funktionalität und Ausführungsumgebung Platform-Unabhängigkeit: ein toller Begriff. Was bedeutet er? Nicht viel. Bei PApp bedeutet es, dass auf Unix-Abhängigkeiten möglichst verzichtet wurde und sich PApp nicht an einen bestimmten Server (z.B. Apache) bindet. In der Praxis kann man als Platform mod_perl oder CGI verwenden und für Windoze schere ich mich einen Dreck. Aber auch andere Umgebungen wie Text-Interfaces sind denkbar: Da PApp der Applikation immer eine gleichbleibende Umgebung anbietet, muss man lediglich ein Schnitstellenmodul schreiben. H2: Persistente Variablen für Sessions und User PApp kümmert sich automatisch um persistente Variablen. So kann man automatisch Variablen erzeugen, die über eine gesamte Sitzung (die mit dem Aufruf der ersten URL beginnt und dann einen Baum bildet) persistent sind. Das geht so einfach, dass man spezielle Hilfsmittel braucht, um nicht an jeder Stelle der Sitzung Zugriff auf beinahe alles zu besitzen. Persistente Variablen gibt es in verschiedenen Geschmäckern. Es gibt: - Session-Variablen (sogenannte {{state keys}}), die über eine gesamte Sitzung erhalten bleiben. Diese werden im Hash C<%S> gespeichert. Man kann beliebige Werte darin ablegen (solange C diese serialisieren kann). Meistens gescheiht dies durch Anklicken eines Links, weniger häufig direkt. - Benutzerabhängige Variablen (die auch über Sitzungsgrenzen hinaus erhalten bleiben und deshalb {{preferences items}}, Voreinstellungen, heissen). Auch diese befinden sich in C<%S>, von wo sie automatisch in die Benutzerdatenbank wandern bzw, von dort gelesen werden. - Lokale Variablen ({{local keys}}) zu nur innerhalb einer Seite oder einer Gruppe von Seiten Bedeutung haben. Diese befinden sich ebenfalls in C<%S> und werden automatisch daraus entfernt. - oder beliebige Kombinationen davon. Ein Beispiel für eine Sitzungsvariable ist die Information, ob der Benutzer sich schon angemeldet/eingeloggt hat oder ob er bestimmte Zugriffsrechte besitzt. Eine benutzerabhängige Variable ist z.B.die Sprache, die der Benutzer zuletzt ausgewählt hat (global) oder die Anzahl der Tabellenzeilen, die der Benutzer gerne auf einer bestimmten Seite angezeigt haben möchte. Eine lokale Variable ist z.B. ein Datenbank-Objekt, das über mehrere Seiten (z.B. einer Transaktion) gebraucht wird und danach seine Gültigkeit verliert. Dadurch ergeben sich mehr Möglichkeiten, als man erwarten würde: Unabhängige Komponenten (z.B. Werbebanner oder Foren) sind auf normale Weise nur sehr schwer zu programmieren, da sie immer Hilfe vom "Hauptprogramm" erfordern wenn es um die Parameterübergabe geht. In PApp benutzt man einfach persistente Variablen. Ein Beispiel für eine etwas ungewöhnlichere Komponente ist die {{editform}}-Bibliothek. Mit ihr kann man HTML-Formulare erstellen, die sich direkt an eine bestimmte Variable binden: !block perl ef_begin; ef_string \$S{search}; ef_end; !endblock Dieses Beispiel stammt von einer Seite, die Objekte aus einer Datenabnk anzeigt und sich dabei auf solche beschränkt, die ein Suchwort enthalten. Das Formular bindet die State-Variable C an ein HTML-Textfeld. Bei der Ausgabe wird der aktuelle Wert von C<$S{search}> benutzt. Ändert der Benutzer den Text und schickt das Formular ab wird die Seite neu aufgebaut - mit einem anderen Wert in C<$S{search}>. Da editform beliebige Perl-Referenzen akzeptiert und man in PApp auch Referenzen auf Dateien, SQL-Spalten etc... erzeugen kann werden viele Formulare zum Kinderspiel. Zusätzlich zu den Sitzungsvariablen gibt es noch sogenannte {{Argumente}}, die nur eine Seite lang "persistent" sind, d.h. bei einem Link auf eine andere Seite übergeben werden: !block perl echo slink "Klicken Sie hier", -argument1 => "wert1", var2 => "wert2"; !endblock Wenn man diesem Link folgt, steht der "wert1" im Hash C<%A> bzw. "wert2" im Hash C<%S>: !block perl printf "Das Argument argument1 ist %s, die State-Variable var2 ist %s", $A{argument1}, $S{var2}; !endblock Werte, die von "Aussen" (z.B. GET-Request in CGI) kommen, stehen, um die Verwirrung komplett zu machen, in C<%P>. Als Faustregel gilt: was in C<%S> und C<%A> steht ist sicher (bzw. man hat es selbst dorthin gepackt), was in C<%P> steht ist unsicher und muss erst gefiltert/geprüft werden. H2: Sicherheit In vielen CGI-Programmen (aber nicht nur dort) wird der Fehler begangen, sensitive Daten in {{hidden}}-Feldern in einem Formular zu "verstecken" oder sie in die URL zu kodieren um die Parameterübergabe zu realisieren. Dies ist in PApp nicht nötig, da der persistente 'State' in einer Datenbank gespeichert wird und nur der (mit 256 Bit verschlüsselte) Zeiger darauf zum Client übertragen wird. Dadurch werden sensitive Daten gar nicht erst zum Client übertragen, was natürlich auch Bandbreite spart. Sollte der Schlüssel einmal bekannt werden kann man zwar auf andere Sessions zugreifen, die Daten jedoch immer noch nicht verändern (dieser Fall tritt beispielsweise auch auf, wenn ein kaputtes Proxy zwischen Server und Client sitzt). Eine typische PApp-URL sieht übrigens so aus (Applikation "kis", Modul "abteilungswahl"): !block verbatim /kis/abteilungswahl/NIlRSJNDIfP3AHfVjxLF5F !endblock Da eine "Seite" in PApp aus einem Modulbaum besteht, geht es auch komplizierter: !block verbatim /admin/+admin=poedit+poedit=view--?papp=eTDgL.I-lj9O9lTO3wznTk !endblock Zusammen mit einem sicheren Transportprotokoll (z.B. SSL, wobei die meisten Browser nur ungenügend oder garnicht gegenüber Attacken schützen, nur so am Rande ;) schützt man sich damit gegen alle Seiten. Benutzerauthentifizierung über Zertifikate ist auch im Einsatz (mit dem C-Modul von PApp). H2: Session/User-tracking Persistente Variablen erfordern Session-Tracking. Benutzerabhängige Einstellungen (Preferences) darüber hinaus auch User-Tracking. Ersteres geschieht mit einer Session-ID, die (auf verschiedene Arten) in die URL-kodiert wird und die alle notwendigen Daten enthält, um die gewünschte Seite komplett zu erzeugen. Darüber hinaus wird (optional) ein Cookie benutzt, das aber z.Zt. nur zum Identifizieren des Benutzers dient, da ich Session-Definitionen per Cookie als Unsinn erachte. Das Cookie wird auch nur einmal am Tag gesetzt, so daß man auch mit der Browser-Einstellung "vor Cookies warnen" arbeiten kann. Eine Anwendung wird darüber informiert, ob eine neue Session begonnen wurde oder ob sich ein bisher unbekannter Benutzer angemeldet hat, so daß man Benutzereinstellungen o.ä. initialisieren kann. Dies nützt PApp selbst, um sich z.B. die Sprache des Benutzers zu merken oder um das User-Cookie nur einmal täglich zu setzen. Eine "Sitzung" ist übrigens ein Baum (nicht nur in PApp), der mit dem ersten "Hit" als Wurzel beginnt. Dadurch ist es unter anderem möglich, die Anzahl der "Reloads" einer Seite zu bestimmen. Dies ist nützlich, um potentiell gefährliche Aktionen (z.B. Löschen von Datenbanken, abschicken von E-mails) nur einmal auszuführen. H2: Benutzerverwaltung Da PApp Benutzer (mehr oder weniger) eindeutig identifizieren muss, implementiert es intern eine eigene Benutzerverwaltung inklusive einer Unix-artigen Rechtevergabe (es gibt allerdings keinen Super-User im Unix-Sinne). In vielen Anwendungen kann man diese gleich mitbenutzen, da eine eindeutige Benutzer-ID automatisch vergeben wird. !block verbatim Sie sind wahrscheinlich Benutzer Nummer
    #if auth_p Und ausserdem haben sie sich authentifiziert.
    # if access_p "admin" Oh, und "admin"-Rechte besitzen Sie auch noch! Meine Güte!!
    # endif #endif !endblock H2: Geschwindigkeit und Skalierbarkeit Geschwindigkeit war bei der Implementierung von PApp das zweitwichtigste Ziel (Korrektheit ist das wichtigste ;). Als Marke dient mir ein Pentium-II 266Mhz-Rechner, auf dem auch komplexe Seiten mit mindestens 15 Hits/Sekunde dargestellt werden, bzw. der Vergleich mit einem ähnlichen C-Skript. Das State-Management von PApp verschlingt pro Seite 1-3 Datenbankzugriffe, die die Zeit bei weitem dominieren (andere Features wie I18n sind im Prinzip kostenlos). Da komplexe Seiten im Allgemeinen wesentlich mehr und kompliziertere Zugriffe enthalten bzw. man ja irgendwie seine Daten speichern muss, ist dies in der Praxis selten eine Einschränkung (Ausnahmen gab und gibt es immer). Andere PApp-Features (z.B. im SQL-Bereich) bringen häufig sogar eine Steigerung der Geschwindigkeit. Da ich bisher keine Seite fabrizieren konnte, die die 15 Zugriffe/s-Marke unterschritten hätte, glaube ich noch etwas Spielraum für noch mehr Features zu haben. Wer sich übrigens fragt, woher diese 15 Hits/s kommen: 15 Hits ist die Marke, die einen wirklich grossen Server von den 99.99% der restlichen Welt unterscheidet. Hat man auf seinem Server 15 oder mehr Zugriffe pro Sekunde kann man sich meistens auch eine schnellere Datenbank oder mehrere Rechner leisten: PApp skaliert problemlos auf mehrere Maschinen. H2: Ideen, Ideen: z.B. "tied forms" Ich habe es schon angesprochen: Persistenz von fast allem, zusammen mit den Möglichkeiten von Perl können einen schon auf Ideen bringen, z.B. auf eine Bibliothek (editform), die HTML-Formularfelder an Perl-Refrenzen bindet. Nun ist es an der Zeit, einmal eine Referenz auf eine SQL-Tabelle zu erzeugen: !block perl <: my $row = new PApp::DataRef 'DB_row', table => "user", where => [id => $userid], delay => 1, autocommit => 1; # pre-set name $row->{name} ||= ""; ef_begin; :>
    __"ID:" {id} , 5:><: :>
    __"Name:" {name}, 20:><: ef_submit __"Update"; ef_end; :> !endblock Die Referenz auf die Zeile steht in C<$row> un verhält sich wie eine Referenz auf einen herkömmlichen Perl-Hash. Das C sorgt dafür, dass die geänderten Daten automatisch zurückgeschrieben werden sobald das C<$row>-Objekt gelöscht wird (ausser Scope geht, Ced wird). Sie ist lokal (C!) gespeichert, da aber die C-Funktionen die Referenz persistent speichern, wird das Objekt erst auf der nächsten Seite (also nachdem die Ergebnisse hineingeschrieben wurden!) zerstört, bzw. die Daten geschrieben. Etwas kompliziert, aber zum Benutzen muss man die Details ja auch nicht kennen. Nun macht das Beispiel keine Fehlerüberprüfung, meistens das schwierigste. Mit PApp kein Problem. Zuerst schalten wir das C ab, damit die Daten nicht "aus Versehen" geschrieben werden. Ausserdem übergeben wir die Referenz als Argument an die "nächste" Seite. !block perl <: my $row = $A{row} || new PApp::DataRef 'DB_row', table => "user", where => [id => $userid], delay => 1, autocommit => 0; ef_begin -row => $row; # Daten als Argument übergeben !endblock Nun brauchen wir ein Flag, das uns sagt, ob der Datensatz fehlerhaft ist und schon können wir loslegen: !block perl my $err = 0; # Daten fehlerhaft? :>
    __"ID:" {id} , 5:><: #if $row->{id} !~ /^(\d+)$/ __"The ID must be an integer!" <:$err++:> #endif :>
    __"Name:" {name}, 20:><: #if 2 > length $row->{name} __"The Name must contain at least two characters!" <:$err++:> #endif ef_submit __"Update"; ef_end; !endblock Die Daten schreibt man dann bei Bedarf in die Datenbank: !block perl #if $row->dirty # if $err __"The entered Data is invalid, please surrender or die (or correct it ;) # else <:$row->flush:> __"The record has been updated." # endif #endif :> !endblock H2: "Web-Widgets" Eine relativ neue "Neuerung" in PApp ist die Einführung von Web-Widgets. Wie so vieles sind das keine speziellen Objekte, sondern eine Art der Programmierung, die zwar schon immer möglich warm aber an die man nicht sofort denkt. Statt einer ganzen Applikation, die "ganze Seiten" (z.B. ganze HTML-Seiten) ausgibt, schreibt man eine Applikation, die nur noch Teilseiten ausgibt, die man in größere Applikationenn einbaut. Dies ist nicht das gleiche wie ein "include": Der Namensraum (globale Variablen, state-keys, also Session-Variablen und Preferences) einer solchen eingebetteten Applikation ist getrennt von der einbettenden Anwendung und bildet eine Art "Unternamensraum". Derlei eingebettete Anwendungen sind vollkommen autark, haben ihren eigenen Zustand ("aktuelle Seite") und können ihre eigenen Formulare, Links etc. erzeugen die mit anderen Applikationen nicht kollidieren. In einer Anwendung verwende ich dasselbe Forum-Element, um "Web Chat", "Kleinanzeigen" und eine "News!"-Seite zu implementieren - jedesmal eine leicht andere Konfiguration aber derselbe Code. Da PApp-Applikationen im Prinzip nur grosse Statemaschinen sind (das ist eine Einschränkung der verwendeten Protokolle, d.h. bis Perl effektive und schnelle continuations bekommt ;), kann man sie auch einfach in andere einbauen. Sie können sich gegenseitig beeinflussen, treten sich aber nicht auf die Füsse. Also im Prinzip genauso wie ein "Widget" unter X11. H2: Logging/Protokollierung PApp protokolliert wesentlich mehr, als man benötigt, legal wäre, und speicherbar wäre. Da PApp jederzeit in der Lage sein muss, eine ältere Seite zu regenerieren ("Back & Reload"), speichert es pro Seite die persistenten Variablen (ca. 300-900 Byte, je nach Applikation, das ist, wie gesagt, auch eine tolle Sache zum Debuggen). Aber irgendwann müssen diese Daten wieder weg bzw. statistische Daten her - nichts ist interessanter, als Zugriffsmuster, Voreinstellungen oder ähnliches, an dem man ablesen kann, welche Dinge beliebt sind und welche nicht (klar, man kann auch ganz andere Sachen auswerten, aber das ist nicht mein Problem). Beim Aufräumen spielt PApp die einzelnen Zugriffe noch einmal durch. Statt jedoch Seiten zu erzeugen gibt PApp den einzelnen Applikationen die Möglichkeit, statistische Daten zu sammeln. Etwas undokumentierter (wenn es da Abstufungen gibt) ist die Möglichkeit, globale Daten zu sammeln, aber es geht... H1: Eine einfache Anwendung mit PApp What does it look like? %font "t", prefix " " %page Modules PApp is application centric - one application can consist of many pages (called 'modules'). Each web-page is (usually) a seperate module. Modules and even applications can be nested or embedded. State keys can be shared with ease within a session or user context. One module == one state of the state machine (http and wap are statemachines!). %page A simple module %font "t", prefix " " __"PApp - demo", <?localtime:> 'en':> "de":>

    1:> ]]> %page phtml mode switches <: switch to perl statement mode switch to plain html/text mode ?> switch to interpolated (qq) html/text mode %page Another simple module - "editor" %font "t", prefix " " #if defined $S{projectid} ... edit the specific project #else ... show a list of projects #endif <:footer:> ]]> %page SQL support - the project list %font "t", prefix " "
    __"Project"__"Budget" <: my $st = sql_exec \my($id, $name, $budget), "select id, name, budget from project"; while ($st->fetch) { :>
    $id?> $budget <: } :>
    %page SQL support - persistent rows %font "t", prefix " " <:ef_begin:> <:my $row = new PApp::DataRef 'DB_row', table => "project", where => [id => $S{projectid}]:>

    __"Name": <:ef_string \$row->{name}, 40:>

    __"Budget": <:ef_string \$row->{budget}, 8:>

    __"Description": <:ef_text \$row->{description}, 60, 10:> <:ef_submit __"Update":> <:ef_end:> %page Internationalization (I18n) Strings inside source files can be "tagged" Database columns can be tagged PApp is unicode-only Output conversion (e.g. utf8>latin1, utf8>iso2022jp) Translation editor ("poedit") included %page Speed Speed is slightly worse than Apache::Registry - on simple pages! Complex pages usually make no difference (database overhead!) Many PApp features deliver a performance that would be tedious to match in plain perl! PApp scales to many server machines, if necessary %page XML No, XML is not a feature. XML might be nice to computers, it isn't usually for humans, though. XML is robust XML is fast XML is well supported %page XML + XSLT = protocol independency PApp can support XML as well as HTML You can write your pages in XML XSLT stylesheets can be applied at parse time before module execution to the resulting output Thus, PApp applications can easily be made protocol-independent by writing them in XML and using a stylesheet that outputs either HTML, XML, WML or whatever you like! %page Dynamic stylesheet example %font "t", prefix " " load_stylesheet("dunno1"); $stylesheet[1] = $papp->load_stylesheet("dunno2"); ]]> %page "Web-Widgets" A nice term - what is it? PApp applications can be "embedded" into host applications With careful coding, reusable objects be created (e.g. a forum module, an ad-banner module etc.) Embedded apps come with their own state machine, own environment etc. Apply stylesheets to customize, if necessary %page PApp seperates layout semantics language from each other! %page Disadvantages perl 5.7.0 + patches is currently required many modules are necessary mod_perl is currently the only supported platform written by one overworked person only the api is in flux no stable release yet %page More info Dabei ist PApp etwas sehr persönliches - ich habe meine persönlichen Vorstellungen davon, wie Web/CGI etc. funktionieren sollte, darin verwirklicht. Es hat meine Motivation (die sich mit "nie wieder CGI" zusammenfassen liess) gewaltig gesteigert - Ein einfaches aber dennoch komplettes Content-Management-System kann man in weniger als 500 Zeilen hinlegen - für mich ein wichtiger Faktor, denn ich hasse nichts mehr, als das Rad jedesmal neu erfinden zu müssen. H2: PApp is Free Software Although we are not sure wether we'll publish all versions of PApp under the GPL, or which modules from our own applications might become standard components (like the forum), we are determined to make PApp free software. The principal PApp architect (me!) did a lot of free software modules and works at quite a few free software programs (like GCC or The Gimp) and is determined to make PApp as free and as powerful as possible. H1: Disadvantages PApp is not actually a revolution, judged by its components. It does, however draw a lot of functionality and ideas into a single, well-contained package. Nevertheless, there are quite a few reasons on why {{NOT}} to use PApp, or at least not to use PApp {{YET}}. * PApp is not yet a released module, its API is in flux (with respect to recent features), and not everything is working as it should yet. This is fortunately only a question of time. * Only a single person is currently developing and designing PApp for free. This means that advances might sometimes not lead into the direction you want, and maybe not in the schedule you want. * PApp requires the very latest perl - at the moment, this is perl 5.7 + custom patches, due to the buggy unicode support in earlier versions. The latest released PApp module (on CPAN) does not rely on unicode, but is already quite outdated with respect to current features. * There is a total lack of tutorials or introductory courses. While all PApp modules are documented, it is very much impossible to learn it using the reference documentation alone. Basically a one-day course under four eyes is necessary to get you up and running - PApp is not trivial. The lost time is generally made up with the increase in productivity experienced with PApp, though, similar to learning Perl ;) A1: References * PApp is available as a standard module from CPAN, although the current version uploaded is very old. * The PApp homepage is available at the author's homepage, under {{URL:http://www.goof.com/pcg/marc/papp.html}}. This page includes links to the current manpages. Online demos are, unfortunately, not yet available. * The newest versions of PApp can be accessed using CVS as a sourceforge project, at {{URL:http://www.sourceforge.net}}. * The slides for this presentation will be available at the author's homepage, at {{URL:http://www.goof.com/pcg/marc/docs.html}}, shortly after the linuxworldexpo. A2: Apology I'd like to apologize for any typoes or other mistakes in this document. It was written in a single session without access to a spellchecker and so has not been debugged it yet ;) %page