Signature
str_repeat(string $string, int $times): stringPurpose
str_repeat() returns a new string made of the input string written end to end a given number of times. It is the simplest way to build separators, indentation, text-based progress bars, padding blocks or placeholder strings without writing a loop. The function has been part of PHP since version 4 and is available in every PHP 8 release.
Signature
Parameters and defaults
$stringis the text to repeat. It can be one character, a longer fragment such as"-=", or even an empty string. There is no default value.$timesis how many copies to concatenate. It must be greater than or equal to0. There is no default value either, so both arguments are required.
The repeated fragment is treated as a sequence of bytes. When you pass a UTF-8 string, the whole string is copied each time, so a multi-byte character is never split. Repeating it is safe.
Return value and failure cases
The function always returns a string. The length of the result is strlen($string) * $times.
- If
$timesis0, the result is an empty string. - If
$stringis empty, the result is an empty string no matter how large$timesis. - If
$timesis negative, PHP 8 throws aValueError. Older code that relied on a warning and afalseresult should be updated, because the function now stops with an exception unless you catch it. - A very large count can produce a string bigger than the available memory, which ends the script with a fatal error. Validate counts that come from user input before you pass them in.
Example 1: a text progress bar
Two calls to str_repeat() give you the filled and the empty part of a bar. The empty part uses the remainder, so the total width stays constant.
$done = 3;
$total = 8;
echo '[' . str_repeat('#', $done) . str_repeat('.', $total - $done) . "]\n";[###.....]
The output is a bar with three filled cells and five empty ones, wrapped in brackets.
Example 2: indenting nested labels
A common use is indentation that grows with depth. The loop index doubles as the repeat count, and the first line gets an empty prefix because 0 copies is valid.
$names = ['root', 'child', 'leaf'];
$lines = [];
foreach ($names as $depth => $name) {
$lines[] = str_repeat(' ', $depth) . $name;
}
echo json_encode($lines), "\n";["root"," child"," leaf"]
Each level adds two more spaces in front of the name. Printing the lines as JSON makes the leading spaces visible.
Example 3: zero, empty input and negative counts
The boundary cases are worth knowing before you use a computed count. Zero copies and an empty fragment both give an empty string, while a negative count raises an exception that you can catch.
echo json_encode([str_repeat('ab', 0), str_repeat('', 5), strlen(str_repeat('xyz', 4))]), "\n";
try {
str_repeat('x', -1);
} catch (ValueError $e) {
echo get_class($e), "\n";
}["","",12] ValueError
The first line shows two empty strings and the length 12. The second line shows that the negative count was rejected with a ValueError.
Edge cases
- Computed counts: A subtraction such as
$width - $usedcan go negative when the content is longer than the available width. Guard it withmax(0, $width - $used)so the call never throws. - Multi-character fragments: The result always contains whole copies of the fragment. If you need an exact total length that is not a multiple of the fragment length, repeat generously and cut with
substr(). - Integers only: Pass integer counts. Relying on loose conversion of strings or floats makes the code harder to read and can behave differently under strict typing.
- Bytes versus characters:
strlen()counts bytes, so the length of a repeated multi-byte string is larger than the number of visible characters.
Common mistakes
- Forgetting that a negative count throws. Computing a count from user data without a lower bound turns a layout helper into a crash.
- Using it for padding to a width. If you want to pad a value to a fixed width,
str_pad()is usually clearer because it calculates the missing length for you. - Expecting a separator between copies.
str_repeat('a', 3)givesaaa. If you need a delimiter, useimplode(',', array_fill(0, 3, 'a'))instead. - Swapping the arguments. The string comes first and the count second. Writing
str_repeat(3, 'a')is a type error in strict mode and a mistake in any mode. - Passing unchecked huge counts. The result is built fully in memory, so the count should have a sensible upper limit.
Related functions
str_pad()pads a string to a certain length with another string.substr_count()counts how many times a substring occurs, which is a handy way to check a repeated result.implode()witharray_fill()repeats values with a separator between them.strlen()confirms the byte length of the result.
Recap
str_repeat($string, $times) concatenates $times copies of $string and returns the result. A count of 0 or an empty string gives an empty result, a negative count throws a ValueError, and very large counts can exhaust memory. Keep counts as non-negative integers, clamp computed values with max(0, ...), and reach for str_pad() or implode() when you need padding or separators.
Tested examples
$done = 3;
$total = 8;
echo '[' . str_repeat('#', $done) . str_repeat('.', $total - $done) . "]\n";
[###.....]
$names = ['root', 'child', 'leaf'];
$lines = [];
foreach ($names as $depth => $name) {
$lines[] = str_repeat(' ', $depth) . $name;
}
echo json_encode($lines), "\n";
["root"," child"," leaf"]
echo json_encode([str_repeat('ab', 0), str_repeat('', 5), strlen(str_repeat('xyz', 4))]), "\n";
try {
str_repeat('x', -1);
} catch (ValueError $e) {
echo get_class($e), "\n";
}
["","",12] ValueError