01 · THE IDEAREAD → TRY → LEARN
Cutting a string by position
Many small tasks need only part of a string: the first few characters of a code, the file extension at the end, or everything except a trailing marker. substr() handles these jobs. Its PHP 8.4 shape is substr(string $string, int $offset, ?int $length = null): string. It always returns a string. Since PHP 8.0, a request that cannot be satisfied gives "" instead of false, so you can pass the result straight to other string functions.
Offset and length
The offset says where the slice starts. Counting begins at zero, so offset 0 is the first byte. A negative offset counts back from the end, so -1 is the last byte and -2 starts at the last two.
The length says how much to take. If you omit it, or pass null, you get everything from the offset to the end. A positive length means at most that many bytes. A negative length means leave that many bytes off the end. Length 0 always gives an empty string.
$word = 'abcdef';
echo substr($word, 0, 3), PHP_EOL;
echo substr($word, 3), PHP_EOL;
echo substr($word, -2), PHP_EOL;
echo substr($word, 1, -1), PHP_EOL;abc def ef bcde
The four lines give a prefix, a tail from a fixed position, a suffix and a middle section with both ends trimmed.
Bytes, not characters
The manual describes positions as characters, but substr() counts bytes. For ASCII text the two are the same. For UTF-8 text, a single visible character can take several bytes, and cutting in the middle produces invalid text. Use mb_substr() when you need to slice multibyte text by character. This lesson uses ASCII so the offsets are easy to follow.
Forgiving boundaries
The function does not raise errors for ranges that are too large. A start beyond the end gives an empty string. A length that is too long stops at the end of the string. A negative offset that reaches before the start is clamped to the beginning.
echo json_encode([
'past_end' => substr('abc', 5),
'long_length' => substr('abc', 1, 10),
'negative_too_far' => substr('abcdef', -10, 2),
'zero_length' => substr('abcdef', 2, 0),
'negative_length_cutoff' => substr('abcdef', 4, -4),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;{"past_end":"","long_length":"bc","negative_too_far":"ab","zero_length":"","negative_length_cutoff":""}
This is convenient, but it can hide mistakes. If an empty result would be a bug in your program, check the string length first.
The suffix trap
The usual way to take the last $count bytes is substr($text, -$count). This works until $count is 0. Then -$count is still 0, which means start at the beginning, so you get the whole string instead of an empty one. A guard fixes it.
$count = 0;
echo json_encode([
'unguarded' => substr('abcdef', -$count),
'guarded' => $count === 0 ? '' : substr('abcdef', -$count),
]), PHP_EOL;{"unguarded":"abcdef","guarded":""}
Worked plan
Suppose you want a function that returns both ends of a string. Work through it in steps:
- Clamp the requested count with
max(0, $count), so a negative count means none. - Take the prefix with
substr($text, 0, $count). A length larger than the string is safe. - Take the suffix with
substr($text, -$count), but return''when the count is0. - Return both parts in an associative array.
With 'abcdef' and a count of 2, the prefix is ab and the suffix is ef. With a count of 10, both parts are the whole string, because the slice stops at the string's boundaries.
Exercise contract
Write edgeSlices(string $text, int $count): array. It must return an array with the keys prefix and suffix. The prefix holds the first $count bytes and the suffix holds the last $count bytes. If $count is 0 or negative, both are empty strings. If $count is longer than the text, both are the whole text. Tests compare the returned array, so do not print anything.
Recap
- Offsets start at zero and negative offsets count from the end.
- A positive length limits the slice. A negative length trims from the end.
- Out-of-range requests give
""or a shorter result, not an error. substr()works on bytes, so usemb_substr()for multibyte characters.-0is0, so guard suffix extraction when the count may be zero.
Functions · Arrays · Types
- Extract prefixes and suffixes with explicit byte offsets
- Implement and test a deterministic PHP 8.4 function