ZetCode

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.

length_case.php
<?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.

searching.php
<?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.

strpos_pitfall.php
<?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.

substr.php
<?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.

replace.php
<?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.

trim_pad.php
<?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.

split_join.php
<?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.

compare.php
<?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.

escape_format.php
<?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('&lt;b&gt;bold&lt;/b&gt;'), "\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
&lt;a href=&quot;x.php?a=1&amp;b=2&quot;&gt;O&#039;Reilly&lt;/a&gt;
&lt;a href="x.php?a=1&amp;b=2"&gt;O'Reilly&lt;/a&gt;
<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.

multibyte.php
<?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.

slugify.php
<?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 string functions

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

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all PHP tutorials.