Ausgangssituation

Vor kurzem hatten wir in  der Firma das Problem, dass auf der Budgetmap unseres Portals ein String, bestehend aus Leerzeichen und einem Ampersand, einen AJAX-Request und die weiterführende Verarbeitung via PHP nicht überstand. Unsere Frontend-Entwickler saßen somit vor ihren Bildschirmen und überlegten, wie das Problem behoben werden kann. Die Lösung: im PHP-Controller wird einfach nur das erste übergebene Wort ausgewertet. Die Tür ging auf, ein Frontend’ler kam herein und warf eine Frage in den Raum: “Wie muss ich einen Regulären Ausdruck formulieren, damit ich die erste Zeichenkette vor einem Leerzeichen zurückbekomme?” - Total verwirrt und entsetzt warfen wir die Hände über den Kopf: “RegEx? Bloß nicht!”

Problemstellung

Wie die meisten von euch sicherlich wissen, sind Reguläre Ausdrück (vor allem unter PHP) eine der schlimmsten Programmierlösungen, die man einem (nach Performance ächzenden) Webserver nur antun kann – zumindest wenn implementierbare Alternativen existieren. Umgehend schossen mir natürlich unzählige Möglichkeiten durch den Kopf, welche das Problem genauso einfach und performanter lösen können. Zwei von diesen möchte ich euch einmal kurz Vorstellen.

Benchmark mit PHP-Quelltext

Nachdem ich nun lang und breit erzählt habe, möchte ich euch mein kleines Benchmark-Script bereitstellen, damit ihr die Ergebnisse nachvollziehen könnt. Den Quelltext habe ich etwas ausladender gestaltet, damit er auch für Programmiereinsteiger übersichtlich und selbsterklärend bleibt.

$then = $now = 0;
$then = microtime(true);
for($i=0; $i < 1000000; $i++) {
	$str = 'Australien & Ozeanien';
	if($data = explode(' ', $str) !== false) {
		echo $data[0];
	}
}
$now = microtime(true);
echo 'Explode_true ';
echo $now-$then . "\n";

$then = $now = 0;
$then = microtime(true);
for($i=0; $i < 100000; $i++) {
	$str = 'Europa';
	if($data = explode(' ', $str) !== false) {
		echo $data[0];
	}
}
$now = microtime(true);
echo 'Explode_false ';
echo $now-$then . "\n";

$then = $now = 0;
$then = microtime(true);
for($i=0; $i < 1000000; $i++) {
	$str = 'Australien & Ozeanien';
	if($data = strpos($str, ' ') !== false) {
		$data = substr($str, 0, 10);
	}
}
$now = microtime(true);
echo 'strpos_true ';
echo  $now-$then . "\n";

$then = $now = 0;
$then = microtime(true);
for($i=0; $i < 100000; $i++) {
	$str = 'Europa';
	if($data = strpos($str, ' ') !== false) {
		$data = substr($str, 0, 10);
	}
}
$now = microtime(true);
echo 'strpos_false ';
echo $now-$then . "\n";

Benchmark-Ergebnisse

Nachdem ich den Benchmark geschrieben habe, kam es natürlich zur Auswertung der Ergebnisse. Das Benchmarkingscript selbst habe ich über die Kommandozeile auf meine Ubuntu-Laptop (ein einfaches Thinkpad T60 mit DualCore 2*2.0 GHz und 2 GB Arbeitsspeicher) angestoßen. Da einige PHP-Funktionen starke Performanceunterschiede zwischen erfolgreichem Treffer und keinem Treffer aufzeigen, habe ich beide Situationen ausgewertet. Nun zu den Ergebnissen:

#:~/benchmark/$ php index.php
Explode_true 2.2456698417664
Explode_false 0.16291379928589
strpos_true 1.6829509735107
strpos_false 0.090728998184204

Kurz um (die Ergebnisse zeigen es ja bereits): strpos in Verbindung mit einem substr ist um einiges schneller, als die Explode-Funktion. Das Ganze lässt sich natürlich relativ einfach erklären: Array-Funktionen sind in PHP zwar um einiges schneller als der Umgang mit Objekten - jedoch sind die Stringfunktionen natürlich noch einmal deutlich schneller.