Technik, Gothic und Anderes

Technik ist Spiel, Gothic ist ernst und Zeit hat man zuviel

Archiv

Automatisches und mehrfaches submitten von Formularen

Geschrieben von skaldrom am 30. July 2008

Wirre Gedanken

FormularDieser Beitrag ist dem Titz gewidmet. Dem Titz, der sich aufgeregt hat :pirate-grumble: , obwohl es gar nicht nötig gewesen wäre und der sich nun mit einer gewissen Teilnehmerredundanz anfreunden muss. Hauptsache ist doch, das PHP fliesst und die Variablen bleiben sauber… Und ein Bierchen würde ich auch noch springen lassen :bier:

Eine wichtige Frage zu Beginn: Wieso sollten wir denn automatisch und mehrfach Formulare submitten wollen? Hmm, um den Titz zu ärgern? Weil wir es können? Weil man manchmal tun muss, was man tun muss? Weil man seine 84 Kinder an einem elektronischen Fussballturnier anmelden will :laola: (hat eigentlich schon jemand bemerkt, dass ich neue Smilies und unglaublich Freude daran habe?)? Oder weil man über einen Wettbewerb gestolpert ist, der ebendies nicht verbietet (also das Mehrfachsubmitten, nicht das Kinderanmelden oder die neuen Smilies) und der kein Captcha hat (vielleicht, weil es der Titz vergessen hat)?

Nachtrag 31.07.2008: Schenken mir doch so unglaublich nette Mitarbeiter einer Versicherung heute morgen am Bahnhof einen Müsliriegel. Und auf diesem eher gesunden Teil hat es, ja rate, oh wissbegieriges Volk, einen Wettbewerb. Ob der Titz wohl am Abend für eine andere Firma weitercoded? Ich werde mich auf jeden Fall während der Zugfahrt mal damit befassen :computer: .

Vorgehen

Erster Schritt: Selenium IDE

Man könnte nun wie wild losprogrammieren, oder aber einen einfacheren Weg wählen. Ein guter Startpunkt für automatisiertes Browsen generell ist die Selenium IDE. Dieses geniale Teil für den Firefox zeichnet wie ein Macrorecorder alles auf, was im Browser gemacht wird. Hat man die Teilnahme beim Wettbewerb einmal so aufgezeichnet, so müssen nur noch die click’s, die das Form absenden, durch clickAndWait’s ersetzt werden, damit vor dem Weiterausfüllen (bei mehrseitigen Formularen) auf die neue Seite gewartet wird. Es empfiehlt sich, eine Abschlussüberprüfung als letzten Schritt hinzuzufügen, um um kontrollieren zu können, ob die Kinder erfolgreich angemeldet wurden (markieren des “Dankeblabla”, dann rechte Maustaste und assertTextPresent).

Ein Wettbewerb über 2 Seiten. Aufgezeichnet und bearbeitet mit der Selenium IDE.

Ein Wettbewerb über 2 Seiten. Aufgezeichnet und bearbeitet mit der Selenium IDE.

Diesen Testcase kann man nun abspeichern und eigentlich immer wieder ausführen. Ein Klick reicht und Firefox rasselt alles schön durch. Den Namen leicht verändern kann man durch Editieren des Skripts…

Für den zweiten Schritt sollte man das Testscript als PHP exportieren.

Zweiter Schritt: Selenium RC

Die Selenium RC Komponente kann den Browser fernsteuern und so ferngesteuert am Wettbewerb teilnehmen. Um sie unter Linux Debian zum Laufen zu kriegen, war ein Bisschen Gemurkse notwendig.

Lesen Sie den Rest dieses Beitrages »

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | 2 Komentare »

Dynamische Signatur - e-Schrecking für Foren, MySpace, …

Geschrieben von skaldrom am 28. July 2008

Dynamische Signatur von Danasoft Ich mag es sehr, Leute zu ängstigen. Früher, zu den Gothenzeiten, habe ich dafür ein ganzes Arsenal von Dingen gebraucht: Schminke, Linsen, künstliche Fingernägel, … Heute reicht meine Anwesenheit und mein Gesicht :-) . Wenn ich mich unter besonders verständnisvollen Menschen befinde, breche ich in spontane, apokalyptische Prophezeiungen oder aber auch abwechselnd in ebenso apokalyptisch anmutende Zuckungen aus.

Als besonders ängstigend hat sich auch folgendes Vorgehen herausgestellt: Man fixiert eine Person, lächelt wissend, nickt ebenso wissend und macht wissende Bemerkungen wie “ja, es ist schon schwer” oder aber auch “ich denke Du machst bald einen Fehler”.

Online kommt mein Gesicht nicht so zur Geltung und Zuckungen fahren über die Webcam auch nicht so apokalyptisch durch Mark und Bein, also haben acht von neun Stimmen in meinem Kopf gesagt, ich soll den letzten Trick elektronifizieren (eine Stimme summte die Melodie von Tetris).

Danasoft hat schon was sehr Nettes, wie man am Lead-Image erkennen kann. Ich will aber nichts Nettes, sondern was Böses, darum habe ich die Pöhse Horror Pranke (PHP) hervorgeholt und was gecoded.

Grundidee

(Reload für weitere Weisheiten).
Dynamische Signatur

Es soll eine Signatur (oder Batch, Widget, Papperl, …) werden, die man in Foren (MyFace, SpaceBook, prollVZ, …) und Blogs verwenden kann, und das vorgibt mehr zu wissen als es tatsächlich tut.

Lesen Sie den Rest dieses Beitrages »

Ähnliche Artikel

Eingeordnet in Web | 4 Komentare »

AntMe, Spass mit programmierbaren Ameisen

Geschrieben von skaldrom am 2. July 2008

AntMe!Nachdem ich die Ameisen in meiner Wohnung mit ganz perfidem tragts-es-zu-euch-nach-Hause-und-vergiftet-eure-Nachkommen-Gift losgeworden bin, habe ich sehr viel sympathischere Zeitgenossen dieser Spezies getroffen: AntMe ist ein Programmierspiel, dass aus Microsofts Coding4Fun Initiative (nicht zu verwechseln mit dem Galileo Coding for Fun Buch. Das ist auch spassig, behandelt auch AntMe, aber zusätzlich noch viele Andere Dinge) hervorgegangen ist. Es hat einen eigenen Wikipedia Artikel und vorallem eine geniale Homepage.

Grundsätzlich geht es darum Ameisen zu implementieren die überleben, Äpfel und Zucker sammeln und unter Umständen auf Käfer und fremde Ameisen losgehen. Ein Markierungsmechanismus sorgt dafür, dass die Ameisen untereinander kommunizieren können. Fortgeschrittene Ameisenpapis und -mamis können spezialisierte Ameisen erstellen und RPG mässig Eigenschaften verbessern, wenn sie dafür andere verschlechtern.

Die Ameisen in Aktion

Die Ameisen können in C# oder in Visual Basic .net mit der notwendigen Intelligenz versehen werden. Bestimmte Methoden werden zu bestimmten Ereignissen aufgerufen; beispielsweise WirdMüde() (ja, mit “ü” und deutsch) oder SiehtFeind(ByVal käfer As Käfer). Zur Steuerung steht fast der ganze .net Sprachumfang zur Verfügung, sowie Ameisenspezifische Hilfsmethoden wie etwa GeheZuBau() oder GreifeAn(käfer).

Quickstart

Auf der AntMe Homepage findet man verschiedene Versionen. Wir spielen hier mit der 1.5 Beta 2 (VB.net oder C#, je nach Gusto). Die Profiversion beinhaltet den gesamten Quellcode der Spielengine (!). Das ist so genial, dass es nochmals erwähnt werden darf: Die Profiversion beinhaltet den gesamten Quellcode der Spielengine. Will man sich nur mit den Ameisen beschäftigen, reicht die Einsteigerversion. Im Download enthalten ist ein gut dokumentiertes Ameisenskelett, bei dem sofort losgottgespielt werden kann.
Eine gute Doku über die Klassen und deren Methoden sowie ein Tutorial wird mitgeliefert. Zum Teil beziehen sich die Unterlagen noch auf Version 1.1, aber der Transfer zu 1.5 ist eigentlich kein Problem. Für Leute, die lieber kuckn statt lesen, gibt es gut gemachte Screencasts, die ebenfalls in die Thematik einführen.

Für eher haptisch veranlagte Menschen gibt es auch ein Buch: AntMe! - Programmieren und Spielen mit den Ameisen und Visual C#. Das kenne und besitze ich allerdings (noch) nicht.

Verfügt man noch über keine Programmierumgebung, so könn bei Microsoft gratis die Express Versionen verschiedener Sprachen downgeloaded werden. Für die 3D Visualisierung braucht man
noch DirectX. Mit der neusten Version (nicht mit der im Forum angegebenen) hat es wunderbar gefunzt.

Turniere

Ein Bisschen lebt das Ganze auch vom kompetitiven Element (um ehrlich zu sein, vielleicht ist ein Bisschen etwas untertrieben: ICH FRESS EUCH ALLE AUF!). In der Version 1.5 funktioniert das Importieren von anderen Ameisen leider nicht und es wird eine unbehandelte Ausnahme ausgespuckt:
Der Typ für Member AntMe.SpielKonfiguration.AntMe.Simulation, Version=1.5.0.0, Culture=neutral, PublicKeyToken=37d8e32ef3294969 wurde nicht aufgelöst.

Workaround für die VB-Version (C# ungetestet):
Lesen Sie den Rest dieses Beitrages »

Ähnliche Artikel

Eingeordnet in Coding, Desktop | 5 Komentare »

PHP_SELF ist böse! Potentielles Cross Site Scripting (XSS)!

Geschrieben von skaldrom am 7. May 2008

Was haben wir gelernt?

EvilJa, uns wurde gelehrt, dass man wenn immer möglich nicht Dateinamen direkt, sondern eine Variable angeben soll, die für den Dateinamen steht. Warum konnte mir zwar noch niemand so genau sagen, aber ich nehme an, dass es darum geht, dass der Dateinamen oder der Pfad ändern könnte. So habe ich ziemlich oft ganz brav geschrieben:

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method ="post">
...
</form>

Und (zum Glück) bin ich nicht der Einzige. Eine kleine Suche auf Googles Codesearch hat gezeigt, dass mindestens Mambo, PHPMyFAQ, Einige Wordpressthemes und viele mehr dasselbe Problem haben. Ich bin bei Weitem auch nicht der Erste, der über Probleme mit dieser Technik erfahren und darüber geschrieben hat.

Problem? mit $_SERVER['PHP_SELF']?

Lasst mich ausholen: Der Apache Webserver hat eine Option AcceptPathInfo, welche standardmässig auf On ist. Mit dieser Option mappt der Apache beliebig lange Pfade auf Dateien, sofern diese irgendwie Bestandteil des Pfades sind. Der Rest wird in Umgebungsvariablen mitgegeben.

Gimme Code

Nehmen wir an, es gäbe eine Datei /subdir/mypath.php welche so aussieht:

<?php
echo "<pre>";
echo "REQUEST_URI: ".$_SERVER['REQUEST_URI']."\n";
echo "PHP_SELF:    ".$_SERVER['PHP_SELF']."\n";
echo "SCRIPT_NAME: ".$_SERVER['SCRIPT_NAME']."\n";
echo "</pre>";
?>

Bei einem Aufruf von: http://localhost/subdir/mypath.php/additional/stuff/nonsense.php?para=4 mappt Apache netterweise alles auf unsere Datei http://localhost/subdir/mypath.php und verstaut den Rest im $_SERVER Array. Die Ausgabe ist:

REQUEST_URI: /subdir/mypath.php/additional/stuff/nonsense.php?para=4
PHP_SELF:    /subdir/mypath.php/additional/stuff/nonsense.php
SCRIPT_NAME: /subdir/mypath.php

PHP_SELF übernimmt also den ganzen Krempel und würde ihn bei unserem Form auch so darstellen. Angenommen, wir haben ein PHP-Script mit der URL http://localhost/contact/myform.php mit folgendem Inhalt:
<form action=”<?php echo $_SERVER['PHP_SELF']; ?>” method =”post”>

Ruft man dieses nun mit folgender URL auf (die aufwändiger als notwendig konstruiert und des besseren Verständnisses wegen nicht URL-encodiert ist):
http://localhost/contact/myform.php/”></form>Hier ein Javascript: <script>alert(’gotcha’);</script><form action=”/contact/myform.php
erhält man:
<form action=”/”></form>Hier ein Javascript: <script>alert(’gotcha’);</script><form action=”/contact/myform.php” method =”post”>

Also vollkommen gültiges HTML (sogar das Form funktioniert) mit fremdbestimmbaren Seiteninhalt. Das ist ja wohl hässlich…

Theorie! Gib mir Praxis!

Ich weiss nicht, wie lange folgende Links funktionieren, beziehungsweise diese Sites anfällig für diese Art von XSS sind:

Digital Postcard (Kein XSS, aber mein Text :-) ):
Postcard XSS

Multimediatreff:
Multimediatreff

Jobs. ch (nicht im Form, dafür mit hohem PageRank):
Jobs.ch

Was nutzt das dem bösen Hacker?

Text in Fremdpages einbauen, Phishing, Indentitätenklau und noch einiges Weiteres. Ein Folgeartikel wird mindestens eine Anwendung zeigen.

Was tun?

Ganz einfach: Das oft verschmähte $_SERVER['SCRIPT_NAME'] verwenden!!!

BTW: Viele Variablen in $_SERVER sind anfällig, aber alles muss ich ja auch nicht verplappern, oder?

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | Keine Kommentare »

Wenn ich so programmieren würde wie Psychiater behandeln…

Geschrieben von skaldrom am 6. February 2008

PsychiaterWenn ich Applikationen so programmieren würde wie die meisten Psychiater die Menschen behandeln, dann …

  • … hätte ich keine konkrete Ahnung, wie Computer tatsächlich funktionieren, nur vage Ideen an Hand der visualisierten Strömchen.
  • … würde ich eine fehlerhafte Applikation immer wieder denselben Fehler machen lassen und ihr dann mittels Powerpoint zeigen, wie es richtig gehen würde, bis sie verlernt den Fehler zu machen.
  • … würde ich die Applikation Zufallsausgaben machen lassen und sie ausgedehnt loben, wenn sie per Zufall die richtigen Zahlen ausspuckt.
  • … würde ich bei neuen Fehlern irgendwann mal die Compliance der Applikation anzweifeln.
  • … würde ich Termine ohne Uhrzeit vereinbaren: Vormittags (heisst “vor 16:00″) oder Nachmittags (”am Morgen des nächsten Tages vielleicht”).
  • … würde ich von Montag bis Mittwoch von 10:00 bis 16:00 arbeiten (Morgen-, Mittag- und Nachmittagspause sind auch Arbeit) und 18 Wochen des Jahres mit Ferien und Kongressen verbringen.
  • … würde ich bei einer Fehlermeldung:
    • … die Fehlermeldung wegprogrammieren, nicht die Ursache des Problems.
    • … den Computer einen Vertrag unterzeichnen lassen, dass er die Fehlermeldung nicht mehr bringt.
  • … würde ich bei einer Programmiersitzung zuerst mal hinsitzen und schauen, was sich so während des Tages entwickelt.
  • … würde ich bei einem Bug das Aufstehen, Anziehen, Duschen, Kaffeetrinken, etc. des Programmierers nachstellen, um den Ursprung des Bugs zu ergründen.
  • … würde ich zuerst einen Monat ein Bisschen die eine Programmiersprache versuchen, dann einen Monat eine Andere, einen Monat später wieder die Erste, aber etwas intensiver, dann die Zweite mit einer Dritten kombinieren, …
  • … könnte ich nicht unterscheiden zwischen den verschiedenen Betriebssystemen und würde bei allen dasselbe ausprobieren.
  • … wäre ich überzeugt davon, dass jede noch so komplexe Applikation mit einer Stunde Programmieren pro Woche realisiert werden kann.
  • … würde ich bei schweren Bugs vorschlagen, mit dem Kunden zusammenzusitzen, um Wege zu finden wie er mit der verbuggten Applikation arbeiten kann statt diese zu beheben.
  • … würde es mir im Traum nicht einfallen den Debugger hervorzuholen, sondern ich würde immer wieder unterschiedliche Eingaben machen um die Ausgaben interpretieren zu können.
  • … würde ich die Ausgaben der Applikation nicht wirklich als deren Ausgabe sehen, sondern als Metapher für das, was sie ausgeben möchte, wenn sie denn funktionieren würde. Aus der Differenz würde ich schliessen, in welcher Projektphase Defizite vorhanden waren.

Ähnliche Artikel

Eingeordnet in Weitere Gedanken | 9 Komentare »

UWA Widgets coden

Geschrieben von skaldrom am 25. January 2008

Web 2.0, Just Do It…

Je besser man etwas kennt, desto besser kann man darüber lästern… Ich habe beschlossen, mich ganz praktisch durch das Tal der Tränen zu kämpfen und mich auf dieser Wanderung mit den technischen Teilen von Web 2.0 herumzuschlagen. Wie fühlt sich Ajax an? Wieviel Schatten spendet der DOM-Tree? XML oder JSON, JavaScript und APIs sollten die Dämonen auf meinem Weg sein.

Das Resultat

Das Resultat kann im Ecosystem angesehen und installiert oder von der oncode.info Site downgeloaded werden. Es ist ein Widget, das Adressen über tel.search.ch sucht.
Tel.search.ch bietet ein geniales, gut dokumentiertes REST API an.

Ein paar Screenies?
So siehts in Netvibes aus:
Das tel.search.ch Widget in Netvibes

So in iGoogle:
Das tel.search.ch Widget in iGoogle

Und so in der Sidebar von Windows Vista:
tel.search.ch UWA Widget in Windows Vista Sidebar

Lesen Sie den Rest dieses Beitrages »

Ähnliche Artikel

Eingeordnet in Webapplikationen | 3 Komentare »

PHP-UWA Widget Library

Geschrieben von skaldrom am 26. November 2007

Widgets and Web 2.0

Universal Widget ArchitectureWidgets are little miniapplications which show data in a clearly arranged way or perform a more or less simple task for the user. Widgets are present on Windows Vista, Mac, iPhone and also in a webbased form for iGoogle, Yahoo!, Netvibes and many other portals. To give a boost to widget development, Netvibes presented a new framework which shall facilate the coding of widgets. The child is called Universal Widget Architecture. Widgets coded with the help of this framework should work on all the mentioned plattforms.

UWA Standard

A widged, coded with UWA is basically a XML-Dokument. It contains metadata, settings and the active part, written in Javascript. Especially the Preferences are of interest, because they are dynamic and allow widgetspecific settings. There are also some convenience-functions in the UWA-library.

The UWA specification has its own homepage and is very well documented. There are examples, a code-skeleton with explanations, a step-by-step tutorial, a forum and even a cheat-sheet. The start is very easy with such a lot of documentation.

Widget Repository

Finished and released widgets can be made available for the public and published in the Widget Repository (Ecosystem). Widgets ion this website can be added to the different platforms with a single (or double) click). There are some widgets in the repository which are coded in the deprecated Mini-API standard, but these will dissappear soon (hopefully).

Implementations

That sounded fascinating and must have a use somewhere… I will implement some widgets later, I needed to make these widgets usable in our own projects first. So I wrote a little PHP-class which I called pretentious PHP-UWA Widget Library.Handling widged should be easy, using this class. A little bit more ambitious is the handling of widget dependend preferences.

A minimal example is included in the download-package and can also be checked online.

PHP-UWA Widget Library Example

Displaying a widget should be straight forward:

<?php
require_once('uwawidget.php');
$uwawidget=new uwawidget('http://www.netvibes.com/api/uwa/examples/digg.xhtml');
echo $uwawidget->getWidgetHTML();
?>

There are two classmembers which can give more information about the widget:

getMetaData()
Metadaten like author, keywörds, description, … See the docu.
getAdditionalData()
Additional info like icons, stylesheets, …

Basically, there are the following sections for settings:

general
The widgets URL.
configuration
Displayparameters, which described in the docs.
preferences
Widget dependent preferences, also mentioned in the docs

For all these settings, there are the following classmembers:

Setters and Getters
setModuleUrl(), setConfiguration(), setPreferences()
getModuleUrl(), getConfiguration(), getPreferences()
getSettingsFormData($section)
Returns the settings in a friendly array, from which a form can be generated. $section can be “general”, “configuration” or “preferences”
getSettingsHTML($section)
Returns the settings in an array with the format “Label” => “HTML”. $section can be “general”, “configuration” or “preferences”

For a test, I coded a Moodle block, which allows to use UWA Widgets inside the LMS. Heyo, Wordpress, Xoops, etc-Coderz, how about an integration of the Widgets in your system???

Examples from the Moodle block:

Moodle UWA Calculator Moodle UWA Converter Moodle UWA Translator
Moodle UWA Google Notes Moodle UWA Spider Moodle UWA ToDo
The configuration in Moodle:
Widget settings in Moodle

Uh, almost…

…I was almost faster than the german computer magazine c’t which has a short bit good introduction into UWA-Widgets.

Ähnliche Artikel

Eingeordnet in Learninmanagement Systeme (lms), Web | 4 Komentare »

Einfache Animationen auf der Konsole mit PHP (1-Dimensional)

Geschrieben von skaldrom am 7. November 2007

AnimationWenn man mit PHP Konsolenapplikationen schreibt, wäre es manchmal schön, eine pfundige Ausgabe mit etwas Bewegung erzeugen zu können. Solche Kleinstanimationen eignen sich auch für Aufgaben beim Erlernen von PHP, denn sie sind sehr unaufwändig und es tut sich trotzdem was.

Diese Ausgaben sind Eindimensional (auf einer Linie) und funktionieren unter Linux und Windows. Ich warte sehnsüchtig auf die PHP-Languagebindings für die aalib oder libcaca :-)…

Der Trick ist der Carriage Return (CR), der den Cursor an den Anfang der Zeile setzt, aber nicht vertikal weiterscrollt. in PHP wird dieses Zeichen mit \r codiert. Grundsätzlich ist der Ablauf wie folgt:

Bilderfolge in ein Array rendern
Solange man Lust hat:
    Zeige nächstes Bild
    Warte
  }
}

Unter Linux können zur weiteren Effektsteigerungen auch noch ANSI-Escapesequenzen verwendet werden. Mit diesen kryptischen Dingern kann man den einzelnen Zeichen Vorder- und Hintergrundfarbe verpassen und verwirrendes einschalten wie etwa ein Blinken. ESCAPE ist in PHP beispielsweise 33 in oktal und wird mittels \033 dargestellt.

Nun, zeig mal Code!

<?php
// Render Movie
$frames=array();
// 10 images for this movie
for($i=0;$i<10;$i++) {
    // The background consits of blue underscores
    $frame=array_fill(0, 10, "\033[34m_\033[0m");
    // One actor is a red star
    $frame[$i]="\033[31m*\033[0m";
    // The other a green dot
    $frame[9-$i]="\033[32m.\033[0m";
    // Join Background and actors
    $frames[]=join("",$frame);
}

// Output
for($i=0;$i<20;$i++) {
  foreach($frames as $frame) {
        // Rewind cursor and show frame
        print "\r".$frame;
        // Sleep a bit, because nights are hard these times...
        usleep(100000)
  }
}
print "\n";
?>

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | Keine Kommentare »

Erstellen eines Timers in Xoops

Geschrieben von skaldrom am 1. November 2007

Xoops ist ein geniales CMS, auch wenn es in letzter Zeit durch administrative Querelen nicht sehr gute Schlagzeilen gemacht hat. Wir benutzen es in einer erweiterten Version als Intranet.

Nun braucht ein Modul plötzlich unendlich lange um sich darzustellen. Das hindert unseren rasanten Arbeitsfluss und darum muss darum korrigiert werden. Statt rumzufrickeln lohnt es sich, die Sache seriös anzugehen.

Xoops bietet eine sehr gute Debugfunktion an: Adminsitration → Systemeinstellungen → Voreinstellungen → Allgemeine Einstellungen → Debug-Modus kann auf PHP-Debug gesetzt werden. Danach werden unten an der Seite viele Infos dargestellt, unter Anderem SQL-Befehle und Timer.

Xoops Debug

Genau solche Timer kann man selber einfach erstellen. Zuerst muss der XoopsLogger eingebunden werden:

include_once(XOOPS_ROOT_PATH . '/class/logger.php');

Um einen Timer zu starten:

$xoopsLogger->startTime( 'absence_count_query' );

Um ihn wieder zu stoppen:

$xoopsLogger->stopTime( 'absence_count_query' );

Diese Timer wird nun - wie oben ersichtlich - wunderschön in die Debugfunktion eingereiht.

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel, Web | 1 Kommentar »

Eine eigene Programmiersprache erschaffen? Lexer und Parser in PHP!

Geschrieben von skaldrom am 25. October 2007

Ich will Gott sein!

Paper SnippetsWer träumt nicht davon: Eine eigene Programmiersprache, denn der Syntax und das Wort sind Ausdruck! Einen Schritt weiter gehen und vom Programmiersprachenanwender zum Programmiersprachenerschaffer aufsteigen! Auch dies ist nun möglich in PHP, doch leider nur mit minimaler Dokumentation.

Ich gehe davon aus, dass die Leserin/Der Leser Grundlagen im Compilerbau besitzt. Ansonsten müsste man sich diese noch aneignen. Compilerbau ist rekursiv und somit göttlich! *schwärm*.

Das Beispiel

Ich möchte einen Interpreter bauen für normale, arithmetische Ausdrücke wie 9*(8+6)-2/(-4). Klar könnte man diesen einfach durch eval hauen, aber erstens macht das nicht so viel Spass und zweitens ist dieses Beispiel sowas wie das Hello World des Compilerbaus.

Als BNF habe ich folgendes vor:

<Expression> ::= '+' <Expression> // Unäres + (Vorzeichen)
<Expression> ::= '-' <Expression> // Unäres - (Vorzeichen)
<Expression> ::= <Term>
<Expression> ::= <Expression> '+' <Term>
<Expression> ::= <Expression> '-' <Term>
<Term> ::=  <Factor>
<Term> ::= <Term> '*' <Factor>
<Term> ::= <Term> '/' <Factor>
<Factor> ::=  NUMBER // Ok, hier hab ich abgekürzt ;) Soll der Lexer erledigen...
<Factor> ::= '(' <Expression> ')'

Ein Problem stellen die unären Plus- und Minusoperatoren dar. Sie besitzen das gleiche Symbol, nicht aber die gleiche Priorität wie ihre binären Kollegen. Lemon erlaubt keine änderung der Priorität nach Regeln und so funzen jetzt halt auch Konstrukte wie —+++—+1…

Die Werkzeuge

Ich verwende PHP_LexerGenerator, der kompatibel zum re2c Format ist und den PHP_ParserGenerator der in grossen Teilen dem LEMON Parser Generator entspricht. Heruntergeladen werden können sie mittels folgenden Zeilen, die allerdings unter Umständen noch eine Anpassungen der Versionsnummer brauchen:

pear install channel://pear.php.net/PHP_ParserGenerator-0.1.5
pear install channel://pear.php.net/PHP_LexerGenerator-0.3.4

Als einziges, weiteres Beispiel für diese Libs ausserhalb der Doku habe ich nur dieses Drupalmodul gefunden.

Die Realisierung

Der Lexer

Für den Lexer muss eine plex Datei erstellt werden. Sie beinhaltet den Rumpf des Lexerobjekts und die lexikalischen Regeln:

TermLexer.plex

<?php
class TermLexer
{
    private $data;
    public $counter;
    public $token;
    public $value;
    public $node;
    public $line;
    private $state = 1;

    function __construct($data)
    {
        $this->data = $data;
        $this->counter = 0;
        $this->line = 1;
    }

/*!lex2php
%input $this->data
%counter $this->counter
%token $this->token
%value $this->value
%line $this->line
number = /[0-9]+(\.[0-9]+)?/
multiplication = /\*/

division = @/@
plus = /\+/
minus = /\-/
openP = /\(/
closeP = /\)/
other = /./
*/
/*!lex2php
%statename START
multiplication {
  $this->token = TermParser::TP_MULTIPLICATION;
  //echo "multiplication: ".$this->value."\n";
}
division {
  $this->token = TermParser::TP_DIVISION;
  //echo "division: ".$this->value."\n";
}
plus{
  $this->token = TermParser::TP_PLUS;
  //echo "plus: ".$this->value."\n";
}
minus{
  $this->token = TermParser::TP_MINUS;
  //echo "plus: ".$this->value."\n";
}
openP {
  $this->token = TermParser::TP_OPENP;
  //echo "openP: ".$this->value."\n";
}
closeP {
  $this->token = TermParser::TP_CLOSEP;
  //echo "closeP: ".$this->value."\n";
}
number {
  $this->token = TermParser::TP_NUMBER;
  //echo "number: ".$this->value."\n";
}
other {
  return false;
}
*/

}

So richtig laufen tuts aber noch nicht, da er Konstanten des Parsers benötigt. Die stehen erst zur Verfügung wenn wir den Parser auch gebaut haben.

Der Parser

Der Parser wird vom Lexer mit Tokens gefüttert. Die Notation im Lemon-Style unterscheidet sich von Lex und Yacc, ist aber durchaus logisch. Hier der ganze Code, mal hingeknallt:

TermParser.y

/* This is an example for a Parser in PHP */
%name TP_
%declare_class {class TermParser}
%include_class
{
    // states whether the parse was successful or not
    public $successful = true;
    public $retvalue = 0;
    private $lex;
    private $internalError = false;

    function __construct($lex) {
        $this->lex = $lex;
    }
}

%token_prefix TP_

%parse_accept
{
    $this->successful = !$this->internalError;
    $this->internalError = false;
    $this->retvalue = $this->_retvalue;
    echo "WORKED!!\n\n";
}

%syntax_error
{
    $this->internalError = true;
    echo "Syntax Error on line " . $this->lex->line . ": token '" .
        $this->lex->value . "' count ".$this->lex->counter." while parsing rule: ";
    foreach ($this->yystack as $entry) {
        echo $this->tokenName($entry->major) . '->';
    }
    foreach ($this->yy_get_expected_tokens($yymajor) as $token) {
        $expect[] = self::$yyTokenName[$token];
    }
    echo "\n"
    throw new Exception('Unexpected ' . $this->tokenName($yymajor) . '(' . $TOKEN. '), expected one of: ' . implode(',', $expect));
}

%left PLUS MINUS.
%left MULTIPLICATION DIVISION.

start(res)       ::= expression(expr). { res = expr; }

/* Unary minus or plus */
expression(res)  ::= PLUS expression(e). { res = +e; }
expression(res)  ::= MINUS expression(e). { res = -e; }

/* The common stuff */
expression(res)  ::= term(t). { res = t; }
expression(res)  ::= expression(e1) PLUS term(t2). { res = e1+t2; }
expression(res)  ::= expression(e1) MINUS term(t2). { res = e1-t2; }

term(res)        ::= factor(f). { res = f; }
term(res)        ::= term(t1) MULTIPLICATION factor(f2). { res = t1*f2; }
term(res)        ::= term(t1) DIVISION factor(f2). { res = t1/f2; }

factor(res)      ::= NUMBER(n). { res = n; }
factor(res)      ::= OPENP expression(e) CLOSEP. { res = e; }

Generieren der eigentlichen Parser und Lexer

Damit Parser und Lexer generiert werden und Terme gefüttert werden können, sei hier ein kleines Beispiel gegeben:

<?php
// Create Parser
passthru('php /usr/share/php/PHP/ParserGenerator/cli.php TermParser.y');

// Create Lexer
require_once 'PHP/LexerGenerator.php';
$lex = new PHP_LexerGenerator('TermLexer.plex');

# Test
include_once("TermParser.php");
include_once("TermLexer.php");

$teststr='9*(8+6)-2/(-4)';

$lex = new TermLexer($teststr);
// $lex = new TermLexer(file_get_contents($lexerfile));

echo "-------------------------------\n";
echo " Parsing $teststr\n";
echo "-------------------------------\n";
$parser = new TermParser($lex);
while ($lex->yylex())  {
    echo "Parsing  {$lex->token} Token {$lex->value} \n";
    $parser->doParse($lex->token, $lex->value);
}
$parser->doParse(0, 0);

print "Returnvalue: ".$parser->retvalue."\n";
?>

Und nu?

Nun viel Spass… Ich bin auf der Suche nach der EBNF für RegExps, wenn die jemand hat :) …. Ihr könnt den ganzen Code hier auch downloaden…

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | 5 Komentare »