PHP String Functions
last modified November 14, 2026
In this article, we go through the built-in PHP functions for working with strings. We search, extract, replace, trim, pad, split, join and compare text, and finish with a few practical helper functions.
Strings are the most common data type in web programming. PHP has a very rich library of string functions, so it is rarely necessary to write a loop over the characters by hand. The basics of strings are described in the PHP string tutorial; here we focus on the functions.
Most of these functions treat a string as a sequence of bytes. That is fine for ASCII text, but it matters for UTF-8 text, as we show in the section about multibyte strings. All outputs below were produced with PHP 8.5.
Length and case
The first group of functions measures a string and changes the case of its letters.
<?php
$s = 'the quick brown fox';
echo strlen($s), "\n";
echo strtoupper($s), "\n";
echo strtolower('HELLO World'), "\n";
echo ucfirst($s), "\n";
echo ucwords($s), "\n";
echo lcfirst('Hello'), "\n";
echo ucwords('hello-world foo_bar', '-'), "\n";
echo str_word_count($s), "\n";
echo strrev('stressed'), "\n";
echo str_repeat('=-', 10), "\n";
strlen returns the number of bytes. strtoupper and
strtolower change the whole string, ucfirst and
lcfirst change only the first letter, and ucwords
capitalizes every word. A second argument of ucwords lists the
word delimiters.
str_word_count counts words, strrev reverses a
string, and str_repeat repeats it.
$ php length_case.php 19 THE QUICK BROWN FOX hello world The quick brown fox The Quick Brown Fox hello Hello-World foo_bar 4 desserts =-=-=-=-=-=-=-=-=-=-
Searching in strings
To find a substring, we use strpos. It returns the position of the
first occurrence, counted from zero, or false when there is none.
<?php
$text = 'The quick brown fox jumps over the lazy dog';
var_dump(strpos($text, 'quick'));
var_dump(strpos($text, 'cat'));
var_dump(stripos($text, 'THE'));
var_dump(strrpos($text, 'the'));
var_dump(strpos($text, 'The') !== false);
var_dump(str_contains($text, 'fox'));
var_dump(str_starts_with($text, 'The'));
var_dump(str_ends_with($text, 'dog'));
echo substr_count($text, 'o'), "\n";
echo strstr($text, 'jumps'), "\n";
echo strstr($text, 'jumps', true), "\n";
echo strrchr('/var/www/html/index.php', '/'), "\n";
stripos ignores case and strrpos searches from the
end. The three functions str_contains, str_starts_with
and str_ends_with (PHP 8.0) answer simple yes/no questions and
should be preferred when we do not need the position.
substr_count counts occurrences. strstr returns the
part of the string from the first match to the end, or the part before the match
when the third argument is true. strrchr works from
the last occurrence of a character, which is handy for file names.
$ php searching.php int(4) bool(false) int(0) int(31) bool(true) bool(true) bool(true) bool(true) 4 jumps over the lazy dog The quick brown fox /index.php
The strpos pitfall
The most common bug with strpos is to use its result as a
condition. When the needle is at the very start of the string, the function
returns 0, which is falsy.
<?php
$text = 'PHP is great';
if (strpos($text, 'PHP')) {
echo "found (loose check)\n";
} else {
echo "not found (loose check)\n";
}
if (strpos($text, 'PHP') !== false) {
echo "found (strict check)\n";
}
if (str_contains($text, 'PHP')) {
echo "found (str_contains)\n";
}
The text begins with PHP, so strpos returns
0 and the loose check fails. We must compare with
!== false, or use str_contains, which returns a
boolean.
$ php strpos_pitfall.php not found (loose check) found (strict check) found (str_contains)
Extracting substrings
The substr function returns a part of a string given a start
position and an optional length.
<?php $s = 'Hello, World!'; echo substr($s, 7), "\n"; echo substr($s, 0, 5), "\n"; echo substr($s, -6), "\n"; echo substr($s, -6, 5), "\n"; echo substr($s, 3, -3), "\n"; var_dump(substr($s, 20)); echo substr_replace($s, 'PHP', 7, 5), "\n"; echo substr_replace($s, '***', -6, 5), "\n"; echo $s[0], $s[-1], "\n";
Negative values count from the end of the string. A negative start selects the
last characters, and a negative length stops that many characters before the
end. Since PHP 8.0, a start beyond the end returns an empty string rather than
false.
substr_replace replaces a part of a string by position. A single
character can be read with the array syntax, including negative indexes.
$ php substr.php World! Hello World! World lo, Wor string(0) "" Hello, PHP! Hello, ***! H!
Replacing text
The str_replace function replaces all occurrences of a substring.
<?php
$text = 'I like cats. Cats are great. I have two cats.';
echo str_replace('cats', 'dogs', $text, $count), "\n";
echo "Replacements: $count\n";
echo str_ireplace('cats', 'dogs', $text), "\n";
echo str_replace(['cats', 'great'], ['birds', 'noisy'], $text), "\n";
echo strtr('Hi all', ['Hi' => 'Hello', 'all' => 'world']), "\n";
echo strtr('abc', 'ab', 'xy'), "\n";
The optional fourth argument receives the number of replacements. We can also
pass arrays of searches and replacements, which are applied one after another.
str_ireplace is the case-insensitive variant; that is why the
capitalized Cats is replaced too.
strtr translates characters, or, given an array, replaces several
strings in a single pass without re-scanning the replaced text. The output
shows both forms.
$ php replace.php I like dogs. Cats are great. I have two dogs. Replacements: 2 I like dogs. dogs are great. I have two dogs. I like birds. Cats are noisy. I have two birds. Hello world xyc
Trimming and padding
Input from users and files often has surplus whitespace. The functions
trim, ltrim and rtrim remove characters
from both ends, the left end and the right end.
<?php
$s = " \t Hello, World! \n";
var_dump(trim($s));
var_dump(ltrim($s));
var_dump(rtrim($s));
echo trim('--[PHP]--', '-'), "\n";
echo trim('xxhixx', 'x'), "\n";
echo rtrim('1.5000', '0'), "\n";
echo trim('abcPHPcba', 'a..c'), "\n";
echo str_pad('42', 6, '0', STR_PAD_LEFT), "\n";
echo str_pad('left', 10, '.'), "|\n";
echo str_pad('mid', 9, '*', STR_PAD_BOTH), "\n";
echo wordwrap('The quick brown fox sat over the lazy dog', 15, "\n", true), "\n";
By default they strip spaces, tabs, newlines and a few other whitespace
characters. A second argument lists the characters to remove, and a range such
as a..c is allowed. Note that it is a set of characters,
not a prefix or suffix.
str_pad pads a string to a given length on the right, the left, or
both sides. wordwrap breaks long text into lines; the fourth
argument cuts words that are longer than the line.
$ php trim_pad.php string(13) "Hello, World!" string(16) "Hello, World! " string(17) " Hello, World!" [PHP] hi 1.5 PHP 000042 left......| ***mid*** The quick brown fox sat over the lazy dog
Splitting and joining
explode splits a string into an array and implode
joins an array into a string.
<?php
$csv = 'apple,banana,cherry,date';
print_r(explode(',', $csv));
print_r(explode(',', $csv, 2));
print_r(explode(',', $csv, -1));
$words = ['PHP', 'is', 'fun'];
echo implode(' ', $words), "\n";
echo implode(', ', [1, 2, 3]), "\n";
print_r(str_split('abcdef', 4));
$tok = strtok('one two three', ' ');
while ($tok !== false) {
echo "[$tok]";
$tok = strtok(' ');
}
echo "\n";
print_r(preg_split('/\s+/', 'one two three'));
The optional third argument of explode is a limit: a positive
number returns at most that many elements with the rest in the last one, a
negative number drops that many elements from the end.
str_split cuts a string into chunks of the same length.
strtok walks through tokens; the second and later calls pass only
the delimiters. When the separator is more complex, such as any amount of
whitespace, use preg_split.
$ php split_join.php
Array
(
[0] => apple
[1] => banana
[2] => cherry
[3] => date
)
Array
(
[0] => apple
[1] => banana,cherry,date
)
Array
(
[0] => apple
[1] => banana
[2] => cherry
)
PHP is fun
1, 2, 3
Array
(
[0] => abcd
[1] => ef
)
[one][two][three]
Array
(
[0] => one
[1] => two
[2] => three
)
Comparing strings
The comparison functions return a negative number, zero or a positive number, which makes them suitable as sort callbacks.
<?php
var_dump(strcmp('apple', 'banana'));
var_dump(strcmp('b', 'a'));
var_dump(strcmp('same', 'same'));
var_dump(strcasecmp('HELLO', 'hello'));
var_dump(strncmp('PHP 8', 'PHP 7', 3));
$files = ['img12.png', 'img10.png', 'img2.png', 'img1.png'];
sort($files);
echo implode(', ', $files), "\n";
natsort($files);
echo implode(', ', $files), "\n";
var_dump(strnatcmp('img12', 'img2'));
var_dump('abc' == 'ABC', 'abc' === 'abc');
echo levenshtein('kitten', 'sitting'), "\n";
echo similar_text('World', 'Word', $percent), "\n";
printf("%.2f%%\n", $percent);
echo soundex('Robert'), ' ', soundex('Rupert'), "\n";
strcmp is case-sensitive and binary safe, strcasecmp
ignores case, and strncmp compares only the first n characters.
For file names with numbers, a plain sort puts img10
before img2. The natural-order functions natsort and
strnatcmp compare the numbers as numbers.
The remaining functions measure similarity: levenshtein is the
number of edits between two words, similar_text returns the number
of common characters and a percentage, and soundex encodes how a
word sounds in English, so that similar names get the same code.
$ php compare.php int(-1) int(1) int(0) int(0) int(0) img1.png, img10.png, img12.png, img2.png img1.png, img2.png, img10.png, img12.png int(1) bool(false) bool(true) 3 4 88.89% R163 R163
Escaping and formatting
When text is placed into HTML, special characters must be escaped. Otherwise we get broken markup or a cross-site scripting hole.
<?php
$input = '<a href="x.php?a=1&b=2">O\'Reilly</a>';
echo htmlspecialchars($input), "\n";
echo htmlspecialchars($input, ENT_NOQUOTES), "\n";
echo htmlspecialchars_decode('<b>bold</b>'), "\n";
echo strip_tags('<p>Hello <b>World</b></p>'), "\n";
echo nl2br("line1\nline2"), "\n";
echo number_format(1234567.891), "\n";
echo number_format(1234567.891, 2), "\n";
echo number_format(1234567.891, 2, ',', ' '), "\n";
echo chunk_split('abcdefgh', 3, '-'), "\n";
echo bin2hex('PHP'), "\n";
htmlspecialchars converts &, <,
> and quotes to entities; the flags control which quotes are
converted. htmlspecialchars_decode does the opposite, and
strip_tags removes tags altogether. nl2br turns
newlines into <br />.
number_format groups thousands and sets the decimal places and the
separators. chunk_split inserts a separator after every chunk, and
bin2hex shows the bytes of a string. For more control over the
output, see the printf and sprintf tutorial.
$ php escape_format.php <a href="x.php?a=1&b=2">O'Reilly</a> <a href="x.php?a=1&b=2">O'Reilly</a> <b>bold</b> Hello World line1<br /> line2 1,234,568 1,234,567.89 1 234 567,89 abc-def-gh- 504850
Multibyte strings
In UTF-8, a letter such as Ž or ô takes two bytes.
The classic functions count and cut bytes, so they can split a character in the
middle. The mb_ functions of the mbstring extension
work with characters.
<?php
$s = 'Žltý kôň';
echo strlen($s), "\n";
echo mb_strlen($s), "\n";
echo strtoupper($s), "\n";
echo mb_strtoupper($s), "\n";
echo bin2hex(substr($s, 0, 1)), "\n";
echo mb_substr($s, 0, 3), "\n";
print_r(mb_str_split($s));
echo mb_str_pad('ľ', 5, '-', STR_PAD_BOTH), "\n";
echo mb_trim(" Žltý "), "|\n";
echo mb_ucfirst('éva'), "\n";
The text has 8 characters but 12 bytes. strlen reports 12 and
mb_strlen 8. Since PHP 8.2, strtoupper only changes
ASCII letters, so ž and ô stay as they are, while
mb_strtoupper converts everything.
substr($s, 0, 1) returns only the first byte of Ž,
which is the invalid UTF-8 sequence c5 (we print it in hex), while
mb_substr returns three whole characters. PHP 8.3 added
mb_str_pad and PHP 8.4 added mb_trim and
mb_ucfirst, which fill the gaps in the multibyte family.
$ php multibyte.php
12
8
ŽLTý Kôň
ŽLTÝ KÔŇ
c5
Žlt
Array
(
[0] => Ž
[1] => l
[2] => t
[3] => ý
[4] =>
[5] => k
[6] => ô
[7] => ň
)
--ľ--
Žltý|
Éva
Practical helpers
The functions combine well. The following script builds four small helpers: a URL slug generator, a text truncator that does not cut words, a palindrome check, and a word frequency counter.
<?php
function slugify(string $title): string
{
$s = strtolower(trim($title));
$s = preg_replace('/[^a-z0-9]+/', '-', $s);
return trim($s, '-');
}
function truncate(string $text, int $max): string
{
if (strlen($text) <= $max) {
return $text;
}
$cut = substr($text, 0, $max - 3);
$cut = substr($cut, 0, strrpos($cut, ' ') ?: $max - 3);
return $cut . '...';
}
function isPalindrome(string $s): bool
{
$clean = preg_replace('/[^a-z0-9]/', '', strtolower($s));
return $clean === strrev($clean);
}
function wordFrequency(string $text): array
{
$words = str_word_count(strtolower($text), 1);
$freq = array_count_values($words);
arsort($freq);
return $freq;
}
echo slugify(' PHP String Functions: A Quick Guide! '), "\n";
echo truncate('The quick brown fox jumps over the lazy dog', 25), "\n";
var_dump(isPalindrome('A man, a plan, a canal: Panama'));
print_r(array_slice(wordFrequency('the cat and the hat and the bat'), 0, 3));
slugify lowercases the title, replaces every run of other
characters with a hyphen, and trims hyphens at the ends. truncate
cuts the text and moves back to the last space, so words stay whole.
isPalindrome removes everything but letters and digits before
comparing the string with its reverse. wordFrequency splits the
text with str_word_count, counts the words with
array_count_values and sorts them in descending order. Only the
top three words are printed. These helpers work with ASCII text; for other
languages, use the mb_ variants.
$ php slugify.php
php-string-functions-a-quick-guide
The quick brown fox...
bool(true)
Array
(
[the] => 3
[and] => 2
[cat] => 1
)
Source
PHP multibyte string functions
In this article, we have covered the most useful PHP string functions. See also the PHP string, PHP regular expressions and PHP printf and sprintf tutorials.
Author
List all PHP tutorials.