PHP Strings

Quotes, heredoc, interpolation, concatenation, and the 20 string functions you'll reach for daily. Plus the UTF-8 trap and how to escape output safely.

Beginner 10 min read 14 examples

Single vs Double Quotes

PHP
<?php
$name = "Ruban";

// Single quotes - LITERAL
echo 'Hello $name';      // Hello $name
echo 'Tab:\t Newline:\n'; // Tab:\t Newline:\n  (no escapes processed)

// Double quotes - interpolated and escapes processed
echo "Hello $name";       // Hello Ruban
echo "Tab:\t Newline:\n";// Tab:    Newline:\n (actual tab and newline)

// Escape sequences in double quotes
echo "Quote: \" Backslash: \\ Dollar: \$";
FeatureSingle \' \'Double " "
Variable interpolationNoYes
Escape sequencesOnly \\\' and \\\\All (\n, \t, \r, ...)
SpeedMarginally fasterSlightly slower

String Interpolation

PHP
<?php
$name = "Ruban";
$user = ["age" => 30];
$obj  = new stdClass();
$obj->title = "Engineer";

// Simple
echo "Hello $name";

// With array - needs curly braces
echo "Age: {$user['age']}";

// With object property
echo "Title: {$obj->title}";

// Complex expression - must use curly braces
echo "Upper: {$name}";    // works
// echo "Upper: $name";    // also works for simple cases

Heredoc & Nowdoc

For multi-line strings, heredoc is much cleaner than escaping quotes:

PHP
<?php
$name = "Ruban";

// Heredoc - like double quotes (interpolation)
$html = <<<HTML
<div class="user">
    <h2>$name</h2>
    <p>Member since 2024</p>
</div>
HTML;

// Nowdoc - like single quotes (literal)
$template = <<<'TPL'
Hello {{ name }},
Welcome to {{ site }}!
TPL;
Indentation matters (PHP 7.3+)

The closing identifier can be indented - PHP strips that exact amount of leading whitespace from every line. Saves you ugly left-aligned blocks inside indented code.

Concatenation

PHP
<?php
$first = "John";
$last  = "Doe";

// . operator
$full = $first . " " . $last;

// .= append
$msg = "Hello, ";
$msg .= $full;          // "Hello, John Doe"

// Implode an array (faster for many parts)
$parts = ["a", "b", "c"];
echo implode("-", $parts);  // a-b-c

Common String Functions

FunctionPurposeExample
strlen()Length in bytesstrlen("hello") -> 5
mb_strlen()Length in charactersmb_strlen("café") -> 4
strtolower()To lowercasestrtolower("HI") -> "hi"
strtoupper()To uppercasestrtoupper("hi") -> "HI"
ucfirst()Capitalize first letterucfirst("hello") -> "Hello"
ucwords()Capitalize each worducwords("hi there") -> "Hi There"
trim()Strip whitespacetrim(" x ") -> "x"
ltrim()/rtrim()Strip from one sidertrim("/path/", "/") -> "/path"
substr()Extract substringsubstr("hello", 1, 3) -> "ell"
str_repeat()Repeat n timesstr_repeat("-", 5) -> "-----"
str_pad()Pad to lengthstr_pad("5", 3, "0", STR_PAD_LEFT) -> "005"
strrev()Reversestrrev("abc") -> "cba"

Searching & Replacing

PHP
<?php
$text = "Hello World";

// PHP 8.0+ - bool returns
var_dump(str_contains($text, "World"));    // true
var_dump(str_starts_with($text, "Hello")); // true
var_dump(str_ends_with($text, "ld"));      // true

// Find position
echo strpos($text, "World");   // 6
echo stripos($text, "WORLD");  // 6 (case-insensitive)

// Replace
echo str_replace("World", "PHP", $text);   // Hello PHP
echo str_ireplace("WORLD", "PHP", $text);  // case-insensitive
echo substr_replace($text, "All", 6, 5);   // Hello All

Splitting & Joining

PHP
<?php
// String -> array
$csv = "a,b,c,d";
$arr = explode(",", $csv);            // ["a","b","c","d"]

// Limit pieces
$arr = explode(",", $csv, 2);         // ["a", "b,c,d"]

// Array -> string
$joined = implode("-", $arr);          // "a-b-c-d"

// Split string into chars
$chars = str_split("hello", 1);       // ["h","e","l","l","o"]

// Split into chunks
$chunks = str_split("abcdefgh", 3);   // ["abc","def","gh"]

sprintf & printf

For formatted output with placeholders:

PHP
<?php
$name = "Ruban";
$price = 49.5;

printf("Hi %s, total: $%.2f\n", $name, $price);
// Hi Ruban, total: $49.50

// sprintf returns the string instead of printing
$msg = sprintf("ID: %05d", 42);        // ID: 00042

// Common format specifiers
// %s - string,   %d - integer,    %f - float
// %.2f - 2 decimal places,        %x - hex
// %05d - pad to 5 digits with 0,  %-10s - left-align in 10 chars

Multibyte (Unicode) Strings

PHP\'s native string functions count bytes, not characters. For Unicode safety use the mbstring extension:

PHP
<?php
$s = "café";

echo strlen($s);       // 5 (4 chars + 1 extra byte for é)
echo mb_strlen($s);    // 4 (correct!)

echo substr($s, 0, 3);    // "caf" + corrupted byte = garbage
echo mb_substr($s, 0, 3); // "caf" (correct!)

echo strtolower("Ä");     // "Ä" (not lowered)
echo mb_strtolower("Ä");  // "ä"
Set the default encoding

Add mb_internal_encoding('UTF-8'); at app bootstrap so every mb_* call assumes UTF-8.

Escaping for Output

PHP
<?php
$userInput = "<script>alert(1)</script>";

// HTML output - prevent XSS
echo htmlspecialchars($userInput, ENT_QUOTES, "UTF-8");
// &lt;script&gt;alert(1)&lt;/script&gt;

// URL parameter
$q = urlencode("hello world & friends");
echo "https://example.com/search?q=$q";

// SQL - NEVER concatenate user input. Use prepared statements:
// $stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
// $stmt->execute([$userInput]);

// JSON
echo json_encode(["name" => $userInput], JSON_HEX_TAG | JSON_HEX_AMP);

Next Steps

Frequently Asked Questions

Use single quotes by default - they're slightly faster and don't scan for variables. Use double quotes when you need interpolation ("$name") or escape sequences ("\n", "\t").

Heredoc for multi-line strings with several variables - cleaner than dozens of . operators. Use concatenation for short joins. For trusted template output, heredoc; for user-facing HTML, use a template engine like Twig.

Regular strlen() counts bytes, not characters. For UTF-8 strings (with accents, emoji, Chinese), use mb_strlen(), mb_substr(), mb_strtolower(). Otherwise strlen("café") returns 5, not 4.

For just a few values, the . operator is fine. For thousands, collect them in an array and call implode("", $parts) at the end - it pre-allocates memory and is much faster than repeated .=.

Use str_contains($haystack, $needle) in PHP 8+. For prefix/suffix: str_starts_with(), str_ends_with(). Before PHP 8, use strpos() !== false.