Himanshu SharmaSoftware engineer
Let’s talk

PHP · Lesson 28 of 54

Searching, slicing and replacing text

Search, replace, slice, trim, pad, split and change the case of text with PHP's string functions, including str_contains, str_starts_with and str_replace.

13 minStrings, dates and patternsUpdated 22 October 2026

Awaiting technical review. This lesson is readable but not yet listed in search engines.

By the end of this lessonUse PHP's core string functions to search, slice, clean, replace and format text.

Text is everywhere

Websites spend much of their time handling text: names typed into forms, product descriptions, URLs, email addresses, search terms. PHP has a large set of string functions for this. This lesson covers the ones you will use every week. All of them work on bytes, which is fine for English text; for accents and other scripts, see the next lesson on Unicode text and the mb_ functions.

Searching

PHP 8 added three clear functions that answer yes or no:

php
<?php
$title = "Mechanical Keyboard with RGB lights";

var_dump(str_contains($title, "Keyboard"));
var_dump(str_contains($title, "keyboard"));
var_dump(str_starts_with($title, "Mech"));
var_dump(str_ends_with("invoice.pdf", ".pdf"));
result
bool(true)
bool(false)
bool(true)
bool(true)

They are case-sensitive. To find a position, use strpos() (case-sensitive) or stripos() (case-insensitive). They return the position of the first match, counting from 0, or false if there is none:

php
<?php
$title = "Mechanical Keyboard with RGB lights";

var_dump(strpos($title, "Keyboard"));
var_dump(stripos($title, "keyboard"));
var_dump(strpos($title, "Mouse"));
var_dump(strrpos("a-b-c", "-"));
echo substr_count("banana", "a") . "\n";
result
int(11)
int(11)
bool(false)
int(3)
3

As explained in Comparisons, == versus === and type juggling, a match at position 0 is falsy, so always test strpos(...) === false, or use str_contains() when you only need yes or no. strrpos() finds the last match.

Cutting out parts

substr($string, $start, $length) returns part of a string. A negative start counts from the end:

php
<?php
$sku = "ELEC-KB-2024-0042";

echo substr($sku, 0, 4) . "\n";
echo substr($sku, -4) . "\n";
echo substr($sku, 5, 2) . "\n";
echo strlen($sku) . "\n";
result
ELEC
0042
KB
17

For text separated by a character, explode() from Common array functions is usually clearer than counting positions: explode("-", $sku)[1] is KB.

Cleaning input

Text from forms often has stray spaces at the ends. trim() removes whitespace (spaces, tabs, new lines) from both ends; ltrim() and rtrim() do one side. A second argument lists other characters to remove:

php
<?php
$email = "  Asha.Verma@Example.com \n";
$clean = strtolower(trim($email));
echo "[$clean]\n";

echo rtrim("1,2,3,", ",") . "\n";
echo trim("--SALE--", "-") . "\n";
result
[asha.verma@example.com]
1,2,3
SALE

Changing case

php
<?php
$name = "kAVYA nAIR";

echo strtolower($name) . "\n";
echo strtoupper($name) . "\n";
echo ucfirst(strtolower($name)) . "\n";
echo ucwords(strtolower($name)) . "\n";
echo lcfirst("Hello") . "\n";
result
kavya nair
KAVYA NAIR
Kavya nair
Kavya Nair
hello

ucwords() capitalises the first letter of every word, which is handy for names and titles, but be careful with names such as "O'Brien" or "McDonald": no simple function gets every name right, so do not "correct" what people type unless you have to.

Replacing

str_replace($search, $replace, $subject) replaces every occurrence. It also accepts arrays, applied pair by pair, and an optional fourth argument receives the number of replacements:

php
<?php
echo str_replace("colour", "color", "Pick a colour, any colour") . "\n";

$template = "Hi {name}, your order {order} has shipped.";
echo str_replace(["{name}", "{order}"], ["Liam", "#1042"], $template) . "\n";

str_replace(" ", "-", "a b c d", $count);
echo "Replaced $count spaces\n";
echo str_ireplace("SALE", "offer", "Big sale today") . "\n";
result
Pick a color, any color
Hi Liam, your order #1042 has shipped.
Replaced 3 spaces
Big offer today

str_ireplace() ignores case. For patterns rather than fixed text, such as "any run of digits", use preg_replace() from Regular expressions with preg_ functions. strtr() replaces several strings at once from a key-value array.

Padding, repeating and wrapping

php
<?php
echo str_pad("42", 6, "0", STR_PAD_LEFT) . "\n";
echo str_pad("Total", 10, ".") . "99.00\n";
echo str_repeat("-", 20) . "\n";
echo wordwrap("The quick brown fox jumps over the lazy dog", 15, "\n", true) . "\n";
echo strrev("stressed") . "\n";
result
000042
Total.....99.00
--------------------
The quick brown
fox jumps over
the lazy dog
desserts

Building a URL slug

Putting several functions together: turn a product name into a URL-friendly "slug" like the ones this site uses for lesson addresses:

php
<?php
function slugify(string $text): string
{
    $text = strtolower(trim($text));
    $text = preg_replace('/[^a-z0-9]+/', '-', $text);
    return trim($text, '-');
}

echo slugify("  Noise-Cancelling Headphones (Black) ") . "\n";
echo slugify("PHP & MySQL: The Basics!") . "\n";
result
noise-cancelling-headphones-black
php-mysql-the-basics

The middle line uses a regular expression to turn every run of characters that are not letters or digits into one hyphen.

Comparing strings

strcmp() compares two strings and returns a negative number, zero or a positive number, like the spaceship operator; strcasecmp() ignores case. To compare secret values such as tokens, use hash_equals(), which takes the same time whatever the input and so does not leak information through timing.

Common mistakes

  • Testing strpos() with if (strpos(...)), which fails when the match is at position 0.
  • Forgetting that most functions are case-sensitive. Use the i versions or lowercase both sides.
  • Using these functions on non-English text and cutting a character in half. Use the mb_ versions.
  • Not trimming input, so "asha@example.com " with a trailing space fails to match.

Exercise

Write maskEmail(string $email): string that keeps the first letter of the name and the whole domain, replacing the rest of the name with asterisks: asha.verma@example.com becomes a*********@example.com.

Show one possible answer
php
<?php
function maskEmail(string $email): string
{
    $at = strpos($email, "@");
    if ($at === false || $at < 1) {
        return $email;
    }
    $name = substr($email, 0, $at);
    $domain = substr($email, $at);
    return substr($name, 0, 1) . str_repeat("*", strlen($name) - 1) . $domain;
}

echo maskEmail("asha.verma@example.com") . "\n";
echo maskEmail("li@example.org") . "\n";
result
a*********@example.com
l*@example.org

Key points

  • str_contains, str_starts_with and str_ends_with (PHP 8) answer yes-or-no questions about text.
  • strpos returns a position or false; always compare its result with === false.
  • substr cuts out part of a string; trim removes whitespace from both ends.
  • str_replace replaces text, ucfirst / ucwords / strtolower / strtoupper change case, and str_pad pads to a width.

Check yourself

1. What does substr('Keyboard', 0, 3) return?
2. What does str_contains('Mechanical Keyboard', 'key') return?
3. What does trim(' hello ') return?
4. What does str_replace(['a', 'b'], ['1', '2'], 'abc') return?