3-11 edit_cd_news_inhalt.indd - Institut für Informatik Augsburg

Werbung
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
Herunterladen