Phar Tutorial – PHP Archive

Da ich mich heute endlich mal mit einem neuen Feature aus PHP 5.3 beschäftigen konnte, will ich meine Erkenntnisse Niemandem vorenthalten. Das neue Feature nennt sich Phar und ich bin mir sicher, dass wir davon noch viel in Zukunft hören werden. Jetzt gibt es aber erst einmal eine kleine Einführung.

Phar ist eine Zusammenführung der beiden Wörter PHP und Archiv und ähnelt dem .jar-Format von Java (jar = Java Archiv). In einem Phar-Archiv können prinzipiell alle Datei-Fromate verpackt werden und so lassen sich beispielsweise komplette PHP-Anwendungen in einer Datei archivieren, damit sie leichter verbreitet werden kann.
Ein neues Archiv lässt sich mit den folgenden Code-Zeilen anlegen:

$dir = __DIR__.'/';
$archive = new Phar($dir.'myphar.phar.gz');

Wir legen also ein neues Archiv mit dem Namen myphar.phar.gz an, das Gzip komprimiert ist (zu erkennen an der Endung .gz).
Die Phar-Klasse steht natürlich nur dann zur Verfügung, wenn die Extension auch in der php.ini aktiviert wurde.
Als nächstes müssen wir dem Archiv noch Daten zuführen. Dazu legen wir ein neues Verzeichnis test an mit einer Datei hello.php und dem Inhalt

echo 'hello world';

Jetzt können wir folgenden Befehl verwenden, um alle Daten aus dem Test-Verzeichnis in das Archiv zu speichern:

$archive->buildFromDirectory($dir.'test');
$archive->stopBuffering();

Mit der Methode stopBuffering() werden die Daten dann auf die Festplatte geschrieben.

Wir brauchen also nur knapp 4 Zeilen Code, um ein ganzes Archiv anzulegen, aber wie bekommt man die Dateien jetzt wieder heraus? Dazu gibt es mehrere Möglichkeiten. Welche man benutzt, hängt davon ab, was man mit dem Inhalt machen möchte. Zunächst öffnen wir das Phar-Archiv:

$dir = __DIR__.'/';
$archive = new Phar($dir.'myphar.phar.gz');

Jetzt können wir entweder den ganzen Inhalt zur Laufzeit auslesen, oder aber wieder in ein normales Verzeichnis entpacken.

// Alle Dateien auslesen
foreach($archive as $file)
{
	$ext = strrchr($file->getBasename(), '.');
	// PHP-Dateien können per include einfach eingebunden werden.
	if($ext == '.php')
	{
		include $file->getPathname();
	}
}
 
// Alle Dateien in ein normales Verzeichnis entpacken
$archive->extractTo($dir.'myphar');

Wenn man genau weiß, welche PHP-Datei man haben will, kann man diese auch direkt inkludieren, ohne das Archiv zu öffnen. Dazu muss man nur ein „phar://“ vor den Pfad stellen:

$dir = __DIR__.'/';
include 'phar://'.$dir.'myphar.phar.gz/hellp.php';

So, das sind erst eommal die Grundfunktionen, die man von Phar kennen sollte. Aber man merkt schnell, dass Phar ziemlich einfach zu handhaben ist, weil alles schön in Klassen verpackt ist.
Und noch ein letzter Tipp: Phar baut zum größten Teil auf die SPL Klassen SplFileInfo und DirectoryIterator. Die sollte man sich daher mal genauer anschauen.

SQL Injections Ade: PHP IDS

Nachdem mein Browsergame Yumee letzte Woche wahrscheinlich Opfer einer SQL-Injection geworden ist (fragt mich nicht wie das ging), habe ich kurzer Hand entschieden, die Library PHP IDS einzusetzen.

Kurz zu Einleitung: IDS kommen ursprünglich aus der Netzwerktechnik und überwachen dort Netzwerke auf mögliche Angriffe. Und genau das tut auch die PHP IDS. Wir lassen alle eingehenden Variablen (GET, POST und COOKIE), bevor die Anwendungen damit arbeitet, einmal überprüfen. Wenn die IDS nicht anschlägt, geht der Aufruf durch, ansonsten beenden wir Anwendung einfach.

PHP IDS hat neben seiner Hauptfunktion allerdings noch weitere Features. So lässt sich beispielsweise der HTML Pruifier anbinden, der eingehende Strings nach bösen HTML-Code durchsucht (z.B. <script> oder onclick). Außerdem gibt es verschiedene Logging-Möglichkeiten + automatische E-Mail-Benachrichtigung.
Einziger Nachteil: Es geht ein wenig Performance verloren, wobei das kaum zu merken ist.

Autoloading àla Zend Framework

Wer hat sich schonmal gefragt, warum alle Klassen des Zend Frameworks immer so lang sein müssen? Zend_View_Helper_Navigation_Breadcrumbs, Zend_Db_Adapter_Pdo_Mysql oder auch sehr schön ist Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive.
Die Antwort ist ganz einfach: So lässt sich genau nachvollziehen, wo sich die Klasse befindet, denn aus dem Namen lässt sich das genaue Verzeichnis der Klasse ablesen. So liegt die Klasse Zend_View_Helper_Navigation_Breadcrumbs beispielsweise unter Zend/View/Helper/Navigation/Breadcrumbs.php. Dieses Schema bringt nicht nur Struktur in das Klassen-Chaos einer Anwendung, sondern bietet auch eine hervorragende Möglichkeit, die Klassen automatisch zu laden, ohne dass die Klasse jedes mal mit require_once inkludiert werden müssen. Dazu bedienen wir uns einfach der Funktion __autoload, die jedes mal aufgerufen wird, wenn eine Klasse verwendet wurde, die nicht existiert.

function __autoload($className)
{
    $classFile = implode("/", explode("_", $className)).".php";
    require_once(DEIN_ROOT_PFAD.$classFile);
}
$obj = new Zend_Db_Adapter_Pdo_Mysql(); // Lädt automatisch die Datei Zend/Db/Adapter/Pdo/Mysql.php

Natürlich lässt sich das noch weiter ausbauen und mit verschiedenen Include-Paths verwenden.
Wenn ihr also das nächste mal eine neue Anwendung plant, macht es Sinn, dasselbe Namensschema wie das Zend Framework oder PEAR zu nutzen. So müsst ihr euch nicht mehr darum kümmern, ob eine Klasse wirklich mit require oder include eingebunden wurde und die Klassen-Datei wird nur noch dann geladen, wenn sie tatsächlich gebraucht wird.

Und noch ein Tipp:
Mit Hilfe der Funktion spl_autoload_register($callback) lassen sich mehrere Autoloader gleichzeitig verwenden und können als Callback auch in eigene Klasse ausgelagert werden.