
Chapter 07: Mastering String Manipulation
Overview
Working with text is a core part of web development. Whether you're formatting a user's name, creating a URL slug from a blog post title, or parsing data from a file, you'll constantly be manipulating strings.
PHP provides a rich library of over 100 functions dedicated to working with strings, making it a powerful tool for text processing. In this chapter, we'll cover the most common and useful functions that you'll use every day, including modern PHP 8.0+ features that make text processing more intuitive.
By the end of this chapter, you'll be able to clean, search, transform, and format strings with confidence.
Prerequisites
Required:
- PHP 8.4 installed (Chapter 00)
- A text editor or IDE
- Basic understanding of variables and data types (Chapter 02)
Estimated time: ~25 minutes
What You'll Build
By the end of this chapter, you'll create several working examples demonstrating:
- Length calculation and case conversion
- Substring extraction with
substr() - Title case formatting (
ucfirst,ucwords,lcfirst) - Creating blog excerpts and text previews
- Converting text to HTML with
nl2br() - Padding strings for formatted output
- Modern PHP 8.0+ string searching functions
- Find and replace operations (including batch replacements)
- String splitting and joining
- Professional string formatting with
sprintf - Handling international characters with multibyte functions
- A practical URL slug generator function
- A simple CSV parser
Quick Start
If you want to see string manipulation in action immediately, create a file called quick-strings.php:
<?php
// Quick demonstration of PHP string power
$text = " Learn PHP 8.4 Today! ";
echo "Original: '$text'\n";
echo "Cleaned: '" . trim($text) . "'\n";
echo "Lowercase: " . strtolower($text) . "\n";
echo "Slug: " . strtolower(str_replace(' ', '-', trim($text))) . "\n";
// Modern PHP 8.0+ features
if (str_contains($text, 'PHP')) {
echo "This text is about PHP!\n";
}Run it:
php quick-strings.phpNow let's dive deeper into each capability.
Objectives
- Find the length of a string and access individual characters
- Use modern PHP 8.0+ string search functions (
str_contains,str_starts_with,str_ends_with) - Search for and replace substrings using multiple methods
- Change the case of a string (uppercase/lowercase)
- Split a string into an array and join an array into a string
- Format strings professionally using
sprintf - Handle multibyte (international) strings safely
Step 1: Basic String Operations (~4 min)
Let's start with some of the most fundamental operations: finding a string's length, changing its case, and trimming whitespace.
Goal: Learn to measure, clean, and transform string casing.
Actions:
Create a new file named
01-basic-strings.php.Add the following code:
<?php
// 01-basic-strings.php - Basic string operations
$sentence = " The quick brown fox jumps over the lazy dog. ";
// 1. Get the length of the string
$length = strlen($sentence);
echo "Original length: $length characters\n";
// 2. Convert to uppercase and lowercase
$upper = strtoupper($sentence);
$lower = strtolower($sentence);
echo "Uppercase: $upper\n";
echo "Lowercase: $lower\n";
// 3. Trim whitespace from the beginning and end
$trimmed = trim($sentence);
echo "Trimmed length: " . strlen($trimmed) . " characters\n";
echo "Trimmed: '$trimmed'\n";
// 4. Trim only specific sides
$leftTrimmed = ltrim($sentence); // Remove left whitespace
$rightTrimmed = rtrim($sentence); // Remove right whitespace
echo "Left trimmed: '$leftTrimmed'\n";
echo "Right trimmed: '$rightTrimmed'\n";- Run the file:
php 01-basic-strings.phpExpected Result:
Original length: 49 characters
Uppercase: THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.
Lowercase: the quick brown fox jumps over the lazy dog.
Trimmed length: 45 characters
Trimmed: 'The quick brown fox jumps over the lazy dog.'
Left trimmed: 'The quick brown fox jumps over the lazy dog. '
Right trimmed: ' The quick brown fox jumps over the lazy dog.'Why it works:
strlen(): Returns the number of bytes in the string (which equals the number of characters for ASCII text).strtoupper()/strtolower(): Convert the entire string to uppercase or lowercase.trim(): Removes whitespace (or other specified characters) from both ends—essential for cleaning up user input.ltrim()/rtrim(): Target only the left or right side.
Note: For strings containing international characters (like "Ñoño" or "日本語"), use the mb_* functions like mb_strlen() and mb_strtoupper() to handle multibyte characters correctly. We'll cover this at the end of the chapter.
Step 2: Extracting and Transforming Substrings (~5 min)
Often you need to extract part of a string or change its capitalization style. These functions are essential for formatting names, titles, excerpts, and display text.
Goal: Learn to extract substrings and apply title/sentence case transformations.
Actions:
Create a file named
02-substrings.php.Add this code:
<?php
// 02-substrings.php - Extracting and transforming substrings
// 1. substr() - Extract part of a string
$text = "Hello, World!";
$first5 = substr($text, 0, 5); // Start at 0, take 5 chars
$world = substr($text, 7, 5); // Start at 7, take 5 chars
$last6 = substr($text, -6); // Last 6 characters (negative offset)
$withoutLast = substr($text, 0, -1); // Everything except last char
echo "Original: $text\n";
echo "First 5 chars: $first5\n";
echo "Middle portion: $world\n";
echo "Last 6 chars: $last6\n";
echo "Without last char: $withoutLast\n\n";
// 2. Title case and capitalization
$name = "john doe";
$title = "the quick brown fox";
$sentence = "hello world";
echo "ucfirst: " . ucfirst($sentence) . "\n"; // Capitalize first letter
echo "ucwords: " . ucwords($title) . "\n"; // Capitalize Each Word
echo "Name formatted: " . ucwords($name) . "\n\n"; // John Doe
// 3. lcfirst() - Lowercase first letter (useful for camelCase)
$className = "MyClass";
echo "Variable name: " . lcfirst($className) . "\n\n"; // myClass
// 4. Creating excerpts (common use case)
$article = "This is a very long article about PHP string manipulation. It contains multiple sentences and demonstrates how to create excerpts from longer text.";
$excerpt = substr($article, 0, 50) . "...";
echo "Excerpt: $excerpt\n\n";
// 5. nl2br() - Convert newlines to HTML <br> tags
$userInput = "Line 1\nLine 2\nLine 3";
echo "Plain text:\n$userInput\n\n";
echo "HTML formatted:\n" . nl2br($userInput) . "\n\n";
// 6. str_pad() - Pad strings to a fixed width
$id = "42";
$price = "9.99";
echo "Invoice:\n";
echo "ID: " . str_pad($id, 5, "0", STR_PAD_LEFT) . "\n"; // 00042
echo "Price: $" . str_pad($price, 8, " ", STR_PAD_LEFT) . "\n"; // Align right
// 7. Counting functions
$text = "The quick brown fox jumps over the lazy dog.";
$wordCount = str_word_count($text);
$theCount = substr_count(strtolower($text), 'the');
echo "\nWord count: $wordCount\n";
echo "Occurrences of 'the': $theCount\n";- Run the file:
php 02-substrings.phpExpected Result:
Original: Hello, World!
First 5 chars: Hello
Middle portion: World
Last 6 chars: World!
Without last char: Hello, World
ucfirst: Hello world
ucwords: The Quick Brown Fox
Name formatted: John Doe
Variable name: myClass
Excerpt: This is a very long article about PHP string m...
Plain text:
Line 1
Line 2
Line 3
HTML formatted:
Line 1<br />
Line 2<br />
Line 3<br />
Invoice:
ID: 00042
Price: $ 9.99
Word count: 9
Occurrences of 'the': 2Why it works:
substr(string $string, int $offset, ?int $length): Extracts a portion of a string. Negative offsets count from the end.ucfirst(): Converts the first character to uppercase—perfect for sentences.ucwords(): Capitalizes the first letter of each word—ideal for names and titles.lcfirst(): Lowercase first character—useful for variable naming conventions.nl2br(): Converts newline characters (\n) to HTML<br />tags for web display.str_pad(): Pads a string to a certain length with another string (defaults to spaces).str_word_count(): Counts the number of words in a string.substr_count(): Counts how many times a substring occurs.
Common Use Cases:
- Blog excerpts:
substr($content, 0, 150) . '...' - Name formatting:
ucwords(strtolower($userName)) - Displaying user comments:
nl2br(htmlspecialchars($comment)) - Report alignment:
str_pad($value, 20) - Reading indicators:
str_word_count($article) . ' words'
Troubleshooting:
substr()returns false: The offset is beyond the string length. Always validate string length first.- Wrong character extracted: Remember PHP uses zero-based indexing. Position 0 is the first character.
ucwords()doesn't work for names like "O'Brien": Usemb_convert_case($name, MB_CASE_TITLE, 'UTF-8')for better handling of special characters.nl2br()doubles line breaks in HTML: If your text already has<br>tags, you might see duplicates. Use the second parameter:nl2br($text, false)for XHTML compatibility.
Step 3: Modern String Searching (PHP 8.0+) (~4 min)
PHP 8.0 introduced cleaner, more readable string searching functions. These are now the recommended way to check if a string contains, starts with, or ends with specific text.
Goal: Learn modern, intuitive string search methods.
Actions:
Create a file named
03-modern-search.php.Add this code:
<?php
// 03-modern-search.php - Modern PHP 8.0+ string search
$sentence = "The quick brown fox jumps over the lazy dog.";
$email = "user@example.com";
$filename = "document.pdf";
// Check if a string contains a substring
if (str_contains($sentence, 'fox')) {
echo "✓ The sentence contains 'fox'\n";
}
// Check if a string starts with a prefix
if (str_starts_with($email, 'user')) {
echo "✓ The email starts with 'user'\n";
}
// Check if a string ends with a suffix
if (str_ends_with($filename, '.pdf')) {
echo "✓ The filename ends with '.pdf'\n";
}
// Case-insensitive searching
if (str_contains(strtolower($sentence), 'fox')) {
echo "✓ Case-insensitive: found 'fox'\n";
}
// Combining checks for validation
function isValidEmail(string $email): bool {
return str_contains($email, '@') && str_contains($email, '.');
}
echo isValidEmail('test@example.com') ? "✓ Valid email\n" : "✗ Invalid email\n";- Run the file:
php 03-modern-search.phpExpected Result:
✓ The sentence contains 'fox'
✓ The email starts with 'user'
✓ The filename ends with '.pdf'
✓ Case-insensitive: found 'fox'
✓ Valid emailWhy it works: These functions return simple true or false values, making your code much more readable than the older strpos() !== false pattern. They're also slightly faster than strpos() because they don't need to calculate the position.
Troubleshooting:
- Error: "Call to undefined function str_contains()": You're running PHP 7.x. These functions require PHP 8.0+. Check your version with
php -v. - Not finding text that's clearly there: These functions are case-sensitive. Use
strtolower()on both the haystack and needle for case-insensitive searches.
Step 4: Finding Positions and Replacing Text (~5 min)
Sometimes you need to know exactly where a substring appears, or you need to replace text.
Goal: Learn to find substring positions and perform text replacements.
Actions:
Create a file named
04-find-replace.php.Add this code:
<?php
// 04-find-replace.php - Finding positions and replacing text
$sentence = "The quick brown fox jumps over the lazy dog.";
// Find the numeric position of the first occurrence
$position = strpos($sentence, 'fox');
// IMPORTANT: Use === because strpos returns 0 for matches at the start,
// and 0 == false, but 0 !== false
if ($position !== false) {
echo "Found 'fox' at position: $position\n";
echo "Character at that position: " . $sentence[$position] . "\n";
} else {
echo "The word 'fox' was not found.\n";
}
// Replace a single occurrence
$newSentence = str_replace('dog', 'cat', $sentence);
echo "Single replacement: $newSentence\n";
// Replace multiple different strings at once
$find = ['brown', 'lazy'];
$replace = ['red', 'energetic'];
$multiReplace = str_replace($find, $replace, $sentence);
echo "Multiple replacements: $multiReplace\n";
// Count how many replacements were made
$count = 0;
$text = "test test test";
$result = str_replace('test', 'example', $text, $count);
echo "Replaced $count occurrences: $result\n";
// Case-insensitive replacement
$text = "PHP is great. php is powerful. Php is popular.";
$caseInsensitive = str_ireplace('php', 'Python', $text);
echo "Case-insensitive: $caseInsensitive\n";- Run the file:
php 04-find-replace.phpExpected Result:
Found 'fox' at position: 16
Character at that position: f
Single replacement: The quick brown fox jumps over the lazy cat.
Multiple replacements: The quick red fox jumps over the energetic dog.
Replaced 3 occurrences: example example example
Case-insensitive: Python is great. Python is powerful. Python is popular.Why it works:
strpos(): Returns the zero-based index of the first match, orfalseif not found.str_replace(): Replaces all occurrences. Pass arrays to do multiple find-and-replace operations in one call.str_ireplace(): Case-insensitive version ofstr_replace().
Troubleshooting:
- Bug:
if ($position)doesn't work when the match is at position 0: Always use!== falsewithstrpos()to avoid this classic PHP gotcha. - Unexpected replacements:
str_replace()replaces all occurrences. If you need to replace only the first occurrence, usepreg_replace()with a limit parameter.
Step 5: Splitting and Joining Strings (~4 min)
You'll frequently need to break a string apart into an array, or join array elements into a single string.
Goal: Master string-to-array and array-to-string conversions.
Actions:
Create a file named
05-split-join.php.Add this code:
<?php
// 05-split-join.php - Splitting and joining strings
// A comma-separated list of tags
$tagsString = "php, web development, programming, tutorial";
// Explode the string into an array using the delimiter ', '
$tagsArray = explode(', ', $tagsString);
echo "Array from string:\n";
print_r($tagsArray);
// Implode the array back into a string with a different separator
$newTagsString = implode(' | ', $tagsArray);
echo "\nJoined with pipes: $newTagsString\n";
// Practical example: building a URL-friendly parameter string
$params = [
'search' => 'php tutorial',
'category' => 'programming',
'sort' => 'newest'
];
$queryString = [];
foreach ($params as $key => $value) {
$queryString[] = "$key=" . urlencode($value);
}
$url = 'https://example.com/search?' . implode('&', $queryString);
echo "\nBuilt URL: $url\n";
// Splitting by single character (space)
$sentence = "The quick brown fox";
$words = explode(' ', $sentence);
echo "\nWord count: " . count($words) . "\n";
echo "Words: " . implode(', ', $words) . "\n";- Run the file:
php 05-split-join.phpExpected Result:
Array from string:
Array
(
[0] => php
[1] => web development
[2] => programming
[3] => tutorial
)
Joined with pipes: php | web development | programming | tutorial
Built URL: https://example.com/search?search=php+tutorial&category=programming&sort=newest
Word count: 4
Words: The, quick, brown, foxWhy it works:
explode(string $separator, string $string): Splits a string into an array using the specified delimiter. The delimiter is not included in the results.implode(string $separator, array $array): Joins array elements with the separator string (also calledjoin()—they're aliases).
Troubleshooting:
- Getting unexpected array elements: Make sure your delimiter exactly matches what's in the string.
explode(',', 'a, b')creates['a', ' b'](note the space). - Empty string results in array with one empty element:
explode(',', '')returns[''], not[]. Check for empty strings before exploding if this matters.
Step 6: Formatting Strings with sprintf (~4 min)
When you need to create complex strings from various pieces of data, concatenation gets messy. sprintf (string print formatted) lets you create strings from templates with placeholders.
Goal: Format strings professionally with precise control over output.
Actions:
Create a file named
06-sprintf.php.Add this code:
<?php
// 06-sprintf.php - Professional string formatting
$product = 'Laptop';
$quantity = 3;
$price = 1200.50;
// Basic sprintf with common placeholders
$format = "You ordered %d units of the %s, for a total of $%.2f.";
$output = sprintf($format, $quantity, $product, $quantity * $price);
echo $output . "\n\n";
// Padding and alignment
$id = 42;
$name = "Alice";
$score = 87.5;
// %05d = pad to 5 digits with zeros
// %-15s = left-align in 15-character field
// %6.2f = 6 total characters, 2 decimal places
echo sprintf("ID: %05d | Name: %-15s | Score: %6.2f%%\n", $id, $name, $score);
// More examples
echo sprintf("ID: %05d | Name: %-15s | Score: %6.2f%%\n", 7, "Bob", 92.33);
echo sprintf("ID: %05d | Name: %-15s | Score: %6.2f%%\n", 123, "Charlie", 78.9);
// Argument swapping (useful for internationalization)
$format_en = "%s costs $%.2f";
$format_fr = "%.2f$ est le prix de %s"; // French puts price first
echo sprintf($format_en, "Book", 29.99) . "\n";
echo sprintf($format_fr, 29.99, "Livre") . "\n";
// Date formatting with sprintf
$year = 2024;
$month = 3;
$day = 7;
$date = sprintf("%04d-%02d-%02d", $year, $month, $day);
echo "\nFormatted date: $date\n";
// Hexadecimal and binary
$number = 255;
echo sprintf("Decimal: %d, Hex: %x, Binary: %b\n", $number, $number, $number);- Run the file:
php 06-sprintf.phpExpected Result:
You ordered 3 units of the Laptop, for a total of $3601.50.
ID: 00042 | Name: Alice | Score: 87.50%
ID: 00007 | Name: Bob | Score: 92.33%
ID: 00123 | Name: Charlie | Score: 78.90%
Book costs $29.99
29.99$ est le prix de Livre
Formatted date: 2024-03-07
Decimal: 255, Hex: ff, Binary: 11111111Why it works:
%s: String%d: Integer (decimal)%f: Floating-point number%.2f: Float with 2 decimal places%05d: Pad integer to 5 digits with leading zeros%-15s: Left-align string in 15-character field%x/%b: Hexadecimal / binary representation
Tip: Use sprintf for building SQL queries (though use prepared statements for user data), formatting reports, creating fixed-width tables, and internationalizing number/date formats.
Troubleshooting:
- Wrong number of arguments:
sprintfmust receive exactly as many arguments as there are%placeholders (excluding%%for literal %). - Type mismatch warnings: Passing a string to
%dwill trigger a warning in PHP 8+. Cast explicitly:(int)$value.
Step 7: Working with Multibyte Strings (~3 min)
When working with international text (Chinese, Japanese, Arabic, emoji, etc.), standard string functions can give incorrect results because they count bytes, not characters.
Goal: Learn to handle international text correctly.
Actions:
Create a file named
07-multibyte.php.Add this code:
<?php
// 07-multibyte.php - Handling international text
$ascii = "Hello";
$japanese = "こんにちは"; // "Hello" in Japanese
$emoji = "Hello 👋🌍";
// Problem: strlen() counts bytes, not characters
echo "ASCII strlen: " . strlen($ascii) . "\n";
echo "Japanese strlen: " . strlen($japanese) . "\n"; // Wrong!
echo "Emoji strlen: " . strlen($emoji) . "\n"; // Wrong!
echo "\n";
// Solution: Use mb_strlen() for character count
echo "ASCII mb_strlen: " . mb_strlen($ascii) . "\n";
echo "Japanese mb_strlen: " . mb_strlen($japanese) . "\n"; // Correct!
echo "Emoji mb_strlen: " . mb_strlen($emoji) . "\n"; // Correct!
echo "\n";
// Other multibyte functions
$text = "Ñoño";
echo "strtoupper: " . strtoupper($text) . "\n"; // May be wrong
echo "mb_strtoupper: " . mb_strtoupper($text) . "\n"; // Correct!
// Substring with multibyte support
$text = "你好世界"; // "Hello World" in Chinese
echo "First 2 chars: " . mb_substr($text, 0, 2) . "\n";- Run the file:
php 07-multibyte.phpExpected Result:
ASCII strlen: 5
Japanese strlen: 15
Emoji strlen: 13
ASCII mb_strlen: 5
Japanese mb_strlen: 5
Emoji mb_strlen: 9
strtoupper: ÑOñO
mb_strtoupper: ÑOÑO
First 2 chars: 你好Why it works: The mb_* (multibyte) functions are aware of character encoding and count characters correctly, not bytes. Most modern applications should use these functions by default.
Tip: Set a default encoding at the start of your script: mb_internal_encoding('UTF-8');
Troubleshooting:
- Error: "Call to undefined function mb_strlen()": The mbstring extension isn't enabled. Install it:
apt-get install php-mbstring(Linux) or enable it inphp.ini. - Still getting wrong results: Check your file encoding. Save files as UTF-8 without BOM.
Exercises
Create a file exercises.php to complete these challenges.
Exercise 1: URL Slug Generator
Create a function generateSlug($title) that converts blog post titles into URL-friendly slugs.
Requirements:
- Input: "My Awesome First Post!"
- Output: "my-awesome-first-post"
Steps:
- Convert the string to lowercase
- Replace all spaces with hyphens (
-) - Remove any characters that aren't letters, numbers, or hyphens
- Remove any duplicate hyphens
Hint: Use strtolower(), str_replace(), and preg_replace('/[^a-z0-9-]+/', '', $text) to remove unwanted characters.
Validation: Test with these inputs:
echo generateSlug("My Awesome First Post!"); // my-awesome-first-post
echo generateSlug("PHP 8.4: What's New?"); // php-84-whats-new
echo generateSlug(" Hello World "); // hello-worldExercise 2: CSV Parser
Parse a multi-line CSV string into formatted output.
Given:
$csv = "dale,dale@example.com,Admin\nalice,alice@example.com,Editor\nbob,bob@example.com,Viewer";Requirements:
- Split the string into individual lines
- For each line, split by comma to get user data
- Format and print each user nicely
Expected Output:
User #1: dale (dale@example.com) - Admin
User #2: alice (alice@example.com) - Editor
User #3: bob (bob@example.com) - ViewerHint: Use nested explode() calls and a counter variable.
Exercise 3: Password Strength Checker
Create a function checkPasswordStrength($password) that returns a string describing the password strength.
Requirements:
- "Weak": Less than 8 characters
- "Medium": 8+ characters
- "Strong": 8+ characters AND contains both uppercase and lowercase
- "Very Strong": All above AND contains a number
Hints: Use strlen(), str_contains() with character ranges, or check for uppercase/lowercase with comparison.
Validation:
echo checkPasswordStrength("pass"); // Weak
echo checkPasswordStrength("password"); // Medium
echo checkPasswordStrength("Password"); // Strong
echo checkPasswordStrength("Password123"); // Very StrongWrap-up
Congratulations! You now have a comprehensive toolkit for working with strings in PHP. Let's recap what you've learned:
Core Skills Acquired:
- Measuring, cleaning, and transforming strings with
strlen(),trim(), and case functions - Extracting substrings with
substr()and creating text excerpts - Formatting titles and names with
ucfirst(),ucwords(), andlcfirst() - Converting plain text to HTML with
nl2br()and padding withstr_pad() - Counting words and substring occurrences
- Using modern PHP 8.0+ search functions (
str_contains,str_starts_with,str_ends_with) - Finding positions and replacing text with
strpos()andstr_replace() - Converting between strings and arrays with
explode()andimplode() - Professional formatting with
sprintf() - Handling international text with multibyte functions
Real-World Applications:
- Cleaning and validating user input
- Creating URL slugs and search-friendly text
- Parsing CSV, log files, and other delimited data
- Building dynamic messages and reports
- Processing multilingual content
Next Steps: With our understanding of core data types like strings and arrays solidified, we're ready to move on to a major new paradigm in programming: Object-Oriented Programming (OOP). In Chapter 08, we'll learn how to model real-world concepts using classes and objects.
Code Examples
Complete, runnable examples from this chapter are available in:
basic-strings.php- String basics and common operationssearch-replace.php- Searching and replacing in stringssplit-join.php- Splitting and joining stringssolutions/- Solutions to chapter exercises
Further Reading
- PHP String Functions — Complete reference
- PHP Multibyte String Functions — For international text
- Regular Expressions in PHP — Advanced pattern matching
- PHP 8.0 Release Notes — New string functions
Knowledge Check
Test your understanding of string manipulation: