In PHP geschriebene Templatesprachen sind eine Sackgasse der Evolution

Wer sich mit PHP beschäftigt wird früher oder später auf Personen treffen, die Befürworter von sogenannten Templatesystemen sind (z.B. Smarty, Fluid, uvm.). Es gibt auch viele Personen, die eigene Templateengines schreiben. Nahezu jedes in PHP geschriebene Content Management System (CMS) verfügt über eine eigene Implementierung oder nutzt ein Templatesystem.

Es will mit einfach nicht in den Kopf, warum die das machen. Ich meine PHP ist eine Templatesprache. Warum zur Hölle sollte man eine Templatesprache in einer Templatesprache schreiben? OK, man versucht eine Trennung zwischen Businesslogik und Darstellung zu erreichen, und behauptet dann auch noch das Model-View-Controller Pattern zu verwenden, was ja State-Of-The-Art ist. (Wer das nicht benutzt ist ein Höhlenmensch, der seit dem Altpaläolithikum jeglichen Fortschritt verschlafen hat!)

Merken diese Leute denn nicht, was für einen riesigen Ballast sie sich damit an Board holen? Man muss nämlich eine weitere Sprache lernen, die in den meisten Fällen zwar an PHP angelehnt ist, aber gerade für Schleifen und If-Then-Else Konstrukte meist unbenutzbar ist. Die Templates werden mit Hilfe von PHP geparst, um dann mühsam wieder HTML-Code daraus zu generieren. Das ist langsam und ineffizient. PHP kann das seit Version 1.0 und macht es zuverlässig und schnell. Um Logik und Darstellung zu trennen braucht es nicht einer künstlichen Barriere, die jedesmal mit viel Aufwand überwunden werden muss, sondern einfach nur etwas Disziplin beim Programmieren. PHP kennt nämlich so tolle Konzepte wie Funkionen und Klassen. Damit kann man ganz prima verschiedene Codebereiche voneinander trennen.

“Aber die Sicherheit”, schreien dann einige, “mit PHP kann ich ja alles machen. Bei einer eigenen Templatesprache kann ich mir sicher sein, dass ein Template keinen Unfug anstellen kann.” Zugegeben, das klingt auf den ersten Blick einleuchtend. Wenn ich eine eigene Templatesprache habe, die nur Variablen ausgeben, aber keine beliebigen Befehle ausführen kann, dann reisst mir das keine Sicherheitslöcher in die Applikation.

Aber das ist nur die halbe Wahrheit. Nahezu alle CMSs können per Plug-In (Add-On, Extension, …) erweitert werden. Diese sind sowieso in PHP geschrieben und können beliebigen Scheiß anstellen. Gelten die Sicherheitsbedenken dafür denn nicht? Und mal ganz ehrlich: Alle Templates die ich bislang genutzt habe waren entweder die Beispieltemplates der Applikation, oder ich hab sie selbst geschrieben. Und wenn ich die Dinger selbst schreibe, dann bin ich ja auch selbst dafür verantwortlich, oder?

Ich hab jetzt viel geschrieben. Manchmal hilft es einen Standpunkt nieder zu machen, um ihn besser zu verstehen. Aber in diesem Fall bin ich überzeugter denn je, dass in PHP geschriebene Templatesprachen unsinnig sind. Eine Sackgasse der Evolution, sozusagen.


JavaScript Dateien effizient übertragen mit GZIP, PHP und Apache

Wenn man diese schönen JavaScript Bibliotheken, wie z.B. jQuery, nutzt, dann steht man schnell vor dem Problem, dass man ein Dutzend JavaScript Dateien laden muss, um die Seite korrekt angezeigt zu bekommen. Wenn man dann den Webserver falsch konfiguriert hat werden die Dateien alle unkomprimiert übertragen. Ausserdem muss der Browser für jede Datei den Webserver fragen. Und wenn man dann auch noch das Caching falsch konfiguriert hat, dann werden für jeden Seitenaufbau alle JS Dateien neu geholt.

Ob man sein System richtig Konfiguriert hat lässt sich sehr schön mit dem Firefox Plugin Firebug beobachten. Wie das geht bleibt dem geneigten Leser als Übung überlassen, denn mir geht es hier hauptsächlich um die Lösung dieses Problems.

Nach Monaten intensiver Recherchen und langen Versuchsreihen bin ich auf folgende Lösung gestoßen: Anstatt die JS Dateien direkt herunterzuladen, kopiere ich einfach den Inhalt von allen Dateien on-the-fly in eine Große. Diese wird dann hübsch komprimiert und mit den notwendigen HTTP Headern ausgestattet, damit das Caching auch funktioniert. Irgendwann hat sich folgendes PHP Script als optimal herauskristallisiert:

<?php 
// License: none (public domain) 
/*
 * Dieses Script concateniert mehrere Dateien und schickt sie gzip
 * komprimiert an den Browser. 
 * Ausserdem wird ein Etag gesetzt, der nötig ist, um das Cachen der 
 * Javascript Datei zu ermöglichen. Der Etag ändert sich jedesmal, wenn 
 * eine der Dateien geändert wird, oder eine hinzu-/weggenommen wird. 
 */ 
header('Content-Type: text/javascript');
header("Expires: ".gmdate("D, d M Y H:i:s", time() + 3600*24)." GMT");

/*
 * Hier die JavaScript Dateien angeben:
 */
$files = array(
    "jquery-1.4.2.min.js",
    "ui.core.js",
    "ui.dialog.js",
    "ui.draggable.js",
    "ui.resizable.js",
    "jquery.textarearesizer.compressed.js"
);

$md5 = '';
foreach ($files as $file)
{
    $md5 .= md5_file($file);
}

$etag = md5($md5);
header("Etag: $etag");

if ( isset($_SERVER['HTTP_IF_NONE_MATCH'])
  && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag)
{
    header('HTTP/1.1 304 Not Modified');
}
else
{
    if ( isset($_SERVER['HTTP_ACCEPT_ENCODING'])
      && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], "gzip") !== false )
    {
        ob_start("ob_gzhandler");
    }
    else
    {
        ob_start();
    }

    foreach ($files as $file)
    {
        echo file_get_contents($file);
    }
}
?>

Vorgehensweise:

Lege eine Datei ‘js.php‘ in dem Ordner mit den JavaScript-Dateien an. Kopiere obigen Code in die Datei. Ändere das $files Array so, dass deine JavaScript-Dateien da drin stehen. Füge folgenden Script-Tag in deinen HTML-Code ein:

<script src="/path/js.php" type="text/javascript"></script>

Das ganze funktioniert natürlich auch mit CSS-Dateien, wobei man dann den Content-Type auf text/css ändern muss.


PHPs fmod() und die Rundungsfehler

Die fmod() Funktion von PHP arbeitet nicht immer so wie man das erwartet. Bzw. sie arbeitet nicht immer so wie Laien das erwarten. Beispielsweise liefert fmod(1.9, 0.1) den Wert 0.1 zurück, obwohl 0.0 richtig wäre. Der Grund ist, dass Fließkommazahlen immer nur Näherungen sind und dass PHP bei manchen Ausgaben lügt.

Man kann ja mal spaßeshalber 1.9 / 0.1 von Python berechnen lassen und man erhält 18.999999999999996. Das ist sehr wahrscheinlich auch der Wert mit dem PHP intern rechnet.

Aber lustigerweise liefert PHP bei der der Rechnung 1.9 / 0.1 die korrekte 19 zurück. 19*0.1 ergibt ebenfalls die erwartete 1.9. Aber 1.9 – 19*0.1 liefert -2.2204460492503E-16 zurück.

Wie man mit diesen Rundungsfehlern umzugehen hat ist sicherlich Einzelfallabhängig. Ich benutze aktuell eine Version, die das Zwischen- und das Endergebnis rundet. Da ich nur mit Zahlen im Bereich 0.01 – 100 arbeite, müsste das ganz passabel sein.

<?php
function fmod($a, $b, $precision = 2)
{
    $tmp = round( $a / $b, $precision);
    return  round($a - $b*floor($tmp), $precision);
}
?>

php-Array to JS-Array

Manchmal benötigt man Daten, die man in PHP hat, auch noch in Java-Script. Natürlich könnte man die Daten per Ajax nachladen, aber das ist nicht immer sinnvoll. Vielleicht ist das ja ein einfaches Array, für das sich der Aufwand schlicht nicht lohnt.

$data = array(
    "foo" => "bar",
    1 => 2,
    3 => array(
        "Hallo" => "Welt!"
    )
);
print phpArrayToJsArray($data, "D");

function phpArrayToJsArray($phpArray, $varPrefix)
{
	$rtn = $varPrefix.' = new Array();'."\n";
	foreach($phpArray as $key => $value)
	{
		if ( is_array($value) ) 
		{
			$rtn .= phpArrayToJsArray($value, "{$varPrefix}['$key']");
		}
		elseif (is_string($value))
		{
			$rtn .= "{$varPrefix}['$key'] = '".addslashes($value)."';\n";
		}
		else
		{
			$rtn .= "{$varPrefix}['$key'] = $value;\n";
		}
	}
	return $rtn;
}

Ausgabe ist:

D['foo'] = 'bar';
D['1'] = 2;
D['3'] = new Array();
D['3']['Hallo'] = 'Welt!';

Follow

Bekomme jeden neuen Artikel in deinen Posteingang.