23.12. Verpacktes in PHP

Viele dürften sich heute Abend über die bunt verpackten Geschenke unter dem Baum freuen.
Um Verpacktes soll es auch hinter dem letzten Adventskalendertürchen gehen.
Genauer um die PHAR Erweiterung von PHP.

Was ist PHAR

Wer schon einmal Java Programme verwendet hat dürfte über sog. JAR Dateien gestolpert sein.
Dies sind Archive, ähnlich wie ZIP oder TAR, die ein ausführbares Programm enthalten, welches nicht erst entpackt werden muss, sondern dessen Inhalt sofort ausgeführt werden kann.
In PHP bestehen die meisten modernen Anwendungen aus vielen einzelnen Dateien, so dass man schnell mal den Überblick verlieren kann.
Also bietet es sich an, dieselben Möglichkeiten in PHP, zu nutzen und ebenfalls ausführbare Archive zu erschaffen.
Seit PHP 5.3 bietet PHP mit der PHAR Erweiterung, welche für PHP Archive steht, standardmäßig eine Lösung um solche ausführbaren Archive zu erstellen.

Eigenständige Anwendungen

Zu verwenden ist ein PHAR Archiv denkbar einfach.
Es kann wie eine normale PHP Datei auf der Konsole ausgeführt werden.
Zum experimentieren soll hier an dieser Stelle auf einem Blogbeitrag von letzter Woche verwiesen sein.
In diesem wurde das genutzte Tool, namens Behat, per PEAR Installiert.
Es gibt jedoch auch die Möglichkeit ein ausführbares Archiv von Behat herunter zu laden.
Dieses kann dann mit php pfad/zum/Archiv/behat.phar innerhalb des Zielverzeichnisses ausgeführt werden.

PHAR Dateien im eigenen Projekt verwenden

Hat man nun Beispielsweise ein Framework als PHAR Datei, so kann man dieses im eigenen Projekt jederzeit verwenden.
Dazu reicht es einfach das Archiv zu inkludieren.
Dies geschieht einfach per:

require_once(„framework.phar“);
set_include_path(„phar://framework.phar“);

Alternativ kann man auch einzelne Dateien direkt aus dem Archiv verwenden.
Dies geschieht unter Verwendung des PHAR Stream Wrappers:

require_once(„phar://framework.phar/foo.php“);

PHAR’s zurechtstutzen

PHP Archive können nur direkt inkludiert werden, bzw. als alleinstehende Anwendungen verwendet werden, wenn für das Archiv ein so genannter Stub registriert wurde.
Ein Stub implementiert dabei die __autoload Methode, welche Festlegt wie aus dem Archiv eine Datei geladen werden kann.
Gesetzt werden kann ein Stub jederzeit indem auf dem entsprechenden Archiv die setStub Methode aufgerufen wird.
Eine Beispielhafte Implementierung für einen Stub, der anhand des Namespaces auf den Ablageort der Datei schließt, könnte wie folgt aussehen:

<?php

function __autoload($name) {
	require_once './' . str_replace('\\',DIRECTORY_SEPARATOR,$name) . '.php';
}

Die eigene Anwendung packen

Nun möchte man eventuell nicht nur PHP Archive in der eigenen Anwendung nutzen, sondern diese selbst als PHAR zur Verfügung stellen.
Die simpelste Möglichkeit die eigene Anwendung zu verpacken ist es ein neues Archiv zu erstellen welches alle Dateien die im Anwendungsverzeichnis liegen enthält.
Dies geschieht indem der Methode buildFromDirectory das Verzeichnis übergeben wird in welchem die zu packende Anwendung zu finden ist.
Als optionaler Parameter kann hier ein regulärer Ausdruck angegeben werden um Dateien dem Archiv hinzuzufügen.
Neben der Möglichkeit eine Kompression zu verwenden, wobei hier die GZ, BZ2 und keine als Angaben möglich sind, kann auch ein defaultStub gesetzt werden.
Der Buildprozess für eine imaginäre Anwendung könnte also in etwa wie folgt aussehen:

$phar = new Phar('MyApp.phar');
$phar->buildFromDirectory('path/to/my/app','/\.php$/');
$phar->compressFiles( Phar::GZ);
$phar->stopBuffering();
$phar->setStub($phar->createDefaultStub('stub.php'));

Für eine genauere Auswahl, welche Dateien zum Archiv gehören sollen, kann das Archiv auch anhand eines Iterators, welcher das Iterator Interface implementiert, erstellt werden.
Hier könnte Beispielsweise per ArrayIterator eine Liste angegeben werden, welche ein Mapping zwischen Dateiname und Speicherort aufbaut.
Ebenfalls denkbar ist hier eine eigene Implementierung, die SplFileInfo Objekte zurück gibt.

Für das Erstellen des eigentlichen Archives wird dann einfach die Methode buildFromIterator verwendet, anstatt der buildFromDirectory Methode.

Go to the web

Da PHP aber klassischer Weise für Webanwendungen eingesetzt wird, stellt sich natürlich die Frage, ob auch hier mit PHAR Paketen gearbeitet werden kann.
Vorteil hierbei ist das einfache Deployment, da alle Installationsvorraussetzungen bereits im Buildschritt erfüllt werden können.
Um ein bestehendes PHAR Webfähig zu machen kann innerhalb des Stubs die Method webPhar auf dem bestehenden PHAR aufgerufen werden.
Dieser wird ein Alias übergeben, um bei Zugriffen auf das Archiv nicht den vollen Pfad angeben zu müssen, ein Index Verzeichnis und eine Datei, die eine 404 Fehlermeldung repräsentiert.
Zudem kann noch ein Array mit Mime Typen übergeben werden, die als Schlüssel die Endung enthält und als Wert den damit verknüpften Mime Type.
Als letzten Parameter kann man noch ein Array übergeben, welches per Key-Value Mapping eine einfache Art von Rewrite Rules zur Verfügung stellt.
Anschließend kann mit dem Aufruf von interceptFileFuncs sichergestellt werden dass alle Dateifunktionen, wie etwa das öffnen und schreiben in eine Log Datei innerhalb der Anwendung, im PHAR Paket ausgeführt werden.

Sicherheit

Eine weitere Stärke spielt eine einzelnes, ausführbares Archiv gegenüber vielen, verteilten Dateien im Hinblick auf die Sicherheit aus.
So finden sich Beispielsweise unter https://github.com/koto/phar-util entsprechende Skripte um das Archiv zu signieren und zu überprüfen ob die Signatur noch mit dem Archiv übereinstimmt.

Troubleshooting

Ein nützlicher Tipp zum Schluss bezieht sich auf ein Problem, welches mit installiertem Suoshin Patch auftritt.
Laut PHPMonkeys kann es hier passieren dass keine Ausgabe zu sehen ist.
Dies liegt an den Sicherheitseinstellungen von Suoshin und kann durch Eintragen der Endung .phar, ausführbare Datei, behoben werden.
Dies geschieht mit folgendem Eintrag in der php.ini:

suhosin.executor.include.whitelist = "phar"

Fazit

PHAR Pakete sind ein Thema, welches man als PHP Entwickler auf jeden Fall auf dem Schirm haben sollte.
Um so verwunderlicher erscheint es, dass kaum jemand diese Möglichkeit kennt und noch weniger Projekte sie nutzen.
Vielleicht konnte dieser Artikel ein wenig Licht in die Dunkelheit der PHP Archive bringen und somit dazu beitragen das mehr Projekte den Schritt wagen ihre Anwendung als ausführbares Paket anzubieten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert