Sonntag, 20. Juli 2008

entwickler.com Magazine Konferenzen Entwickler Akademie Entwickler-Forum Jobbörse Bücher
Software & Support Verlag





April 2006
aus PHP Magazin Ausgabe: 1.2006
Massenkompatibel
Neue PHP-Funktionen ohne neue PHP-Versionen
von Christian Wenz

Neue Versionen von PHP bieten in der Regel neue Möglichkeiten, doch gerade Massenhoster sind bei einem Upgrade häufig relativ zurückhaltend. Mit zwei PEAR-Paketen lassen sich Anwendungen mit relativ wenig Aufwand versionskompatibel erstellen.


Auf einem selbst kontrollierten System ist die Entwicklung immer am einfachsten. Man weiß genau was installiert ist, kann Software und auch Hardware verändern und schafft sich so seine Systemvoraussetzungen selbst. Das ist aber in der Softwareentwicklung nicht immer ein gangbarer Weg. Gerade im Open-Source-Bereich erhofft man sich häufig Bestätigung, aber auch Feedback, Hinweise und Unterstützung dadurch, dass die Programmierleistung frei verfügbar ist. Hat eine Software jedoch exotische Systemvoraussetzungen (etwa PHP 5.1 mit deutschen Regionaleinstellungen unter Windows 98), ist die Verwendung des Produkts schwieriger als sie sein müsste. Dadurch sinkt gleichzeitig die Größe der erreichbaren Zielgruppe - zumindest solange Hoster noch auf alten PHP-Versionen sprichwörtlich sitzen bleiben -, denn es bestehen Barrieren zum Einsatz (die Analogie zu Accessibility-Problemen ist durchaus zutreffend).
Eine an dieser Stelle beliebte Diskussion dreht sich darum, ob Software noch kompatibel zu PHP 4 sein soll oder schon auf Basis von PHP 5 arbeiten darf. Ein Denkansatz besteht darin, dass alle Dämme freigegeben werden sollen, sobald alle relevanten Distributionen PHP 5 als Standardversion anbieten. Häufiger jedoch wird argumentiert, dass PHP 5 erst dann wirklich Standard ist, wenn der tatsächliche Marktanteil der Version eine hohe Schwelle überschritten hat. Üblicherweise bedient man sich in der Softwaretechnik der letzteren Argumentation, auch wenn man gerechtfertigt anmerken könnte, dass ein signifikanter Marktanteil auf sich warten lassen könnte, wenn es nicht auch entsprechende Software dafür gibt.

Kompatibilitätsschicht
Generell sollte es aber ein Ziel sein, bei PHP-basierter Software die Zielgruppe möglichst groß zu halten. Es können jedoch Schwierigkeiten entstehen, wenn einige der praktischen neuen Features einer neuen PHP-Version genutzt werden. Deswegen ist es häufig notwendig, eine Kompatibilitätsschicht einzubauen. Ein gewisses Stück Arbeit, was an einem Beispiel gezeigt werden soll. Seit PHP 5.0 gibt es die Funktion file_put_contents() ein Gegenstück zu file_get_contents(), das übrigens mit PHP 4.3 eingeführt worden ist. Diese Funktion schreibt Daten in eine Datei, ohne mühsames Operieren mit fopen(), fwrite() und fclose(). Schön, wenn man PHP 5 einsetzt - doch unter PHP 4 gibt es diese Funktion noch nicht. Also ist eine kompatible Variante notwendig. Wenn jedoch auch seltener verwendete Funktionalität emuliert werden soll (etwa der dritte Parameter von file_put_contents()), wird das etwas umfangreicher und könnte abschließend etwa wie in Listing 1 aussehen.

Listing 1

function file_put_contents($filename, $content, $flags = null, $resource_context = null)
{
if (is_array($content)) {
$content = implode('', $content);
}
if (!is_scalar($content)) {
user_error('file_put_contents() The 2nd parameter should be either a string or an array',
E_USER_WARNING);
return false;
}
$length = strlen($content);
$mode = ($flags & FILE_APPEND) ? 'a' : 'w';
$use_inc_path = ($flags & FILE_USE_INCLUDE_PATH) ? true : false;
if (($fh = @fopen($filename, $mode, $use_inc_path)) === false) {
user_error('file_put_contents() failed to open stream: Permission denied',
E_USER_WARNING);
return false;
}
$bytes = 0;
if (($bytes = @fwrite($fh, $content)) === false) {
$errormsg = sprintf('file_put_contents() Failed to write %d bytes to %s',
$length,
$filename);
user_error($errormsg, E_USER_WARNING);
return false;
}
@fclose($fh);
if ($bytes != $length) {
$errormsg = sprintf('file_put_contents() Only %d of %d bytes written, possibly out of free disk space.',
$bytes,
$length);
user_error($errormsg, E_USER_WARNING);
return false;
}
return $bytes;
}

Ein recht großer Aufwand, doch glücklicherweise hat sich Aidan Lister die Mühe gemacht, diese und andere Funktionen für ältere PHP-Versionen zu emulieren. Das ganze steht in PEAR zur Verfügung.


Abb. 1: Installation von PHP_Compat

PEAR::PHP_Compat
Das PEAR-Paket PHP_Compat wird wie gehabt installiert: Entweder von der Paket-Homepage pear.php.net/package/PHP_Compat herunterladen, oder den PEAR-Installer verwenden (Abb. 1):

pear install PHP_Compat


Ein Blick in das Installationsverzeichnis fördert Erstaunliches zu Tage: Das Paket besteht aus etwa 75 Dateien. Ein Großteil davon wird von "kompatiblen" PHP-Funktionen belegt, denn pro Funktion gibt es eine einzelne Datei. Diese liegt im Unterverzeichnis Functions und ist stets wie folgt aufgebaut:

if (!function_exists('<Funktionsname>')) {
function <Funktionsname>
{
// ...
}
}


Das ermöglicht einen simplen und gleichzeitig auch statischen Einsatz des Pakets: Der folgende Code lädt die Datei file_put_contents.php und sorgt so dafür, dass die Funktion file_put_contents() auf jeden Fall zur Verfügung steht, auch vor PHP 5.

require_once 'PHP/Compat/Functions/file_put_contents.php';


Ähnlich funktioniert das bei Konstanten. So gibt es seit PHP 5 die neue Konstante E_STRICT, die intern den Wert 2048 aufweist (zum Vergleich: E_ALL hat den Wert 2047). Auch diese Konstante wird emuliert; in der Datei Constant/E_STRICT.php befindet sich folgender Code:

if (!defined('E_STRICT')) {
define('E_STRICT', 2048);
}


Geladen wird diese Konstante mit der statischen Methode loadConstant() oder durch Einbindung der entsprechenden externen Datei:

PHP_Compat::loadConstant('E_STRICT');
// alternativ:
// require_once 'PHP/Compat/Constant/E_STRICT.php';


Wenn mehrere Konstanten auf einmal geladen werden sollen, ist es ebenfalls möglich, ein Array an loadConstant() zu übergeben:

PHP_Compat::loadConstant(array('E_STRICT', 'DIRECTORY_SEPARATOR'));


Ein ähnliches Vorgehen - also das statische Laden von PHP-Code der Klasse PHP_Compat - gibt es auch für Funktionen. Die entsprechende Methode heißt loadFunction() und akzeptiert als Parameter sowohl einen Namen als auch ein Array aus mehreren Namen:

PHP_Compat::loadFunction('file_put_contents');
PHP_Compat::loadFunction(array('file_get_contents', 'fputcsv'));


Das ist aber natürlich nicht immer der optimale Ansatz, denn dafür würde es nicht eines PEAR-Pakets bedürfen, sondern vermutlich einer Google-Suche oder etwas Eigeninitiative (wenngleich es gut möglich ist, dass man eine Funktion nicht komplett kompatibel abbildet). Das PEAR-Paket bietet jedoch noch einige zusätzliche Features, die den Einsatz erleichtern und auch teilweise automatisieren können.
Am interessantesten dabei ist wohl die Möglichkeit, die die Methode loadVersion() bietet. Hier ist es möglich, eine Ziel-PHP-Version anzugeben. Die Methode loadVersion() vergleicht dann diese Zielversion mit der tatsächlich vorhandenen PHP-Version und lädt dynamisch alle zwischenzeitlich hinzugefügten PHP-Funktionen, sofern sie in PHP_Compat emuliert werden. Der Rückgabewert der Methode ist ein Array mit allen geladenen Funktionen und Konstanten, die durch das Skript geladen wurden (Abb. 2). So ist eine einfache "Erfolgskontrolle" möglich. Wird übrigens keine Versionsnummer angegeben, lädt PHP alle neuen Funktionalitäten.

<?php
require_once 'PHP/Compat.php';
$compats = PHP_Compat::loadVersion('5.1.0');
print_r($compats);
?>


Abb. 2: Von PHP 4.x auf 5.1 - zumindest fast

Fazit
Wie immer: PHP und gerade PEAR bietet einige Feinheiten, die man gar nicht erwartet hätte. Für den Einsatz innerhalb eines kontrollierten Umgebung mag PHP_Compat nicht immer sinnvoll sein, aber für Software, die auf möglichst vielen Systemen laufen soll, bietet das Paket viel nützlichen Code. Zaubern kann es allerdings nicht. Werfen Sie dazu einfach noch einmal auf den eingangs gezeigten Emulationscode für die Funktion file_put_contents(). Dieser ist direkt aus dem Code von PHP_Compat entnommen. Wie Sie sehen, wird der Parameter $resource_context nicht verwendet. Gleiches gilt für E_STRICT: Die Konstante existiert dank der Verwendung von PHP_Compat, aber der Fehlerlevel E_STRICT steht trotzdem erst in PHP 5 zur Verfügung.
In der nächsten Ausgabe lernen Sie ein weiteres PEAR-Paket kennen, das ebenfalls Kompatibilitätsinformationen bietet - und das mehr oder minder automatisch.
Christian Wenz ist Autor, Trainer und Berater mit Schwerpunkt Webtechnologien. In seiner Serie "x ohne x" zeigt er regelmäßig, wie PHP ungeahnte Fähigkeiten entwickeln kann. Kompatibler PHP-Code liegt ihm sehr an Herzen, weswegen die Screenshots im PHP Phrasebook (Sams) auf vier verschiedenen Betriebssystemen entstanden. Sein Blog unter www.hauser-wenz.de/blog/ sollte ebenfalls systemunabhängig funktionieren.


    Hat Ihnen dieser Artikel gefallen? Dann abonnieren Sie das PHP Magazin direkt über unser

zur vorherigen Seite
zurück
an den Anfang der Seite
nach oben
Diesen Artikel drucken
drucken
Diesen Artikel weiterempfehlen
empfehlen

Software & Support Verlag GmbH