Strings7 min readExamples run on PHP 8.5.10

str_repeat()

Learn how PHP 8.4 str_repeat() builds a string by repeating another string, with its signature, edge cases, the negative-count error and common mistakes.

Signature

str_repeat(string $string, int $times): string

Purpose

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

  • $string is the text to repeat. It can be one character, a longer fragment such as "-=", or even an empty string. There is no default value.
  • $times is how many copies to concatenate. It must be greater than or equal to 0. 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 $times is 0, the result is an empty string.
  • If $string is empty, the result is an empty string no matter how large $times is.
  • If $times is negative, PHP 8 throws a ValueError. Older code that relied on a warning and a false result 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.

Example
<?php
$done = 3;
$total = 8;
echo '[' . str_repeat('#', $done) . str_repeat('.', $total - $done) . "]\n";
Output
[###.....]

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.

Example
<?php
$names = ['root', 'child', 'leaf'];
$lines = [];
foreach ($names as $depth => $name) {
    $lines[] = str_repeat('  ', $depth) . $name;
}
echo json_encode($lines), "\n";
Output
["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.

Example
<?php
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";
}
Output
["","",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 - $used can go negative when the content is longer than the available width. Guard it with max(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

  1. Forgetting that a negative count throws. Computing a count from user data without a lower bound turns a layout helper into a crash.
  2. 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.
  3. Expecting a separator between copies. str_repeat('a', 3) gives aaa. If you need a delimiter, use implode(',', array_fill(0, 3, 'a')) instead.
  4. 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.
  5. Passing unchecked huge counts. The result is built fully in memory, so the count should have a sensible upper limit.
  • 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() with array_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

A text progress bar
<?php
$done = 3;
$total = 8;
echo '[' . str_repeat('#', $done) . str_repeat('.', $total - $done) . "]\n";
Output
[###.....]
Indenting nested labels
<?php
$names = ['root', 'child', 'leaf'];
$lines = [];
foreach ($names as $depth => $name) {
    $lines[] = str_repeat('  ', $depth) . $name;
}
echo json_encode($lines), "\n";
Output
["root","  child","    leaf"]
Zero, empty input and negative counts
<?php
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";
}
Output
["","",12]
ValueError