Senior 4 min · March 06, 2026

PHP Strings — XSS from Unescaped Product Descriptions

Using echo $review->text instead of htmlspecialchars caused fake login pop-ups on a major retailer's site.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • A PHP string is a sequence of characters wrapped in quotes
  • Single quotes treat everything literally; double quotes parse variables inside
  • PHP offers 100+ built-in functions: strlen, strpos, str_replace, explode, and more
  • Strings are immutable — functions return new values, they don't modify the original
  • Use double quotes for variable embedding; single quotes for performance when no variables
  • Production risk: forgetting strict comparison with strpos() leads to logic bugs
Plain-English First

Imagine a string is just a piece of text written on a sticky note. PHP lets you stick notes together, cut them up, search inside them, count the letters, and swap words out — all without touching the original paper. Every time a website shows your name, a search result, or an error message, it's doing exactly that: manipulating strings. PHP has a huge built-in toolkit of functions that do the heavy lifting so you don't have to write the logic yourself.

Every single thing a user reads on a webpage is text — usernames, blog posts, error messages, product names, email addresses. That text has to come from somewhere, get processed, and then get shown in exactly the right format. PHP is one of the most widely-used languages for powering that backend text-processing work, and strings are at the absolute heart of it. If you can't confidently work with strings in PHP, you'll hit a wall fast.

What Is a PHP String and How Do You Create One?

A string is just a sequence of characters — letters, numbers, spaces, symbols — wrapped in quotes. Think of it like a sentence inside a box. PHP needs those quotes to know where the text starts and where it ends. Without them, PHP would try to read your text as a command and get very confused.

PHP gives you two ways to wrap that box: single quotes and double quotes. The difference matters more than beginners expect. Inside double quotes, PHP looks for variables and replaces them with their values — this is called variable interpolation. Inside single quotes, PHP takes everything literally. No substitutions, no special behaviour — what you type is exactly what you get.

There's also a third syntax called Heredoc, which is like a multi-line box for long chunks of text. It's less common day-to-day but incredibly useful when you're building HTML templates or long email bodies inside PHP.

For now, start with double quotes for most things. Use single quotes when you want to be strict and explicit, or when your string contains a lot of dollar signs (like currency) and you don't want PHP trying to treat them as variable names.

creating_strings.phpPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

// --- Single-quoted string: PHP takes it literally ---
$productName = 'Wireless Headphones';

// --- Double-quoted string: PHP replaces $productName with its value ---
$welcomeMessage = "Thank you for purchasing $productName!";

// --- Concatenation: joining two strings using the dot (.) operator ---
$firstName = 'Sarah';
$lastName  = 'Connor';
$fullName  = $firstName . ' ' . $lastName; // dot joins them with a space

// --- Heredoc: great for multi-line strings (note: no indent before closing label) ---
$emailBody = <<<EOT
Dear $firstName,

Your order for $productName has been confirmed.
Thank you for shopping with us!

EOT;

// --- Print results ---
echo $welcomeMessage . "\n";
echo $fullName . "\n";
echo $emailBody;
Output
Thank you for purchasing Wireless Headphones!
Sarah Connor
Dear Sarah,
Your order for Wireless Headphones has been confirmed.
Thank you for shopping with us!
Watch Out: Single vs Double Quotes
If you write echo 'Hello $firstName'; with single quotes, PHP will literally print the dollar sign and the word firstName — it won't swap in the variable value. Always use double quotes when you want variable interpolation to work.
Production Insight
Using single quotes when you need variable interpolation silently prints the raw variable name instead of its value.
Always match quote style to intent: double for interpolation, single for literal.
This seems trivial but causes confusing bugs in log messages and dynamic queries.
Key Takeaway
Double quotes interpolate variables.
Single quotes treat everything literally.
Choose deliberately, don't just type quotes out of habit.

The Most Useful PHP String Functions You'll Use Every Day

PHP ships with over 100 built-in string functions. That sounds overwhelming, but honestly about a dozen of them handle 90% of real-world work. These aren't arbitrary — each one exists because web developers kept writing the same helper code over and over, and the PHP team baked those solutions into the language.

Here's the mental model: every string function takes at least one string as input and gives you something back — a modified string, a number, or a true/false answer. You're never changing the original variable unless you explicitly overwrite it. Strings in PHP are immutable by default in that sense — the function returns a new value, it doesn't edit in place.

The functions below are grouped by what job they do: measuring strings, transforming their case, trimming whitespace, searching inside them, and replacing content. Learn them in that order and they'll stick.

string_functions.phpPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php

$userInput = '   Hello, World!   ';
$articleTitle = 'php strings are powerful';
$emailAddress = 'user@example.com';
$productDescription = 'The quick brown fox jumps over the lazy dog.';

// ── MEASURING ───────────────────────────────────────────────

// strlen() counts how many characters are in the string (spaces included)
$rawLength = strlen($userInput); // 21 (includes the leading/trailing spaces)

// ── TRIMMING ────────────────────────────────────────────────

// trim() strips whitespace from both ends — classic for cleaning form input
$cleanInput = trim($userInput); // 'Hello, World!'
$trimmedLength = strlen($cleanInput); // 13

// ── CASE TRANSFORMATIONS ────────────────────────────────────

// strtoupper() makes every letter a capital
$shoutedTitle = strtoupper($articleTitle); // 'PHP STRINGS ARE POWERFUL'

// strtolower() makes every letter lowercase — useful before comparisons
$lowerEmail = strtolower($emailAddress); // 'user@example.com'

// ucwords() capitalises the first letter of every word — great for display names
$formattedTitle = ucwords($articleTitle); // 'Php Strings Are Powerful'

// ── SEARCHING ───────────────────────────────────────────────

// strpos() finds the position (index) of the FIRST occurrence of a substring
// Returns FALSE if the substring isn't found — note: position 0 is the very first character
$foxPosition = strpos($productDescription, 'fox'); // 16

// str_contains() (PHP 8+) — returns true/false, much more readable for simple checks
$hasFox = str_contains($productDescription, 'fox'); // true

// ── REPLACING ───────────────────────────────────────────────

// str_replace() swaps out every occurrence of a word for another
$updatedDescription = str_replace('lazy', 'energetic', $productDescription);
// 'The quick brown fox jumps over the energetic dog.'

// ── SLICING ─────────────────────────────────────────────────

// substr() extracts a portion of a string: substr(string, start, length)
$domain = substr($emailAddress, 5); // 'example.com' — starts from index 5
$tld    = substr($emailAddress, -3); // 'com' — negative index counts from the end

// ── SPLITTING ───────────────────────────────────────────────

// explode() splits a string into an array using a delimiter
$emailParts = explode('@', $emailAddress); // ['user', 'example.com']
$username   = $emailParts[0]; // 'user'

// ── OUTPUT RESULTS ──────────────────────────────────────────
echo "Raw length (with spaces): $rawLength\n";
echo "Trimmed: '$cleanInput'Length: $trimmedLength\n";
echo "Shouted: $shoutedTitle\n";
echo "Formatted: $formattedTitle\n";
echo "'fox' found at position: $foxPosition\n";
echo "Contains 'fox': " . ($hasFox ? 'Yes' : 'No') . "\n";
echo "Updated: $updatedDescription\n";
echo "Domain: $domain\n";
echo "TLD: $tld\n";
echo "Username from email: $username\n";
Output
Raw length (with spaces): 21
Trimmed: 'Hello, World!' — Length: 13
Shouted: PHP STRINGS ARE POWERFUL
Formatted: Php Strings Are Powerful
'fox' found at position: 16
Contains 'fox': Yes
Updated: The quick brown fox jumps over the energetic dog.
Domain: example.com
TLD: com
Username from email: user
Pro Tip: PHP 8's str_contains() Is Your Friend
Before PHP 8, checking if a string contained a word meant using strpos() !== false — an ugly pattern that trips up beginners (see Gotchas below). If you're on PHP 8+, use str_contains(), str_starts_with(), and str_ends_with() instead. They return plain true/false and read like plain English.
Production Insight
Choosing the wrong function for the task leads to subtle bugs — using strpos() instead of str_contains() when you just need a boolean.
Performance matters: strpos() is slightly faster than str_contains() for very large strings, but readability wins in 99% of cases.
Always test with edge cases: empty string, single character, UTF-8 content.
Key Takeaway
Group string functions by purpose: measure, trim, case, search, replace, split.
For simple existence checks, use str_contains() (PHP 8+) or strpos() !== false.
The return value type matters — always check it strictly.

Formatting Strings for Output — sprintf and number_format

There's a big difference between storing a number and displaying it nicely. The number 49999.5 in your database needs to look like $49,999.50 on a receipt. PHP's sprintf() function acts like a template engine for strings — you write a pattern with placeholders, then tell PHP what values to slot in.

Think of sprintf() like a Mad Libs game: you write a sentence with blank spaces, and then supply the words separately. The format codes starting with % are the blanks. %s means 'put a string here', %d means 'put a whole number here', %.2f means 'put a decimal number here with exactly 2 decimal places'.

This approach is cleaner than manually concatenating with dots, especially when building things like price labels, log messages, or SQL queries. It also separates your template from your data, which makes the code much easier to read and maintain.

number_format() is a simpler companion function focused purely on formatting numbers — adding thousands separators and controlling decimal places. Every e-commerce site uses it.

string_formatting.phpPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

$itemName     = 'Mechanical Keyboard';
$itemPrice    = 149.9;  // raw price from database
$itemQuantity = 3;
$taxRate      = 0.08;   // 8% tax

// ── number_format() ─────────────────────────────────────────
// number_format(number, decimal_places, decimal_separator, thousands_separator)
$formattedPrice = number_format($itemPrice, 2); // '149.90'

$largeAmount = 1999999.5;
$readableAmount = number_format($largeAmount, 2, '.', ','); // '1,999,999.50'

// ── sprintf() ───────────────────────────────────────────────
// %s = string placeholder, %.2f = float with 2 decimal places, %d = integer
$receiptLine = sprintf(
    '%d x %s @ $%.2f each',
    $itemQuantity,   // fills %d
    $itemName,       // fills %s
    $itemPrice       // fills %.2f
);
// Result: '3 x Mechanical Keyboard @ $149.90 each'

// Calculating and formatting a total with tax
$subtotal  = $itemPrice * $itemQuantity; // 449.70
$taxAmount = $subtotal * $taxRate;       // 35.976
$total     = $subtotal + $taxAmount;     // 485.676

$receiptSummary = sprintf(
    "Subtotal: $%.2f\nTax (8%%): $%.2f\nTotal:    $%.2f",
    $subtotal,
    $taxAmount,
    $total
    // Note: %% prints a literal percent sign — a single % would confuse sprintf
);

// ── OUTPUT ──────────────────────────────────────────────────
echo $receiptLine . "\n";
echo $receiptSummary . "\n";
echo "Large amount formatted: $" . $readableAmount . "\n";
Output
3 x Mechanical Keyboard @ $149.90 each
Subtotal: $449.70
Tax (8%): $35.98
Total: $485.68
Large amount formatted: $1,999,999.50
Interview Gold: sprintf vs echo with concatenation
Interviewers love asking why you'd choose sprintf over just echoing concatenated strings. The answer: sprintf returns a value you can store, reuse, or pass to another function. echo just prints and discards. sprintf also keeps your template and data cleanly separated, which makes complex formatted output far more maintainable.
Production Insight
Using concatenation for complex strings leads to hard-to-read code and increased risk of missing spaces or quotes.
sprintf() is essential for internationalisation: placeholders make it easy to swap translated strings.
Watch out for %% to print a literal percent sign — a single % will be interpreted as a format placeholder.
Key Takeaway
sprintf() separates template from data — cleaner than concatenation.
number_format() adds commas and decimals for readable numbers.
Use placeholders (%s, %d, %.2f) and always escape % with %% to print a literal percent.

Searching and Replacing Text — The Real-World Power Move

The ability to search inside strings and replace content is where PHP strings stop being a theory exercise and start solving real problems. Imagine you're building a blog platform: users write posts with a custom shortcode like [author-name], and your system replaces it with the actual author's name before display. That's str_replace() in action.

For simple replacements, str_replace() is perfect. But sometimes you need more power — maybe you want to find all phone numbers in a block of text, or validate that an email address actually looks like an email. That's when you reach for regular expressions via preg_match() and preg_replace(). Regex is its own deep topic, but it's built on the same string foundation you're learning now.

One practical pattern every PHP developer uses: sanitising user input before storing or displaying it. The htmlspecialchars() function converts dangerous characters like < and > into safe HTML entities, stopping cross-site scripting (XSS) attacks. It's not glamorous, but skipping it is a serious security mistake. Always, always sanitise before you echo user-supplied data.

search_and_replace.phpPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php

// ── BASIC REPLACEMENT ───────────────────────────────────────

$blogTemplate = 'Welcome to [site-name]! Written by [author-name] on [date].';

// str_replace can accept arrays to do multiple replacements in one call
$published = str_replace(
    ['[site-name]', '[author-name]', '[date]'],   // search for these
    ['TheCodeForge', 'Alice Merritt', 'June 2025'], // replace with these
    $blogTemplate                                   // in this string
);
// Result: 'Welcome to TheCodeForge! Written by Alice Merritt on June 2025.'

// ── CASE-INSENSITIVE REPLACEMENT ────────────────────────────

$userComment = 'I love PHP! PHP is awesome. php rocks.';

// str_ireplace() is case-insensitive — it catches 'PHP', 'php', 'Php', etc.
$censoredComment = str_ireplace('php', '***', $userComment);
// Result: 'I love ***! *** is awesome. *** rocks.'

// ── SECURITY: Sanitising user input before display ───────────

$maliciousInput = '<script>alert("You have been hacked!");</script>';

// htmlspecialchars() converts < > " & into safe HTML entities
// This stops the browser from executing the script tag
$safeOutput = htmlspecialchars($maliciousInput, ENT_QUOTES, 'UTF-8');
// Result: '&lt;script&gt;alert(&quot;You have been hacked!&quot;);&lt;/script&gt;'
// The browser displays it as plain text — not an executable script

// ── SIMPLE REGEX EXAMPLE: Validate an email ─────────────────

$emailToCheck = 'contact@thecodeforge.io';

// preg_match() returns 1 if pattern matches, 0 if not
// FILTER_VALIDATE_EMAIL is PHP's built-in email validator (simpler than raw regex)
$isValidEmail = filter_var($emailToCheck, FILTER_VALIDATE_EMAIL);

// ── OUTPUT ──────────────────────────────────────────────────
echo $published . "\n";
echo $censoredComment . "\n";
echo "Safe output: " . $safeOutput . "\n";
echo "Is email valid: " . ($isValidEmail ? 'Yes' : 'No') . "\n";
Output
Welcome to TheCodeForge! Written by Alice Merritt on June 2025.
I love ***! *** is awesome. *** rocks.
Safe output: <script>alert("You have been hacked!");</script>
Is email valid: Yes
Watch Out: Never echo Raw User Input
If a user submits <script>...</script> in a form and you echo it directly without htmlspecialchars(), their script runs in every visitor's browser. This is called XSS (Cross-Site Scripting) and it's one of the most common web vulnerabilities. One line — htmlspecialchars($value, ENT_QUOTES, 'UTF-8') — fixes it every time.
Production Insight
Missing htmlspecialchars() is the root cause of 70% of XSS vulnerabilities in PHP applications.
Using str_replace() for simple string replacement is fast, but for patterns use preg_replace() with careful regex to avoid catastrophic backtracking.
Always test your search/replace with edge cases: empty strings, overlapping matches, and UTF-8 characters.
Key Takeaway
Sanitise user output with htmlspecialchars() before echo — non-negotiable.
Use str_replace() for simple replacements; preg_match()/preg_replace() for patterns.
Case-insensitive replacements? Use str_ireplace() or regex with /i flag.

String Comparison, Sorting, and Multibyte Safety

Comparing strings in PHP isn't always straightforward. The == operator performs type coercion, which can lead to unexpected results. '123' == 123 is true, but '123abc' == 123 is also true because PHP converts the string to a number. For strict comparison, use === which checks both value and type. For string-specific comparison, use strcmp() which returns 0 if equal, negative if first is less, positive if first is greater — and it's binary safe.

When dealing with accented characters (é, ñ, ü) or non-Latin scripts (Cyrillic, Chinese, Arabic), standard functions like strlen() and strtolower() break. They count bytes, not characters. A single emoji like 😀 is 4 bytes. Use the mb_ (multibyte) family of functions: mb_strlen(), mb_strtolower(), mb_substr(), etc. They respect character encoding and count actual characters.

Sorting user-generated lists? Use strnatcmp() for natural order (e.g., 'img2.jpg' before 'img10.jpg') and setlocale() with strcoll() for locale-aware sorting.

comparison_multibyte.phpPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php

// ── BASIC STRING COMPARISON ─────────────────────────────────

$value1 = '123';
$value2 = 123;

var_dump($value1 == $value2);  // bool(true) — loose comparison
var_dump($value1 === $value2); // bool(false) — strict comparison

// strcmp for binary-safe string comparison
$strA = 'apple';
$strB = 'banana';
$result = strcmp($strA, $strB); // negative (-1): 'apple' < 'banana'
echo "strcmp result: $result\n";

// ── MULTIBYTE FUNCTIONS ─────────────────────────────────────

$emoji = '😀';
$accent = 'café';

echo "strlen of emoji: " . strlen($emoji) . "\n";        // 4 (bytes)
echo "mb_strlen of emoji: " . mb_strlen($emoji) . "\n"; // 1 (character)

// mb_strtolower for proper case folding
$mixed = 'CAFÉ';
echo strtolower($mixed) . "\n";                // 'cafÉ' (accent untouched)
echo mb_strtolower($mixed) . "\n";            // 'café' (correct)

// mb_substr for safe slicing
$text = '日本語';
echo substr($text, 1, 1) . "\n";              // garbage byte
echo mb_substr($text, 1, 1) . "\n";          // '本'

// ── NATURAL SORTING ─────────────────────────────────────────

$files = ['img10.jpg', 'img2.jpg', 'img1.jpg'];
natsort($files); // sorts naturally: img1, img2, img10
print_r($files);

// ── OUTPUT ──────────────────────────────────────────────────
echo "\nDone.\n";
Output
strcmp result: -1
strlen of emoji: 4
mb_strlen of emoji: 1
CAFÉ
café
(garbage byte)
Array
(
[0] => img1.jpg
[1] => img2.jpg
[2] => img10.jpg
)
Done.
When to Use mb_ Functions
If your application supports any language beyond plain English, always use mb_strlen(), mb_strtolower(), mb_substr(), and other mb_ versions. The standard functions will silently produce wrong results for multi-byte characters.
Production Insight
Using strlen() on UTF-8 text in a multilingual app leads to incorrect character counts, breaking validation, truncation, and search.
A common bug: checking password length with strlen() when the password contains emojis — a 4-character password can be 16 bytes, causing false rejections.
Multibyte functions have a performance cost; they are slower than byte-oriented functions, but correctness trumps speed here.
Key Takeaway
Use === for strict string comparison; avoid ==.
For multibyte strings, always use mb_ functions — strlen() counts bytes, not characters.
Natural sorting with natsort() prevents 'img10.jpg' appearing before 'img2.jpg'.
● Production incidentPOST-MORTEMseverity: high

XSS via Unsanitised Product Descriptions Took Down a Major Retailer

Symptom
Users reported fake login pop-ups on product pages. The security team found that product descriptions and reviews were displayed on the page without escaping HTML.
Assumption
The team assumed that since the CMS team validated input on submission, the output was safe. They were wrong — stored XSS doesn't need runtime input.
Root cause
The product detail template used echo $review->text; instead of echo htmlspecialchars($review->text, ENT_QUOTES, 'UTF-8');. PHP's default behavior is to output raw text, and the CMS validation only blocked explicit <script> tags but allowed other HTML like <img onerror>.
Fix
Applied htmlspecialchars() to all user-generated text in views, added a global output filter using PHP's output buffering with callback, and set Content-Security-Policy HTTP headers.
Key lesson
  • Never trust that input validation alone secures output.
  • Always apply a context-appropriate encoding function (HTML, JS, URL) before rendering.
  • Automate XSS scanning in CI/CD with tools like OWASP ZAP or SonarQube rules.
Production debug guideSymptom → Action guide for the most common string-related failures.5 entries
Symptom · 01
echo $someString outputs nothing (blank) but you expected text
Fix
Check if the variable is undefined or null. Use var_dump($someString); and set error_reporting(E_ALL); in development. Also check if output buffering (ob_start) is interfering.
Symptom · 02
strpos($haystack, $needle) evaluates to true when match is at position 0 but you expect false
Fix
Replace if (strpos(...)) with if (strpos(...) !== false). Upgrade to PHP 8+ and use str_contains() for clarity.
Symptom · 03
User-submitted text causes JavaScript errors or pop-ups
Fix
Check if htmlspecialchars() is applied before echo. Look for missing ENT_QUOTES flag or wrong encoding parameter.
Symptom · 04
strlen() returns a different count than expected for multi-byte characters (e.g., é, 日本)
Fix
Use mb_strlen() instead of strlen(). Ensure the mbstring extension is enabled and default_charset is set to UTF-8.
Symptom · 05
strtotime() returns false for date strings
Fix
Check format. Use DateTime::createFromFormat() for custom formats. Enable error-reporting to see warnings.
★ Quick XSS Debug Cheat Sheet for PHPWhen you suspect cross-site scripting via unsanitised strings, follow this protocol.
JavaScript alert displayed on page
Immediate action
Check if user-generated content appears in page source as raw HTML.
Commands
grep -r 'echo \$' --include='*.php' . | grep -v htmlspecialchars
curl -I https://example.com | grep content-security-policy
Fix now
Wrap all dynamic content in htmlspecialchars($var, ENT_QUOTES, 'UTF-8') and add CSP header via PHP header() or server config.
Unicode/multibyte characters appear as garbage (e.g., é instead of é)+
Immediate action
View page source — see mojibake.
Commands
echo mb_detect_encoding($string); to check current encoding
Add `<meta charset='UTF-8'>` in HTML head and set `default_charset='UTF-8'` in php.ini
Fix now
Use mb_ functions for all string operations on user input.
`strpos($haystack, $needle)` returns false on valid match+
Immediate action
Check if you need strict comparison (`===`).
Commands
var_dump(strpos('abc', 'a')); // outputs int(0), not false
echo gettype(strpos('abc', 'a')); // should be integer
Fix now
Replace if(strpos(...) != false) with if(strpos(...) !== false) or use str_contains() if PHP 8+.
TaskFunction to UseReturnsPHP Version
Count charactersstrlen()Integer (character count)All versions
Find substring positionstrpos()Integer or falseAll versions
Check if string containsstr_contains()Boolean (true/false)PHP 8.0+
Check string starts withstr_starts_with()Boolean (true/false)PHP 8.0+
Check string ends withstr_ends_with()Boolean (true/false)PHP 8.0+
Replace text (case-sensitive)str_replace()Modified stringAll versions
Replace text (case-insensitive)str_ireplace()Modified stringAll versions
Extract substringsubstr()Extracted stringAll versions
Split string into arrayexplode()Array of partsAll versions
Join array into stringimplode()Single stringAll versions
Format string with placeholderssprintf()Formatted stringAll versions
Format a number for displaynumber_format()Formatted stringAll versions
Sanitise for HTML outputhtmlspecialchars()Safe HTML stringAll versions
Remove leading/trailing spacetrim()Cleaned stringAll versions
Convert to uppercasestrtoupper()Uppercase stringAll versions
Convert to lowercasestrtolower()Lowercase stringAll versions
Compare strings (binary safe)strcmp()Integer (-1, 0, 1)All versions
Count multibyte charactersmb_strlen()Integer (character count)All versions (mbstring required)
Natural sort ordernatsort()Sorted array (modifies input)All versions

Key takeaways

1
Double quotes allow variable interpolation; single quotes treat everything literally
choose deliberately, not by habit.
2
strpos() returns 0 (not false) when a match is at index 0
always use === false for the not-found check, or switch to str_contains() on PHP 8+.
3
Always sanitise user input with htmlspecialchars($value, ENT_QUOTES, 'UTF-8') before echoing it
skipping this is an XSS vulnerability waiting to happen.
4
sprintf() is more than a formatter
it returns a value you can store, pass around, or log, making it far more flexible than concatenating strings inside an echo.
5
For multilingual text, always use mb_ functions like mb_strlen() instead of strlen()
the standard functions count bytes, not characters.

Common mistakes to avoid

3 patterns
×

Using == to check if strpos() returned false

Symptom
strpos() returns 0 when match is at first character, and 0 == false is true in PHP loose comparison, causing a false negative for 'not found'.
Fix
Always use === for strpos() checks: if (strpos($haystack, $needle) === false). Better yet, use str_contains() on PHP 8+.
×

Forgetting that string indexes start at 0

Symptom
substr($email, 1) is used to skip the first character but actually returns from the second character onward because index 1 is the second character.
Fix
Remember that string indexes are zero-based. The first character is at position 0.
×

Echoing unsanitised user input

Symptom
XSS vulnerabilities allow attackers to inject executable scripts that run in visitors' browsers, leading to data theft or defacement.
Fix
Always wrap user-supplied data in htmlspecialchars($value, ENT_QUOTES, 'UTF-8') before echoing. Make this a non-negotiable habit.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the difference between single-quoted and double-quoted strings i...
Q02SENIOR
Why is if (strpos($str, 'word') == false) a bug, and how do you fix it?
Q03SENIOR
If you had to extract the domain name from a list of email addresses sto...
Q04SENIOR
How do you handle multibyte strings in PHP? When should you use mb_ func...
Q01 of 04JUNIOR

What is the difference between single-quoted and double-quoted strings in PHP, and when would you deliberately choose one over the other?

ANSWER
Single-quoted strings are taken literally — no variable interpolation, no escape sequences except \\ and \'. Double-quoted strings parse variables and interpret escape sequences like \n, \t. Use double quotes when you need to embed variables or escape sequences; use single quotes when you want to ensure exact output (e.g., file paths, regular expressions). Double-quoted strings are slightly slower because PHP scans for variables.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between echo and print in PHP for strings?
02
How do I check if a PHP string contains a specific word?
03
Why does strlen() sometimes give the wrong character count for non-English text?
04
How do I convert a string to lowercase in PHP correctly for all languages?
05
What is the most efficient way to build a long string from many parts in PHP?
🔥

That's PHP Basics. Mark it forged?

4 min read · try the examples if you haven't

Previous
PHP Arrays
7 / 14 · PHP Basics
Next
PHP Forms and User Input