PHP4Mono Open.NET Annäherungsversuche Ein erster Blick auf PHP4Mono von Raphael Romeikat Seit kurzem gibt es einen PHP-Compiler für die .NET-Plattform. Das Projekt heißt PHP4Mono und ermöglicht es, Anwendungen für das Microsoft .NET Framework oder die kompatible Entwicklungsplattform Mono in der Sprache PHP zu entwickeln. Dieser Artikel erläutert die Funktionsweise und Anwendungsmöglichkeiten des Compilers und enthält viele Hintergrundinformationen über die Sprache PHP und das Open-Source-Projekt Mono. Haben Sie schon einmal mit dem Gedanken gespielt, eine Anwendung in PHP zu programmieren? Eine, die auf .NET läuft und sogar eine grafische Oberfläche bietet? Bislang war dies eine absurde Idee und nicht zu realisieren. Nun aber gibt es ein Projekt, mit dem sich das Blatt wendet. Es nennt sich PHP4Mono (PHP for Mono) und ermöglicht es, PHP-Skripte in .NET-Assemblies zu übersetzen und diese nicht nur auf Windows einzusetzen. Damit keine Missverständnisse entstehen, im Folgenden geht es nicht um die Programmierung von Webanwendungen, für die PHP ja sehr beliebt ist, und daher auch nicht um eine Alternative zu ASP.NET. Es geht um eine Umsetzung der Skriptsprache PHP für Mono. Nach dem dieses mögliche Missverständnis ausgeräumt wurde, können Sie sich, auf eine PHP-Programmiersprache für .NET einlassen. Syntax und Semantik von PHP sind durch die Programmiersprachen C, Perl und Java geprägt. PHP-Skripte bestehen grundsätzlich aus Statements und kurz & bündig Inhalt Ein erster Überblick über PHP4Mono, dem neuen PHP-Entwicklungssystem für .NET Zusammenfassung PHP4Mono macht die Skriptsprache PHP unter Mono verfügbar. Die resultierenden Anwendungen sind allerdings reguläre Assemblies, z.B. Konsolen- aber auch Windows-Anwendungen. Es geht bei PHP4Mono also nicht um die typische Webprogrammierung, für die PHP normalerweise eingesetzt wird www.dotnet-magazin.de Ausdrücken, die in Funktionen und Klassen zusammengefasst werden können, aber nicht müssen. Variablen, Typen und Kontrollstrukturen lassen sich wie auch alle weiteren Sprachelemente intuitiv verstehen und einsetzen. Eine große Stärke besteht darin, dass eine Vielzahl von Erweiterungen für PHP frei verfügbar ist – APIs, die den Zugriff auf Datenbanken ermöglichen oder auch das Verarbeiten von XML-Dokumenten, dynamische Bildbearbeitung und vieles mehr. Eine aktuelle Statistik belegt, dass PHP weltweit auf über 20 Millionen Domains eingesetzt wird [4] und somit die beliebteste Skriptsprache (im Web) darstellt. Eine lange Zeit wollte PHP allerdings nichts von objektorientierten Konzepten wie Klassen, Interfaces, Vererbung etc. wissen. In Version 4 waren solche Konzepte ansatzweise enthalten, allerdings nur als „Syntactic Sugar“. Die aktuelle Version 5 beinhaltet ein völlig überarbeitetes Objektmodell und echte Objektorientierung, die ihren Namen auch verdient hat. Effektive Kapselung und Wiederverwendung sind für PHP daher kein Problem mehr. Nun, da sich PHP zu einer anspruchsvollen Sprache entwickelt hat, ist es an der Zeit, dass die .NET-Community davon profitiert. In diesem Sinne entstand das Projekt PHP4Mono. Ziel ist, PHP als weitere Programmiersprache für die .NETPlattform verfügbar zu machen. Dadurch können Sie PHP nicht mehr nur als Skriptsprache für Webseiten einsetzen, sondern ebenso als vollwertige Programmiersprache für die Entwicklung eigenständiger Anwendungen. Hierfür hat der Projekt- gründer Raphael Romeikat einen Compiler entwickelt, der PHP-Skripte ohne Umwege in .NET-Assemblies übersetzt. Diese können als eigenständige Anwendungen ausgeführt oder auch als Bibliotheken für andere .NET-Anwendungen eingesetzt werden. .NET ist mehr als nur Windows PHP4Mono entstand zwar im MonoUmfeld, es bedeutet aber nicht, dass der PHP-Compiler oder die damit kompilierten Skripte nur auf Mono und nicht auf dem .NET Framework laufen. Denn Mono ist eine weitere Implementierung des inzwischen standardisierten [5] .NET Framework. Der Unterschied besteht darin, dass Mono nicht nur für das Betriebssystem Windows verfügbar ist, sondern unter anderem auch für Linux, MacOS X, Solaris und BSD. Damit können Sie .NETBibliotheken plattformübergreifend entwickeln und einsetzen (Mono bietet sogar eine .NET Runtime für das neue Nokia 770 WiFi-Internet Tablet an). Ein kleines Beispiel Mit dem PHP-Compiler können Sie beispielsweise ein PHP-Skript in eine .NETAssembly übersetzen und so die in PHP programmierten Klassen in einem C#Programm verwenden. Um das folgende Beispiel nachzuvollziehen, benötigen Sie neben dem .NET Framework lediglich den PHP-Compiler in der aktuellen Version 0.2. Er steht auf der Website des Projekts PHP4Mono zum Download zur Verfügung. Listing 1 zeigt ein PHP-Skript, das eine Klasse Circle definiert. Diese soll 6.06 99 100 Open.NET PHP4Mono Abb. 1: Die einzelnen Phasen beim Kompilieren einen geometrischen Kreis repräsentieren. In der Klasse Circle ist die Konstante Pi definiert, außerdem ein Attribut für den Kreisradius, ein passender Konstruktor und eine Methode für die Berechnung der Kreisfläche. Mit dem passenden Aufruf an den PHP-Compiler erzeugen Sie daraus eine .NET-Assembly. Der entsprechende Aufruf lautet: mPHP /t:library Circle.php Dabei ist mPHP.exe der Dateiname des Compilers und Circle.php der Name der Eingabedatei. Die die Option /t:library sorgt für die Speicherung des übersetzen Skripts als Bibliothek Circle.dll. Diese können Sie nun für jedes andere .NETProgramm verwenden. So greift das Programm Geometry.cs (Listing 2) auf die eben definierte und kompilierte Circle-Klasse zu. Es erzeugt einen Kreis mit Radius 4 berechnet dessen Flächeninhalt mithilfe der in PHP programmierten Methode area und gibt das Ergebnis auf der Konsole aus. Das kurze C#-Programm aus Listing 2 kompilieren Sie mit dem Aufruf an den C#-Compiler csc.exe wie folgt: ter die Kulissen nötig. Der PHP-Compiler besteht aus zwei Komponenten, einem Frontend und einem Backend. Grundsätzliche Aufgabe des Frontends ist, das eingegebene PHP-Skript zu analysieren, im Gegensatz dazu erledigt das Backend die Codeerzeugung in die Zielsprache CIL. Das Frontend liest das Eingabeskript über einen Scanner ein und teilt es in seine Bausteile auf, die sog. Tokens. Jede Variable, jede Zahl, jeder Klassen- oder Funktionsname, jedes Semikolon und jedes sonstige Schlüsselwort ist ein solches Token. Der entstehende Strom von Tokens wird zugleich vom Parser in eine Baumstruktur überführt, entsprechend den grammatischen Regeln von PHP. Das Ergebnis ist der abstrakte Syntaxbaum (AST), eine vollständige strukturierte Darstellung des anfangs eingegebenen PHP-Skripts. Gelingt es, den AST vollständig aufzubauen, so ist das Eingabeskript syntaktisch korrekt. Ansonsten bricht die Kompilierung ab, und es wird eine entsprechende Fehlermeldung ausgegeben, die darüber informiert, an welcher Stelle es im Eingabeskript hakt. Das Listing 3 Die disassemblierte Assembly Circle.dll .assembly extern mscorlib {} Listing 1 Circle.php <?php class Circle { const pi = 3.14159265; public $radius; public function __construct($radius) { $this->radius = $radius; } public function area() { return 0.5 * Circle::pi * $this->radius * $this->radius; } } ?> Listing 2 Geometry.cs csc /r:Circle.dll,mPHPRuntime.dll Geometry.cs .assembly extern mPHPRuntime {} .assembly Circle {} Wichtig sind die mit dem Parameter /r angegebenen Referenzen. Die Bibliothek Circle.dll wird benötigt, da sie die eben definierte Klasse Circle enthält, die Bibliothek mPHPRuntime.dll, da sie Klassen und Methoden enthält, die übersetzte PHP-Skripte benötigen, so z.B. die abstrakte Klasse PHP.Object. Letztere ist die Superklasse aller in PHP programmierten Klassen, also ist auch die Circle-Klasse eine Unterklasse von PHP.Object. Der C#Compiler gibt schließlich die ausführbare Datei Geometry.exe aus. Beim Ausführen dieser Datei können Sie die erwartete Ausgabe auf der Konsole sehen: .module Circle .class public Circle extends PHP.Object { .field public static initonly object pi .field public object radius .method public instance void .ctor(object) { ... } .method public static void .cctor(object) { ... } .method public instance object area() { .maxstack 2 ldc.r8 0.5 box [mscorlib]System.Double ldsfld object Circle::pi call object PHP.Runtime.Operators::Times (object, object) ldarg.0 ldfld object Circle::radius call object PHP.Runtime.Operators::Times(object, object) using System; ldarg.0 Raduis of c is 25,1327412 using PHP; ldfld object Circle::radius class Geometry { public static void Main(string[] args) { Hinter den Kulissen Circle c = new Circle(4); Was passiert genau beim Kompilieren und wie funktioniert die Übersetzung von PHP nach CIL? Um diese interessanten Fragen zu beantworten, ist ein Blick hin- Console.WriteLine(“Radius of c is {0}”, c.area()); } } 6.06 call object PHP.Runtime.Operators::Times(object, object) ret } } www.dotnet-magazin.de PHP4Mono Frontend führt auch Prüfungen zur Semantik des Skripts durch und meldet Fehler, falls darin welche auftreten. Sollte beispielsweise ein Interface implementiert werden, dabei aber nicht alle seine Methoden, so widerspricht dies zwar nicht der Syntax von PHP, wohl aber der semantischen Korrektheit. Dies hat zur Folge, dass das Eingabeskript mit einer entsprechenden Fehlermeldung abgelehnt wird. Nachdem dann das Eingabeskript das Frontend erfolgreich durchlaufen hat, wird der erzeugte AST an das Backend weitergegeben. Das Backend führt soweit möglich Optimierungen am AST durch und reduziert so dessen Größe. Beispielsweise wird Code abgeschnitten, der hinter einer return-Anweisung steht, da solcher Code nie zur Ausführung kommen wird. Der optimierte AST wird an den Translator des Backends übergeben, der nun Schritt für Schritt die Sprachbausteine des Eingabeskripts in Anweisungen der Zielsprache CIL übersetzt. Schließlich wird der erzeugte Zielcode in Form einer .NETAssemby gespeichert, je nach Wunsch als ausführbare Datei mit der Endung .exe oder als Programmbibliothek mit der Endung .dll. Eine grafische Übersicht über die einzelnen Phasen beim Kompilieren finden Sie in Abbildung 1. Nach dem Kompilieren lohnt sich ein genauerer Blick auf den erzeugten CILCode. .NET-Assemblies liegen zunächst in einer Form vor, die nur für die CLR lesbar ist und lassen sich daher nicht einfach mit einem Texteditor öffnen. Eine für den Menschen textuell lesbare Open.NET Form lässt sich dennoch erzeugen. Hier hilft der Disassembler ildasm.exe, der im .NET Framework SDK enthalten ist. Der Aufruf ildasm /out=Circle.txt Circle.dll an den Disassembler schreibt eine texutell lesbare Form der vorhin erzeugten Assembly Circle.dll in die Textdatei Circle .txt, die Sie nun mit einem Texteditor öffnen können. Ein Auszug ist in Listing 3 zu sehen. An erster Stelle finden sich hier Verweise auf externe Assemblies. Sie werden durch das Schlüsselwort .assembly extern eingeleitet und verweisen auf alle Bibliotheken, deren Klassen oder Methoden an irgendeiner Stelle verwendet werden. Im Beispiel sind dies die obligatorische Bibliothek mscorlib, die die generische Superklasse object enthält, und die Bibliothek mPHPRuntime, deren Klasse PHP.Object und die Methode Times weiter unten im CIL-Code verwendet werden. Darauf folgt der Name der aktuellen Assembly Circle und des enthaltenen Moduls Circle. Nun erst taucht die Klasse Circle auf, die ja zuvor in PHP programmiert wurde. Innerhalb dieser Klasse erscheinen zuerst die enthaltenen Attribute pi und radius. In CIL werden Attribute Felder genannt und sind mit dem Schlüsselwort .field gekennzeichnet. Darauf folgen alle Methoden der Klasse, eingeleitet durch das Schlüsselwort .method. Ein nicht-statischer Konstruktor wird in CIL immer .ctor genannt, ein statischer wird stets mit .cctor bezeichnet. In den beiden Konstruk- Anzeige toren werden die beiden Attribute pi und radius der Klasse Circle initialisiert. Die entsprechenden Befehle innerhalb der Konstruktoren sind aus Platzgründen hier nicht abgedruckt. Nun folgt die Methode area, die den Flächeninhalt des Kreises berechnet. Die .maxstack-Anweisung innerhalb der Methode gibt die maximale Größe des Stacks an, der bei Ausführung der Methode erreicht wird. In diesem Fall liegen also zu jedem Zeitpunkt während des Methodenablaufs höchstens zwei Elemente auf dem Stack. Nun folgen die übersetzten PHP-Statements zur Flächenberechnung. Zuerst wird die Konstante 0.5 vom Datentyp System.Double auf den Stack geladen, darauf dann der Inhalt des Attributs pi. Diese beiden Elemente werden durch den Aufruf der Times-Methode vom Stack heruntergenommen, multipliziert und das Zwischenergebnis wiederum auf den Stack gelegt. Nun wird der im Attrubit radius gespeicherte Wert auf den Stack geladen und über einen weiteren Aufruf der Methode Times mit dem Zwischenergebnis von eben zu einem neuen Zwischenergebnis verarbeitet. Dieses wird schließlich ein weiteres Mal mit dem Radius des Kreises multipliziert. Somit liegt das Endergebnis der Flächenberechnung auf dem Stack und wird mithilfe der ret-Anweisung an den Aufrufer der areaMethode zurückgegeben. .NET-GUIs mit PHP Ein besondereres Feature des PHP-Compilers ist die Möglichkeit, andere .NETAssemblies direkt aus einem PHP-Skript heraus anzusprechen. Indem Sie auf diese 101 102 Open.NET PHP4Mono ben. Der Compiler erzeugt nun die Datei Button.exe, die beim Ausführen das eben programmierte Fenster anzeigt. Bei jedem Klick auf den Button wird nun der Text „Hello, World!“ auf die Konsole geschrieben (Abbildung 2). Förderung durch den Summer of Code von Google Abb. 2: Kompilieren und Ausführen des Button-Programms Weise grafische Bibliotheken wie System.Windows.Forms einsetzen, können Sie auch grafische Oberflächen direkt in PHP programmieren. Um das folgende Beispiel nachzuvollziehen, benötigen Sie wiederum das .NET Framework und den PHP-Compiler. Listing 4 enthält ein PHPSkript, in dem eine grafische Oberfläche programmiert wird. Sie besteht aus einem Fenster, darin enthalten ist ein Button, der bei jedem Klick einen Text auf der Konsole ausgibt. Ein einfaches Beispiel, das aber die grundsätzliche Funktionsweise veranschaulicht. Zuerst sind in C#-Manier die verwendeten Namespaces deklariert. Dabei wird der Doppelpunkt als Trennzeichen verwendet, wie auch bei allen anderen statischen Referenzen in PHP. Anstelle System.Windows.Forms heißt es also System::Windows::Forms. Nun werden ein Form- und ein Button-Objekt erzeugt Listing 4 Button.php <?php using System; using System::Windows::Forms; using System::Drawing; $f = new Form(); $f->Text = “Button“; $f->Size = new Size(208, 134); $b = new Button(); $b->Text = “Hit me!“; $b->Size = new Size(200, 100); $f->Controls->Add($b); add_event($b, “Click“, new EventHandler(null, print_hello_world)); Application::Run($f); function print_hello_world($source, $click_count) { echo(“Hello, World!\n“); und bei beiden Beschriftung und Größe gesetzt. Darauf wird der Button zum Form hinzugefügt und auf das Click-Ereignis des Buttons wird mit add_event ein entsprechender EventHandler gesetzt. Dieser ruft bei jedem Klick die Funktion PrintHelloWorld auf, die auf der Konsole den Text „Hello,World!“ ausgibt. Schließlich wird das Fenster durch den Aufruf der Run-Methode angezeigt. Bei diesem Skript fällt auf, dass die PHP-Statements einfach auf der obersten Ebene stehen, ohne sich in einer Klasse oder Methode zu befinden. Dies ist typischer PHP-Stil und zeigt den Ursprung als Skriptsprache. Auf dem .NET Framework ist das natürlich nicht erlaubt und so erzeugt der PHPCompiler beim Übersetzen eine Klasse __MAIN und darin eine statische Methode __MAIN, die den Einstiegspunkt der kompilierten Anwendung enthält und danach alle übersetzten Statements, die im PHP-Skript, wie beschrieben, auf der obersten Ebene stehen. Sie kompilieren das Skript diesmal ohne den Parameter /t:library, denn es soll ja keine Programmbibliothek, sondern eine ausführbare Anwendung erzeugt werden. Stattdessen verweisen Sie auf die beiden Bibliotheken System.Windows.Forms .dll und System.Windows.Drawing.dll, da diese die im PHP-Skript verwendeten .NET-Klassen Form, Button, Size und EventHandler enthalten. Falls die beteiligten Dateien System.Windows.Forms .dll, System.Windows.Drawing.dll und Button.php im gleichen Verzeichnis wie der Compiler liegen, lautet der Aufruf zum Kompilieren wie folgt: mPHP /r:System.Windows.Forms.dll,System.Drawing. dll Button.php } Ansonsten verwenden Sie anstelle der einfachen Dateinamen absolute Pfadanga- ?> 6.06 PHP4Mono ist ein Open-Source-Projekt, wird von SourceForge gehostet und steht unter der GNU General Public License [6]. Es begann mit einer Diplomarbeit an der Universität Augsburg bei Prof. Bauer, Lehrstuhl für Softwaretechnik und Programmiersprachen [7] in Zusammenarbeit mit der Linux Solutions Group [8]. Im Jahr 2005 wurde das Projekt von Googles Open-Source-Initiative Summer of Code [9] gefördert. Mit dieser Initiative honorierte der Suchmaschinenanbieter ausgewählte Open-Source-Projekte aus aller Welt. PHP4Mono steckt schon nicht mehr in den Kinderschuhen, mittlerweile ist bereits die zweite Version des Compilers fertiggestellt und verzeichnet seither zahlreiche Downloads. Die meisten wichtigen Sprachkonstrukte von PHP werden bereits unterstützt und die noch bestehenden Lücken nach und nach geschlossen. Mehr Informationen, ein Tutorial und eine Auflistung der bereits realisierten und noch geplanten Features finden Sie auf der Webseite von PHP4Mono [1]. Raphael Romeikat arbeitet seit 2006 als wissenschaftlicher Mitarbeiter am Lehrstuhl für Softwaretechnik und Programmiersprachen an der Uni Augsburg. Seine Schwerpunkte sind Programmierung verteilter Systeme, Compilerbau und Grid Computing. Sie erreichen Ihn unter romeikat@ informatik.uni-augsburg.de. Links & Literatur [1] PHP4Mono: php4mono.sourceforge.net [2] Mono Project: www.mono-project.com [3] PHP: www.php.net [4] PHP Usage Stats: www.php.net/usage/ [5] IDO/IEC 23271 – Connon Language Infrastructure: www.iso.org/iso/en/ CatalogueDetailPage.CatalogueDetail? CSNUMBER=36769 [6] de.wikipedia.org/wiki/GNU_General_Public_ License/ [7] www.informatik.uni-augsburg.de/lehrstuehle/ swt/vs/ [8] Linux Solutions Group (LiSoG): www.lisog.org [9] code.google.com/summerofcode.html www.dotnet-magazin.de