07: Mastering String Manipulation

Chapter 07: Mastering String Manipulation
Section titled “Chapter 07: Mastering String Manipulation”Overview
Section titled “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
Section titled “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
Section titled “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
Section titled “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+ featuresif (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
Section titled “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)
Section titled “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 whitespaceecho "Left trimmed: '$leftTrimmed'\n";echo "Right trimmed: '$rightTrimmed'\n";- Run the file:
php 01-basic-strings.phpExpected Result:
Original length: 49 charactersUppercase: THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.Lowercase: the quick brown fox jumps over the lazy dog.Trimmed length: 45 charactersTrimmed: '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)
Section titled “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 letterecho "ucwords: " . ucwords($title) . "\n"; // Capitalize Each Wordecho "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"; // 00042echo "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: HelloMiddle portion: WorldLast 6 chars: World!Without last char: Hello, World
ucfirst: Hello worlducwords: The Quick Brown FoxName formatted: John Doe
Variable name: myClass
Excerpt: This is a very long article about PHP string m...
Plain text:Line 1Line 2Line 3
HTML formatted:Line 1<br />Line 2<br />Line 3<br />
Invoice:ID: 00042Price: $ 9.99
Word count: 9Occurrences 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)
Section titled “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 substringif (str_contains($sentence, 'fox')) { echo "✓ The sentence contains 'fox'\n";}
// Check if a string starts with a prefixif (str_starts_with($email, 'user')) { echo "✓ The email starts with 'user'\n";}
// Check if a string ends with a suffixif (str_ends_with($filename, '.pdf')) { echo "✓ The filename ends with '.pdf'\n";}
// Case-insensitive searchingif (str_contains(strtolower($sentence), 'fox')) { echo "✓ Case-insensitive: found 'fox'\n";}
// Combining checks for validationfunction 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)
Section titled “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 !== falseif ($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: 16Character at that position: fSingle 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 exampleCase-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)
Section titled “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: 4Words: 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)
Section titled “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 placesecho sprintf("ID: %05d | Name: %-15s | Score: %6.2f%%\n", $id, $name, $score);
// More examplesecho 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 firstecho 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.9929.99$ est le prix de Livre
Formatted date: 2024-03-07Decimal: 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)
Section titled “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 charactersecho "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 countecho "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 wrongecho "mb_strtoupper: " . mb_strtoupper($text) . "\n"; // Correct!
// Substring with multibyte support$text = "你好世界"; // "Hello World" in Chineseecho "First 2 chars: " . mb_substr($text, 0, 2) . "\n";- Run the file:
php 07-multibyte.phpExpected Result:
ASCII strlen: 5Japanese strlen: 15Emoji strlen: 13
ASCII mb_strlen: 5Japanese mb_strlen: 5Emoji mb_strlen: 9
strtoupper: ÑOñOmb_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
Section titled “Exercises”Create a file exercises.php to complete these challenges.
Exercise 1: URL Slug Generator
Section titled “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-postecho generateSlug("PHP 8.4: What's New?"); // php-84-whats-newecho generateSlug(" Hello World "); // hello-worldExercise 2: CSV Parser
Section titled “Exercise 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) - AdminUser #2: alice (alice@example.com) - EditorUser #3: bob (bob@example.com) - ViewerHint: Use nested explode() calls and a counter variable.
Exercise 3: Password Strength Checker
Section titled “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"); // Weakecho checkPasswordStrength("password"); // Mediumecho checkPasswordStrength("Password"); // Strongecho checkPasswordStrength("Password123"); // Very StrongWrap-up
Section titled “Wrap-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.
::: info 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
Section titled “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
Section titled “Knowledge Check”Test your understanding of string manipulation: