1. Übersicht
Das Verbinden von zwei oder mehr Strings, um daraus einen neuen String zu erzeugen, wird als „Konkatenation” (engl. concatenation) bezeichnet. In PHP gibt es mehrere verschiedene Möglichkeiten, Strings zu verknüpfen, welche je nach Anwendungszweck Vor- und Nachteile haben. Angenommen es liegen die Strings $a und $b vor, welche verbunden werden sollen, um daraus $c zu erzeugen, dann gibt es folgende Wege:
- Konkatenationsoperator: $c = $a . $b;
- Doppelte Anführungszeichen: $c = "$a$b"; oder $c = "{$a}{$b}";
- sprintf: $c = sprintf('%s%s', $a, $b);
- implode: $c = implode('', array($a, $b));
Aus Performance-Sicht ist die Faustregel, bei drei oder weniger zu verbindenden Strings über den PHP-Operator (Punkt) zu konkatenieren. Bei allem darüber hinaus sollte der Weg über doppelte Anführungszeichen oder die implode()-Methode bevorzugt werden. sprintf() verwendet man nur dann, wenn es auf übersichtlichen Code ankommt.
2. Konkatenationsoperator ($a . $b)
Zwei Strings $a und $b können zu einem neuen String $c verbunden werden indem ein Punkt verwendet wird. Der Punkt ist der Konkatenationsoperator in PHP.
<?php $a = 'A'; $b = 'B'; $c = $a . $b; var_dump($c); ?>
string(2) "AB"
Im Gegensatz zu manchen anderen Sprachen darf das Plus-Zeichen nicht zur Konkatenation verwendet werden:
<?php $a = '1111'; $b = '2222'; $c = $a + $b; var_dump($c); ?>
int(3333)
Wie zu sehen ist, bewirkt die Verwendung des Plus-Zeichens keinen Fatal Error. Stattdessen versucht PHP so gut es geht, die beiden Strings in Integer umzuwandeln, um diese dann zu addieren.
3. Konkatenation über doppelte Anführungszeichen
Innerhalb von doppelten Anführungszeichen „sucht” PHP nach Variablen und ersetzt diese automatisch durch deren Wert. (Achtung: Bei einfachen Anführungszeichen funktioniert das nicht.) So ergibt sich die nachfolgende, übersichtliche Schreibweise:
<?php $a = 'A'; $b = 'B'; $c = "$a$b"; var_dump($c); ?>
string(2) "AB"
Nicht immer findet PHP die Variablen innerhalb der doppelten Anführungszeichen korrekt, wodurch diese falsch ersetzt werden oder gar Parse Errors entstehen. Dies ist beispielsweise häufig bei Zugriffen auf Arrays der Fall. Als Abhilfe kann dann die Variable von geschweiften Klammern umgeben werden, um PHP zu signalisieren, dass der gesamte Block einer Variablen entspricht.
Ohne geschweifte Klammer entsteht im folgenden Beispiel ein Parse Error:
<?php $arr = array(1.0 => 'A', 2.0 => 'B'); $d = "$arr[1.0]$arr[2.0]"; var_dump($d); ?>
<br /> <b>Parse error</b>: syntax error, unexpected '.', expecting ']' in <b>...\test.php</b> on line <b>3</b><br />
Mit geschweiften Klammern funktioniert das vorherige Beispiel:
<?php $arr = array(1.0 => 'A', 2.0 => 'B'); $c = "{$arr[1.0]}{$arr[2.0]}"; var_dump($c); ?>
string(2) "AB"
Geschweifte Klammern dürfen natürlich auch dann verwendet werden, wenn diese nicht unbedingt benötigt werden.
4. Konkatenation über sprintf
Die Funktion sprintf($str, $var...) nimmt einen String $str entgegen, sowie als zweiten bis n-ten Parameter beliebig viele weitere Variablen und führt verschiedene Ersetzungen im String durch. Eine von diesen möglichen Ersetzungen ist es, jedes „%s” durch eine der übergebenen Variablen aus $var zu ersetzen. Welche genau das ist, wird durch die Position von „%s” bestimmt: Das erste „%s” wird durch die erste erste Variable aus $var ersetzt (bzw. durch den zweiten an die Funktion übergebenen Parameter), das zweite „%s” durch die zweite Variable aus $var (bzw. durch den dritten Parameter) usw. So kann die Funktion verwendet werden, um zwei Strings über „%s%s” zu verbinden (drei entsprechend mit „%s%s%s” usw.).
<?php $a = 'A'; $b = 'B'; $c = sprintf('%s%s', $a, $b); var_dump($c); ?>
string(2) "AB"
5. Konkatenation über implode()
Die Funktion implode($delemiter, $arr) „verschmilzt” alle Werte aus $arr zu einem String und gibt diesen zurück. Im neuen String werden die Werte durch beliebig viele Zeichen getrennt, welche über $delemiter angegeben werden. Sollen nur mehrere Strings zu einem neuen verknüpft werden, dann kann für $delemiter entsprechend ein leerer String angegeben werden ($delemiter = ''). Das nächste Beispiel stellt die Anwendung von implode() zur Konkatenation dar:
<?php $a = 'A'; $b = 'B'; $c = implode('', array($a, $b)); var_dump($c); ?>
string(2) "AB"
6. Vergleich der Performance der vorgestellten Methoden
Die nachfolgende Tabelle zeigt eine Auswertung der verschiedenen Methoden zur Konkatenation. In der linken bzw. ersten Spalte wird die Anzahl der zu verbindenden Variablen aufgezählt. Es wurden dabei alle Zweierpotenzen von 21 bis 29 durchgetestet (2, 4, 8, ...). Jede Variable enthielt immer den Wert „abc”. Die Konkatenation aus $a und $b (21 zu konkatenierende Variablen) erzeugte also „abcabc”. In den weiteren Spalten folgt der Zeitbedarf der verschiedenen Methoden bei Anwendung auf die aufgelistete Anzahl an Variablen und 100.000 Iterationen. Jeder Wert muss demnach durch 100.000 geteilt werden, um den Zeitbedarf einer einzelnen Iteration zu erhalten. Verglichen werden sollte jedoch der Zeitbedarf der Methoden relativ zueinander, da die absoluten Werte vom Testsystem abhängen (Hardware, Betriebssystem etc.).
Wie der Tabelle zu entnehmen ist, schneidet die Konkatenation über den Operator („$c = $a . $b”) bei zwei Variablen am besten ab. Ab vier und mehr Variablen hat bereits der Weg über geschweifte Klammern („$c = "{$a}{$b}";”) einen kleinen Vorsprung gegenüber der vorherigen Methode. Ab etwa 32 Variablen sollte über implode() konkateniert werden. (Bei derartig vielen Variablen ist aber anzunehmen, dass diese in einem Array liegen und dynamisch erzeugt wurden, sodass die anderen Methoden ohnehin nicht in Frage kommen.) Die Funktion sprintf() schneidet grundsätzlich am schlechtesten ab und sollte daher nur dann verwendet werden, wenn dies der Lesbarkeit des Codes hilft.
Anzahl | $a.$b | "{$a}{$b}" | sprintf | implode |
---|---|---|---|---|
2 (2^1) | 0.0148s | 0.0198s | 0.0811s | 0.0568s |
4 (2^2) | 0.0323s | 0.0299s | 0.1189s | 0.0658s |
8 (2^3) | 0.0639s | 0.0569s | 0.1954s | 0.081s |
16 (2^4) | 0.1339s | 0.1026s | 0.3471s | 0.1129s |
32 (2^5) | 0.2599s | 0.1959s | 0.6402s | 0.1929s |
64 (2^6) | 0.5675s | 0.393s | 1.1628s | 0.3371s |
128 (2^7) | 1.549s | 0.8179s | 2.3355s | 0.6069s |
256 (2^8) | 3.6292s | 1.5214s | 4.5611s | 1.1527s |
512 (2^9) | 8.5196s | 3.0539s | 9.098s | 2.2968s |
Die Tabelle wurde mit dem nachfolgenden Skript erzeugt. Das Generieren ist nicht trivial, da nur implode() einfach mit einer beliebigen Zahl an Variablen aufgerufen werden kann. Für die anderen Methoden muss der passende Code per eval() erzeugt werden. (Bei sprintf() kann zwar der zu formatierende String („%s%s%s...”) dynamisch erzeugt werden, die Übergabe der Parameter aber nicht.) Es wird für jede gewünschte Variablenzahl eine passende Methode generiert. Die Anwendung von eval() innerhalb einer for-Schleife würde das Ergebnis verfälschen.
<?php define('ITERATIONS', 100000); define('TESTSTRING', 'abc'); function calculateTimeImplode($countPieces, $str, $iterations) { $arr = array_fill(0, $countPieces, $str); $start = microtime(true); for ($x=0; $x<$iterations; ++$x) { $s = implode('', $arr); } return round(microtime(true) - $start, 4); } ?> <table id="php-concat"> <thead> <tr> <th>Anzahl</th> <th>$a.$b</th> <th>"{$a}{$b}"</th> <th>sprintf</th> <th>implode</th> </tr> </thead> <tbody> <?php foreach (range(1, 9) as $exponent): ?> <?php $f1 = 'function calculateTimeAdotB'.$exponent.'($str, $iterations) { $start = microtime(true); for ($x=0; $x<$iterations; ++$x) { $s = $str'.str_repeat('.$str', pow(2, $exponent)-1).'; } return round(microtime(true) - $start, 4); }'; $f2 = 'function calculateTimeAdotBInDoubleQuotes'.$exponent.'($str, $iterations) { $start = microtime(true); for ($x=0; $x<$iterations; ++$x) { $s = "'.str_repeat('{$str}', pow(2, $exponent)).'"; } return round(microtime(true) - $start, 4); }'; $f3 = 'function calculateTimeSprintf'.$exponent.'($str, $iterations) { $start = microtime(true); for ($x=0; $x<$iterations; ++$x) { $s = sprintf(\''.str_repeat('%s', pow(2, $exponent)).'\', $str'.str_repeat(', $str', pow(2, $exponent)-1).'); } return round(microtime(true) - $start, 4); }'; eval($f1); eval($f2); eval($f3); $f1FName = 'calculateTimeAdotB'.$exponent; $f2FName = 'calculateTimeAdotBInDoubleQuotes'.$exponent; $f3FName = 'calculateTimeSprintf'.$exponent; $f4FName = 'calculateTimeImplode'; ?> <tr> <td><?php echo pow(2, $exponent)." (2^$exponent)"; ?></td> <td><?php echo $f1FName(TESTSTRING, ITERATIONS); ?>s</td> <td><?php echo $f2FName(TESTSTRING, ITERATIONS); ?>s</td> <td><?php echo $f3FName(TESTSTRING, ITERATIONS); ?>s</td> <td><?php echo $f4FName(pow(2, $exponent), TESTSTRING, ITERATIONS); ?>s</td> </tr> <?php endforeach; ?> </tbody> </table>
Kommentare (1)
Von neu nach altWir bitten um ihr Verständnis.