Strings7 min readExamples run on PHP 8.5.10

vfprintf()

Learn how PHP 8.4 vfprintf() formats an array of values and writes the result to a stream, with examples, return value details, edge cases and common mistakes.

Signature

vfprintf(resource $stream, string $format, array $values): int

Purpose

vfprintf() builds a string from a format template and an array of values, then writes that string to a stream. It is the array-taking sibling of fprintf(). Use it when your values already live in an array, for example a database-style row, a list of report columns or arguments collected at runtime, and you want to send formatted text to a stream without unpacking the array yourself.

Signature

Parameters

  • $stream: an open, writable stream resource, such as one returned by fopen(). There is no default. In the browser sandbox, php://output and php://memory are convenient in-memory targets.
  • $format: the template. Ordinary characters are copied unchanged. Each conversion specification follows the shape %[argnum$][flags][width][.precision]specifier.
  • $values: the array of values consumed by the conversions, in order, unless an argnum$ selects one explicitly.

Format pieces

  • argnum$ picks a value by position, starting at 1, so one value can be reused (%1$s).
  • Flags include - (left-justify), + (always show a sign), 0 (zero padding), a space (space padding, the default) and ' followed by a character (custom padding character).
  • Width is the minimum field size. Precision, written after a period, means decimals for f, F, e and E, significant digits for g, G, h and H, and a maximum length for s.
  • Common specifiers are s, d, u, f, F, e, x, X, o, b, c and the literal %%.

Return value and failures

The function returns an int: the length of the string that was written. Since PHP 8.0 it no longer returns false on failure. Problems with the format, such as too few values for the conversions, or an invalid width or precision, now cause an error to be thrown (the manual documents ValueError). Tests that check for such mistakes should catch Throwable rather than test for false.

The stream argument must be a valid resource. If fopen() failed and you pass false, you get a type error instead of formatted output, so check the result of fopen() first.

Example 1: writing a zero-padded date

The first example writes to php://output and shows that the return value counts every written character, including the newline.

Example
<?php
$out = fopen('php://output', 'w');
$length = vfprintf($out, "%04d-%02d-%02d\n", [2025, 5, 6]);
fclose($out);
echo $length, "\n";
Output
2025-05-06
11

Example 2: reusing values by position

With argnum$, the order in the template does not have to match the order in the array. Here the name is used twice. Single quotes keep PHP from interpreting $s as a variable. The in-memory stream is rewound so the written text can be read back.

Example
<?php
$stream = fopen('php://memory', 'w+');
$bytes = vfprintf($stream, '%2$s owes %1$.2f USD (%2$s)', [12.5, 'Ana']);
rewind($stream);
$text = stream_get_contents($stream);
fclose($stream);
echo json_encode(['bytes' => $bytes, 'text' => $text], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
{"bytes":24,"text":"Ana owes 12.50 USD (Ana)"}

Example 3: padding, alignment and precision

This row combines left-justification, a fixed float precision and a custom padding character. The '* flag pads with asterisks.

Example
<?php
$stream = fopen('php://memory', 'w+');
$length = vfprintf($stream, '%-8s|%6.1f|%\'*5d', ['apple', 3.14159, 42]);
rewind($stream);
$text = stream_get_contents($stream);
fclose($stream);
echo json_encode(['length' => $length, 'text' => $text], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
{"length":21,"text":"apple   |   3.1|***42"}

Edge cases

  • The returned length is measured in bytes, not characters. Width and precision with s also count bytes, so multibyte text can be padded or cut in the middle of a character.
  • The c specifier ignores padding and width.
  • %% consumes no value, so it does not shift later positions.
  • The f specifier is locale aware, while F is not. Prefer F when the output must be stable across environments.
  • A precision given as a bare period means 0 for that conversion.
  • Extra values beyond what the format uses are ignored; missing ones are an error.
  • Values are coerced to what the specifier needs, so a numeric string works with d, and a float passed to d is converted to an integer.

Common mistakes

  1. Using double quotes around %1$s. PHP then tries to interpolate $s. Use single quotes or escape the dollar sign.
  2. Passing separate arguments instead of an array. vfprintf($s, $fmt, 1, 2) is wrong; the third argument must be one array.
  3. Forgetting that the stream must be writable. A stream opened with 'r' cannot receive the output.
  4. Reading an in-memory stream without rewind(). After writing, the pointer is at the end, so the read returns an empty string.
  5. Expecting false on failure. Modern PHP throws instead.
  6. Treating the return value as a character count for non-ASCII text.

fprintf() takes values as separate arguments. vprintf() prints an array-formatted string to output, and vsprintf() returns it as a string. sprintf() and printf() are the non-array counterparts. number_format() is better for thousands separators.

Recap

vfprintf() formats an array of values with a printf-style template and writes the result to a stream, returning the number of bytes written. Choose it when the arguments are already in an array and the destination is a stream. Use single-quoted templates for positional arguments, rewind in-memory streams before reading, and expect an error rather than false when the format and values do not match.

Tested examples

Write a zero-padded date to php://output and show the returned length
<?php
$out = fopen('php://output', 'w');
$length = vfprintf($out, "%04d-%02d-%02d\n", [2025, 5, 6]);
fclose($out);
echo $length, "\n";
Output
2025-05-06
11
Reuse a value with positional arguments and read it back from memory
<?php
$stream = fopen('php://memory', 'w+');
$bytes = vfprintf($stream, '%2$s owes %1$.2f USD (%2$s)', [12.5, 'Ana']);
rewind($stream);
$text = stream_get_contents($stream);
fclose($stream);
echo json_encode(['bytes' => $bytes, 'text' => $text], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
{"bytes":24,"text":"Ana owes 12.50 USD (Ana)"}
Combine left-justification, precision and a custom padding character
<?php
$stream = fopen('php://memory', 'w+');
$length = vfprintf($stream, '%-8s|%6.1f|%\'*5d', ['apple', 3.14159, 42]);
rewind($stream);
$text = stream_get_contents($stream);
fclose($stream);
echo json_encode(['length' => $length, 'text' => $text], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Output
{"length":21,"text":"apple   |   3.1|***42"}