Strings #
PHP has over 100 built-in functions for manipulating strings — from very simple ones like strlen() to complex ones like similar_text() and levenshtein(). You don’t need to memorize all of them, but there’s a core set that appears in almost all everyday PHP code. This article covers the most important string functions in an organized way — not just a list, but with context on when and how to use them, including the mb_ functions for multibyte strings (UTF-8, emoji, Asian characters) that you must use when dealing with non-ASCII text.
Basic String Information #
<?php
$text = " Hello, PHP World! ";
// String length — in BYTES, not characters
echo strlen($text); // 20
// Length in characters (correct for UTF-8)
echo mb_strlen($text); // 20 (same if all ASCII)
echo mb_strlen("日本語"); // 3 characters (but 9 bytes!)
echo strlen("日本語"); // 9 bytes
// Check whether the string is empty
var_dump($text === ''); // false
var_dump(empty($text)); // false
var_dump(empty('')); // true
var_dump(empty('0')); // true — gotcha!
Searching and Checking #
<?php
$text = "The quick brown fox jumps over the lazy dog";
// str_contains — PHP 8.0+, check for substring existence
var_dump(str_contains($text, 'fox')); // true
var_dump(str_contains($text, 'cat')); // false
var_dump(str_contains($text, '')); // true — an empty string is always present
// str_starts_with and str_ends_with — PHP 8.0+
var_dump(str_starts_with($text, 'The')); // true
var_dump(str_starts_with($text, 'the')); // false — case-sensitive!
var_dump(str_ends_with($text, 'dog')); // true
var_dump(str_ends_with($text, 'Dog')); // false
// strpos — position of the first occurrence (0-based), false if absent
$position = strpos($text, 'fox');
var_dump($position); // int(16)
// ANTI-PATTERN: loose comparison with false
if (strpos($text, 'fox') == false) { } // wrong! position 0 == false is also true!
// CORRECT: use === false
if (strpos($text, 'fox') === false) {
echo "Not found\n";
}
// Or even better, use str_contains (PHP 8.0+)
if (!str_contains($text, 'fox')) {
echo "Not found\n";
}
// strrpos — position of the LAST occurrence
$url = 'https://example.com/path/to/file.php';
$pos = strrpos($url, '/');
$file = substr($url, $pos + 1); // "file.php"
// substr_count — count substring occurrences
echo substr_count("banana", "an"); // 2
echo substr_count("hello world", "l"); // 3
// Case-insensitive search
$pos = stripos("Hello WORLD", "world"); // 6
var_dump(str_contains(strtolower($text), 'fox')); // portable case-insensitive way
Searching and Replacing #
<?php
// str_replace — replace all occurrences
$result = str_replace('fox', 'cat', "The fox and the fox");
// "The cat and the cat"
// Replace many at once — array to array
$clean = str_replace(
['<script>', '</script>', 'javascript:', 'onclick'],
['', '', '', ''],
$userInput
);
// str_ireplace — case-insensitive
$result = str_ireplace('HELLO', 'Hi', "HELLO world Hello"); // "Hi world Hi"
// substr_replace — replace at a specific position
$text = "Hello World";
$result = substr_replace($text, 'PHP', 6, 5); // "Hello PHP"
// replace 5 characters starting at position 6
// preg_replace — replace with regex
$clean = preg_replace('/\s+/', ' ', "there are extra spaces");
// "there are extra spaces"
$slug = preg_replace('/[^a-z0-9]+/', '-', strtolower("Hello PHP World!"));
// "hello-php-world-"
$slug = trim($slug, '-'); // "hello-php-world"
// str_repeat — repeat a string
echo str_repeat('=', 40); // ========================================
echo str_repeat('ab', 3); // ababab
// strtr — replace characters one by one (useful for transliteration)
$from = 'áàâäãåéèêëíìîïóòôöõúùûüý';
$to = 'aaaaaaeeeeeiiiiooooouuuuy';
$clean = strtr($text, $from, $to);
// Or with an array mapping
$result = strtr("Hello World", ['Hello' => 'Hi', 'World' => 'Earth']);
// "Hi Earth"
Splitting and Joining #
<?php
// explode — split a string into an array
$csv = "apple,mango,orange,grape";
$fruits = explode(',', $csv);
// ['apple', 'mango', 'orange', 'grape']
// With a limit — at most N elements, the rest is joined in the last element
$limited = explode(',', $csv, 2);
// ['apple', 'mango,orange,grape']
// Negative limit — remove the last N elements
$withoutLast = explode(',', $csv, -1);
// ['apple', 'mango', 'orange']
// implode / join — join an array into a string
$text = implode(', ', $fruits); // "apple, mango, orange, grape"
$text = implode(' | ', $fruits); // "apple | mango | orange | grape"
$text = implode('', ['a', 'b', 'c']); // "abc"
// str_split — split a string into an array per N characters
$chars = str_split("Hello"); // ['H', 'e', 'l', 'l', 'o']
$chunks = str_split("Hello World", 3); // ['Hel', 'lo ', 'Wor', 'ld']
// chunk_split — split with a separator (useful for base64)
$base64 = base64_encode(file_get_contents('image.jpg'));
$wrapped = chunk_split($base64, 76, "\n"); // wrap every 76 characters
// wordwrap — wrap long text
$paragraph = "This is a very long text that needs to be wrapped at a certain width.";
$wrapped = wordwrap($paragraph, 40, "\n", true);
Case Manipulation #
<?php
$text = "hello world php programming";
// Basic case conversion
echo strtoupper($text); // HELLO WORLD PHP PROGRAMMING
echo strtolower("HELLO"); // hello
// Capitalization
echo ucfirst($text); // Hello world php programming
echo lcfirst("HELLO"); // hELLO
echo ucwords($text); // Hello World Php Programming
// For multibyte (non-ASCII characters)
echo mb_strtoupper("héllo wörld", 'UTF-8'); // HÉLLO WÖRLD
echo mb_strtolower("HÉLLO WÖRLD", 'UTF-8'); // héllo wörld
echo mb_convert_case("héllo wörld", MB_CASE_TITLE, 'UTF-8'); // Héllo Wörld
// camelCase → snake_case
function camelToSnake(string $text): string
{
return strtolower(preg_replace('/(?<!^)(?=[A-Z])/', '_', $text));
}
echo camelToSnake('camelCaseString'); // camel_case_string
echo camelToSnake('getUserById'); // get_user_by_id
// snake_case → camelCase
function snakeToCamel(string $text): string
{
return lcfirst(str_replace('_', '', ucwords($text, '_')));
}
echo snakeToCamel('snake_case_string'); // snakeCaseString
echo snakeToCamel('get_user_by_id'); // getUserById
Trimming and Padding #
<?php
$text = " Hello World ";
// trim — remove whitespace from both sides
echo trim($text); // "Hello World"
echo ltrim($text); // "Hello World " (left only)
echo rtrim($text); // " Hello World" (right only)
// trim with custom characters
echo trim("***Hello***", "*"); // "Hello"
echo trim("/path/to/dir/", "/"); // "path/to/dir"
// str_pad — padding for alignment
echo str_pad("42", 5, "0", STR_PAD_LEFT); // "00042"
echo str_pad("Hello", 10, "-", STR_PAD_BOTH); // "---Hello---"
echo str_pad("PHP", 10, " ", STR_PAD_RIGHT); // "PHP "
// A tidy table with str_pad
$items = [
['name' => 'Laptop', 'price' => 15000000],
['name' => 'Mouse', 'price' => 250000],
['name' => 'Monitor', 'price' => 5000000],
];
foreach ($items as $item) {
echo str_pad($item['name'], 10) . str_pad(number_format($item['price']), 12, ' ', STR_PAD_LEFT) . "\n";
}
// Laptop 15,000,000
// Mouse 250,000
// Monitor 5,000,000
Substrings and Extraction #
<?php
$text = "Hello, World!";
// substr — take part of a string
echo substr($text, 7); // "World!" (from position 7)
echo substr($text, 7, 5); // "World" (5 characters from position 7)
echo substr($text, -6); // "World!" (6 from the end)
echo substr($text, -6, 5); // "World" (5 characters, 6 from the end)
// mb_substr — for multibyte
echo mb_substr("日本語テスト", 2, 3); // "語テス" (3 characters starting at position 2)
// strstr — take from where the substring is found
$email = "[email protected]";
echo strstr($email, '@'); // "@example.com" (including the delimiter)
echo strstr($email, '@', true); // "budi" (before the delimiter — before=true)
// strrchr — from the LAST occurrence
$path = "/var/www/html/index.php";
echo strrchr($path, '/'); // "/index.php"
echo ltrim(strrchr($path, '/'), '/'); // "index.php"
// basename() is better for paths:
echo basename($path); // "index.php"
echo basename($path, '.php'); // "index"
echo dirname($path); // "/var/www/html"
echo pathinfo($path, PATHINFO_EXTENSION); // "php"
Formatting and Output #
<?php
// sprintf — format a string
$name = 'Budi';
$score = 95.67;
$rank = 3;
echo sprintf("Name: %-15s Score: %06.2f Rank: %d", $name, $score, $rank);
// "Name: Budi Score: 095.67 Rank: 3"
// Frequently used format specifiers:
// %s — string
// %d — decimal integer
// %f — float (default 6 decimals)
// %.2f — float with 2 decimals
// %05d — 5-digit integer with leading zeros
// %-10s — left-aligned 10-char string
// %10s — right-aligned 10-char string
// number_format — format numbers with thousands separators
echo number_format(1234567.891); // "1,234,568"
echo number_format(1234567.891, 2); // "1,234,567.89"
echo number_format(1234567.891, 0, ',', '.'); // "1.234.568" (Indonesian format)
echo "Rp " . number_format(15000000, 0, ',', '.'); // "Rp 15.000.000"
// printf — like sprintf but prints directly
printf("Total: Rp %s\n", number_format(15000000, 0, ',', '.'));
// vsprintf / vprintf — from an argument array
$args = ['Budi', 28, 'Jakarta'];
$text = vsprintf("Name: %s, Age: %d, City: %s", $args);
// money_format is deprecated — use NumberFormatter
$fmt = new NumberFormatter('id_ID', NumberFormatter::CURRENCY);
echo $fmt->format(15000000); // "Rp15.000.000,00"
echo $fmt->formatCurrency(15000000, 'IDR'); // "Rp15.000.000,00"
Hashing and Encryption #
<?php
// One-way hashes
echo md5("password"); // 5f4dcc3b5aa765d61d8327deb882cf99 — DON'T use for passwords!
echo sha1("password"); // 5baa61e4c9b93f3f0682250b6cf8331b57ff68 — DON'T!
echo hash('sha256', "data"); // SHA-256 hash
echo hash('sha512', "data"); // SHA-512 hash
// The correct hash for passwords
$hash = password_hash('secret123', PASSWORD_BCRYPT);
var_dump(password_verify('secret123', $hash)); // true
var_dump(password_verify('wrong', $hash)); // false
// Check whether the hash needs rehashing (if the algorithm changes)
if (password_needs_rehash($hash, PASSWORD_BCRYPT)) {
$hash = password_hash('secret123', PASSWORD_BCRYPT); // rehash
}
// HMAC — hash with a secret key (for integrity verification)
$secret = 'application-secret-key';
$payload = json_encode(['user_id' => 42, 'exp' => time() + 3600]);
$hmac = hash_hmac('sha256', $payload, $secret);
// Verify the HMAC — use hash_equals for a timing-safe comparison
$valid = hash_equals($hmac, hash_hmac('sha256', $payload, $secret));
// Two-way encryption (symmetric)
$key = sodium_crypto_secretbox_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$encrypted = sodium_crypto_secretbox('secret message', $nonce, $key);
$decrypted = sodium_crypto_secretbox_open($encrypted, $nonce, $key);
echo $decrypted; // "secret message"
// Encode/decode
echo base64_encode("binary data or text");
echo base64_decode("ZGF0YSBiaW5lciBhdGF1IHRla3M=");
// URL encoding
echo urlencode("price=Rp 15.000 & name=Budi");
// "price%3DRp+15.000+%26+name%3DBudi"
echo rawurlencode("price=Rp 15.000 & name=Budi");
// "price%3DRp%2015.000%20%26%20name%3DBudi" (RFC 3986 compliant)
$decoded = urldecode("price%3DRp+15.000");
Multibyte Functions (mb_) #
For all string operations involving non-ASCII characters (UTF-8, Unicode), always use the mb_ functions rather than the regular string functions:
<?php
$text = "Héllo Wörld 日本語 😀";
// mb_ equivalents
echo mb_strlen($text, 'UTF-8'); // 18 characters
echo mb_strtoupper($text, 'UTF-8'); // HÉLLO WÖRLD 日本語 😀
echo mb_substr($text, 6, 5, 'UTF-8'); // Wörld
echo mb_strpos($text, 'Wörld', 0, 'UTF-8'); // 6
// Set the default encoding for all mb_ functions
mb_internal_encoding('UTF-8');
mb_regex_encoding('UTF-8');
// After setting the internal encoding, you can omit the encoding argument:
echo mb_strlen($text); // 18
echo mb_strtolower($text); // héllo wörld 日本語 😀
// Detect encoding
$encoding = mb_detect_encoding($text, ['UTF-8', 'ISO-8859-1', 'Windows-1252']);
// Convert encoding
$utf8 = mb_convert_encoding($latin1String, 'UTF-8', 'ISO-8859-1');
$latin1 = mb_convert_encoding($utf8String, 'ISO-8859-1', 'UTF-8');
// mb_convert_variables — convert entire variables
mb_convert_variables('UTF-8', 'ISO-8859-1', $array); // converts all array elements
// Important mb_ functions:
// mb_strlen — length in characters
// mb_substr — substring
// mb_strpos — substring position
// mb_strrpos — last position
// mb_strtolower — lowercase
// mb_strtoupper — uppercase
// mb_convert_case — case conversion with modes
// mb_str_split — split into an array of characters (PHP 7.4+)
$chars = mb_str_split("日本語"); // ['日', '本', '語']
Comparison and Similarity Functions #
<?php
// strcmp — binary-safe comparison, returns 0 if equal
$cmp = strcmp("apple", "banana"); // negative (apple < banana)
$cmp = strcmp("banana", "apple"); // positive
$cmp = strcmp("apple", "apple"); // 0
// strcasecmp — case-insensitive
var_dump(strcasecmp("HELLO", "hello")); // int(0)
// strncmp — compare the first N characters
var_dump(strncmp("hello world", "hello PHP", 5)); // int(0) — first 5 characters match
// similar_text — similarity percentage of two strings
similar_text("Hello", "World", $percent);
echo round($percent, 1) . "%"; // 40%
similar_text("Budi Santoso", "Budi Hartono", $percent);
echo round($percent, 1) . "%"; // 75%
// levenshtein — minimum number of edit operations
echo levenshtein("kitten", "sitting"); // 3
echo levenshtein("laptp", "laptop"); // 1 — one insertion
// soundex and metaphone — phonetic matching
echo soundex("Robert"); // R163
echo soundex("Rupert"); // R163 — same!
echo metaphone("Smith"); // SM0
echo metaphone("Smythe"); // SM0 — same!
Summary #
- PHP 8.0+:
str_contains,str_starts_with,str_ends_withreplace the verbosestrpos() !== falsepattern that’s error-prone when===isn’t used.strpos()returnsfalseor an integer — always use=== falseto check for absence. Position 0 is valid and equalsfalsewhen compared with==.mb_functions for non-ASCII text —strlen("日本語")returns 9 (bytes), not 3 (characters). Always usemb_strlen,mb_substr,mb_strtolower, etc. for multibyte content.password_hash+password_verifyfor passwords — never usemd5orsha1for password hashing. UsePASSWORD_BCRYPTorPASSWORD_ARGON2ID.hash_equalsfor timing-safe hash comparisons — prevents timing attacks that could expose tokens through execution time differences.sprintffor complex formatting — cleaner than repeated concatenation when you have many variables with specific formats.number_format($number, 0, ',', '.')for Rupiah formatting — 0 decimals, comma as the decimal separator, dot as the thousands separator.mb_internal_encoding('UTF-8')in your application bootstrap ensures allmb_functions use UTF-8 by default without mentioning the encoding on every call.