0

String interpolation vs concatenation — performance implications

Beginner5 min read·php-03-002
performance

Concept

PHP offers three ways to embed variables into strings: double-quoted string interpolation, concatenation with ., and sprintf/printf. They differ in readability, flexibility, and performance — though in modern PHP the differences are rarely significant enough to drive architectural decisions.

Double-quoted interpolation ("Hello, $name!" or "Hello, {$obj->name}!") is handled at compile time — the parser splits the string into literal chunks and variable reads, generating specialized opcodes. It's readable for simple cases but can become confusing with complex expressions or array access.

Concatenation (.) creates a new string from two strings. Every . operation allocates a new zend_string. In a tight loop building a large string with many .= operations, each step allocates and potentially frees memory. The engine has some optimization for simple cases but cannot eliminate all allocations. For building large strings in a loop, implode() on a pre-built array is faster.

sprintf is the most explicit and separable approach — the format string and data are cleanly separated. Slightly slower than interpolation for simple cases (function call overhead + format parsing), but often the right choice for formatted output, number formatting, and internationalization where the template may come from a translation file.

The real performance winner for many concatenations: build an array and implode(). This way you only allocate the final string once.

Code Example

php
<?php
declare(strict_types=1);

$name = 'Codrut';
$count = 42;
$price = 9.9;

// Interpolation — clean for simple variables
echo "Hello, $name! You have $count messages.\n";

// Curly brace syntax for complex expressions
$user = ['name' => 'Codrut'];
echo "User: {$user['name']}\n";

$obj = new stdClass();
$obj->role = 'admin';
echo "Role: {$obj->role}\n";

// Concatenation — verbose but explicit
echo 'Hello, ' . $name . '! You have ' . $count . " messages.\n";

// sprintf — best for formatted output
echo sprintf("Price: %'.2f EUR\n", $price);    // "Price: 9.90 EUR"
echo sprintf("%-20s %5d\n", $name, $count);     // padded columns

// Performance test pattern — building large strings
// SLOW: many small concatenations in a loop
$result = '';
for ($i = 0; $i < 10_000; $i++) {
    $result .= "item-$i,"; // allocates new string each iteration
}

// FAST: collect and join once
$parts = [];
for ($i = 0; $i < 10_000; $i++) {
    $parts[] = "item-$i";
}
$result = implode(',', $parts); // one allocation for the final string

// ob_start() for template-style output
ob_start();
echo "Name: $name\n";
echo "Count: $count\n";
$output = ob_get_clean();