substr_replace

Replaces or inserts text at a given offset and length inside a string, or inside every string of an array.

Signature
substr_replace(array|string $string, array|string $replace, array|int $offset, array|int|null $length = null): string|array
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$stringarray|stringThe input string, or an array of strings in which case each element is processed in turn.
$replacearray|stringThe replacement text. When string is an array this may also be an array, applied element by element.
$offsetarray|intWhere the replacement starts. Negative values count back from the end of the string.
$lengthoptionalarray|int|null= nullHow many bytes to replace. A negative value stops that many bytes before the end; 0 inserts without removing anything; null replaces through to the end of the string.

Return value

string|arrayThe resulting string, or an array of results when the first argument is an array.

Errors

Does not throw when the array arguments have different element counts. Elements missing from the replace, offset or length arrays fall back to the defaults — an empty replacement, offset 0 and a length running to the end of the string.

Changelog

  • PHP 8.0.0length is nullable now.

substr_replace swaps out the slice of string that starts at offset and runs for length bytes, putting replace in its place. The replacement does not have to be the same length as the slice it replaces, which is what makes it a splice rather than an overwrite.

When to reach for it

Use it when you know where rather than what: masking a card number, redacting a fixed-width field, or inserting a separator at a known position. Setting length to 0 turns it into a pure insert — nothing is removed and the replacement is spliced in at the offset. A negative offset with a matching negative length lets you edit a trailing region without measuring the string first.

What trips people up

People reach for it expecting str_replace semantics and are surprised that it never searches for anything; if you want to replace by content, use str_replace or strtr.

It counts bytes, so masking UTF-8 text can split a character. The array form is genuinely useful for batch edits, but keep any array arguments the same length as the input array: when an array of replacements, offsets or lengths runs short, the leftover strings silently fall back to the defaults — an empty replacement, offset 0 and a length running to the end. And omitting length replaces everything from the offset to the end — a common cause of accidentally truncated strings.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Mask all but the last four digits of a credit card or account number.
  • Insert a hyphen or separator at a fixed position in a code, such as formatting a postcode.
  • Redact a fixed-width field in a flat-file record without disturbing the columns around it.
  • Apply the same positional edit to every string in an array in one call.

Frequently asked questions

What is the difference between substr_replace and str_replace?

substr_replace edits by position and length, while str_replace searches for matching text. If you know the offset use substr_replace; if you know the content use str_replace.

How do I insert text without deleting anything?

Pass 0 as the length. substr_replace('abcdef', '-', 3, 0) returns 'abc-def' — nothing is removed and the replacement is spliced in at offset 3.

Can substr_replace work on an array of strings?

Yes. Pass an array as the first argument and it returns an array with the same edit applied to each element; offset, length and replace may themselves be arrays, applied element by element. Their element counts do not have to match: if one of those arrays is shorter than the input array, the leftover strings fall back to the defaults — an empty replacement, offset 0 and a length running to the end — rather than raising an error.

More examples

Masking with a reusable helper

Prints '************1111' and then '123' unchanged. The guard matters: without it, a value shorter than the keep count would produce a negative length and replace the wrong region.

Example · php
<?php
function mask(string $value, int $keep = 4): string {
    $hidden = strlen($value) - $keep;
    return $hidden <= 0
        ? $value
        : substr_replace($value, str_repeat('*', $hidden), 0, $hidden);
}

echo mask('4111111111111111'), "\n";
echo mask('123'), "\n";

Inserting rather than replacing

Prints 'SW1A 1AA' and '978-01-32350-88-4'. The + $i in the loop compensates for the fact that each inserted hyphen shifts every later position by one — the classic off-by-N when inserting repeatedly.

Example · php
<?php
$code = 'SW1A1AA';

// length 0 = pure insert, nothing removed
echo substr_replace($code, ' ', 4, 0), "\n";

$isbn = '9780132350884';
foreach ([3, 5, 10, 12] as $i => $pos) {
    $isbn = substr_replace($isbn, '-', $pos + $i, 0);
}
echo $isbn, "\n";

Discussion

  • Be the first to comment on this lesson.