はじめに
前回の記事では、配列を使ってフォーマット出力を直接行う vprintf() を解説しました。今回紹介する vsprintf() は、その兄弟関数にあたり、画面に直接出力するのではなく、フォーマット済みの文字列を戻り値として返すという点が異なります。
Webアプリケーションでは、フォーマットした文字列をその場で表示するのではなく、変数に格納してログに書き込んだり、メール本文として組み立てたり、他の処理に渡したりすることの方がむしろ多いものです。そうした場面で活躍するのが vsprintf() です。本記事では基本的な使い方から、実践的な活用パターンまで詳しく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | vsprintf() |
| 所属拡張 | コア関数(標準で常に利用可能) |
| シグネチャ | vsprintf(string $format, array $values): string |
| 引数1 | $format — 書式指定文字列(%s, %d などのプレースホルダーを含む) |
| 引数2 | $values — プレースホルダーに埋め込む値の配列 |
| 戻り値 | フォーマット済みの文字列 |
| 対応バージョン | PHP 4.1.0以降 |
| 出力への影響 | なし(画面には何も出力されない) |
| 類似関数 | sprintf()(可変長引数版)、vprintf()(直接出力する版) |
処理の流れ(イメージ図)
書式指定文字列
"%s様、ご注文(注文番号: %d)合計 %d円 承りました。"
│
値の配列
["田中", 10023, 4500]
│
▼
vsprintf($format, $values)
│
┌──────────┴──────────────┐
│ プレースホルダーに配列の要素を │
│ 順番に埋め込み、文字列として組み立てる │
│ ※画面には何も出力されない │
└──────────┬──────────────┘
▼
戻り値として文字列を取得:
"田中様、ご注文(注文番号: 10023)合計 4500円 承りました。"
│
▼
変数に代入して自由に活用
(メール送信、ログ保存、他の文字列との連結など)
ポイントは、vsprintf() が出力を一切行わず、組み立てた文字列を戻り値として返すだけという点です。この「副作用がない」という性質のおかげで、関数の戻り値をそのまま別の処理(メール送信関数の引数など)に渡す、テストがしやすい、といったメリットがあります。
実践サンプル7選
例1:基本的な使い方
<?php
class BasicVsprintfDemo
{
public function format(string $formatStr, array $values): string
{
// 画面には出力せず、組み立てた文字列を返す
return vsprintf($formatStr, $values);
}
}
$demo = new BasicVsprintfDemo();
$message = $demo->format('名前: %s, 年齢: %d歳', ['太郎', 30]);
echo $message . PHP_EOL;
// "名前: 太郎, 年齢: 30歳"
例2:メール本文をテンプレートから組み立てるクラス
<?php
class EmailBodyBuilder
{
private const TEMPLATE = "%s様\n\nご注文ありがとうございます。\n注文番号: %d\n合計金額: %d円\n\n発送予定日: %s";
/**
* テンプレートと動的な値の配列から、
* メール本文の文字列を組み立てて返す
*/
public function build(array $orderData): string
{
return vsprintf(self::TEMPLATE, [
$orderData['customerName'],
$orderData['orderId'],
$orderData['totalAmount'],
$orderData['shippingDate'],
]);
}
}
$builder = new EmailBodyBuilder();
$body = $builder->build([
'customerName' => '鈴木',
'orderId' => 20260817,
'totalAmount' => 8900,
'shippingDate' => '2026年8月20日',
]);
echo $body . PHP_EOL;
例3:ログメッセージを文字列として組み立てて保存するクラス
<?php
class FileLogger
{
public function __construct(private string $logFilePath)
{
}
/**
* vsprintf()で組み立てたログ行を、
* ファイルへの書き込み処理に渡す
*/
public function log(string $level, string $template, array $context): void
{
$timestamp = date('Y-m-d H:i:s');
$body = vsprintf($template, $context);
$line = "[{$timestamp}] [{$level}] {$body}\n";
file_put_contents($this->logFilePath, $line, FILE_APPEND);
}
}
$logger = new FileLogger('/tmp/app.log');
$logger->log('INFO', 'ユーザー %s (ID: %d) がログインしました', ['太郎', 42]);
$logger->log('ERROR', '決済処理が失敗しました(コード: %s, 金額: %d円)', ['E5001', 5000]);
例4:バリデーションエラーメッセージを動的に組み立てるクラス
<?php
class ValidationMessageBuilder
{
private array $messageTemplates = [
'required' => '%sは必須項目です。',
'minLength' => '%sは%d文字以上で入力してください。',
'maxLength' => '%sは%d文字以内で入力してください。',
];
/**
* ルール名と引数の配列から、
* 対応するエラーメッセージ文字列を生成する
*/
public function buildMessage(string $rule, array $args): string
{
$template = $this->messageTemplates[$rule] ?? '不明なエラーです。';
return vsprintf($template, $args);
}
}
$builder = new ValidationMessageBuilder();
echo $builder->buildMessage('required', ['メールアドレス']) . PHP_EOL;
echo $builder->buildMessage('minLength', ['パスワード', 8]) . PHP_EOL;
例5:多言語対応のメッセージ文字列を生成するクラス
<?php
class LocalizedStringBuilder
{
private array $translations = [
'ja' => '%sさんが%d件のコメントを投稿しました。',
'en' => '%s posted %d comments.',
];
/**
* 言語ごとのテンプレートに値を埋め込んだ文字列を返す
* (直接出力しないため、後続のAPIレスポンス組み立てなどに使いやすい)
*/
public function translate(string $locale, string $name, int $count): string
{
$template = $this->translations[$locale] ?? $this->translations['en'];
return vsprintf($template, [$name, $count]);
}
}
$builder = new LocalizedStringBuilder();
echo $builder->translate('ja', '花子', 5) . PHP_EOL;
echo $builder->translate('en', 'Hanako', 5) . PHP_EOL;
例6:CSV風の行データを配列から一括で文字列生成するクラス
<?php
class CsvLineFormatter
{
/**
* 複数行の配列データを、
* それぞれvsprintf()でフォーマットした文字列の配列として返す
*/
public function formatLines(array $rows, string $format): array
{
return array_map(
fn (array $row) => vsprintf($format, $row),
$rows
);
}
}
$formatter = new CsvLineFormatter();
$rows = [
['ノート', 300, 50],
['ペン', 120, 200],
];
$lines = $formatter->formatLines($rows, '%s,%d円,在庫%d個');
foreach ($lines as $line) {
echo $line . PHP_EOL;
}
例7:sprintfとvsprintfの使い分けを示す比較クラス
<?php
class SprintfVsVsprintfComparator
{
/**
* 同じ結果を、sprintf()(可変長引数)と
* vsprintf()(配列引数)それぞれで生成する比較
*/
public function withSprintf(string $name, int $score): string
{
return sprintf('%sさんのスコアは%d点です。', $name, $score);
}
public function withVsprintf(array $data): string
{
return vsprintf('%sさんのスコアは%d点です。', $data);
}
}
$comparator = new SprintfVsVsprintfComparator();
echo $comparator->withSprintf('太郎', 85) . PHP_EOL;
echo $comparator->withVsprintf(['太郎', 85]) . PHP_EOL;
// 両方とも同じ結果になる
関連関数との比較
| 関数 | 役割 | vsprintfとの違い |
|---|---|---|
vsprintf() | 配列を使ってフォーマット済み文字列を生成 | 本記事の対象。結果を文字列として返す(出力しない) |
sprintf() | 可変長引数を使ってフォーマット済み文字列を生成 | 値を個別の引数として渡す点が異なる |
vprintf() | 配列を使ってフォーマット出力を直接行う | 結果を画面に直接出力する点が異なる |
printf() | 可変長引数を使ってフォーマット出力を直接行う | 値を個別の引数として渡し、かつ直接出力する |
strtr() | プレースホルダーを単純な文字列置換で埋め込む | 型指定(%d, %fなど)を伴わない、より単純な置換処理 |
よくある落とし穴(注意点)
vprintf()と混同して意図せず画面に出力してしまう 関数名が似ているため取り違えやすいですが、vsprintf()は出力を一切行いません。文字列として結果を受け取りたい場合は必ずvsprintf()を、直接表示したい場合はvprintf()を使いましょう。- 配列の要素数とプレースホルダーの数の不一致
vprintf()と同様、書式文字列中のプレースホルダーの数より配列の要素数が不足していると、PHP 8以降ではValueErrorが発生します。動的にデータを組み立てる場合は要素数を事前に検証しましょう。 - 連想配列のキーの順序に依存してしまう
vsprintf()に渡す配列は、キーの名前ではなく配列の並び順でプレースホルダーに順番に割り当てられます。連想配列を渡す場合、意図した順序で値が並んでいるかを事前に確認する必要があります(例2のように明示的に配列を組み立てるのが安全です)。 - マルチバイト文字の幅指定によるレイアウト崩れ
%-10sのような幅指定はバイト数を基準にしているため、日本語などのマルチバイト文字を含む文字列では見た目の桁が揃わないことがあります。表形式の整形が必要な場合はmb_str_pad()などの併用を検討しましょう。 - エスケープ処理を忘れてXSSなどのリスクを招く
vsprintf()で組み立てた文字列をHTMLとしてそのまま出力する場合、埋め込む値に悪意あるスクリプトが含まれているとXSSの原因になります。ユーザー入力を含む場合はhtmlspecialchars()などで適切にエスケープしましょう。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | 書式指定文字列に対し、配列で渡した値を埋め込んだ文字列を生成して返す |
| 主な用途 | メール本文の組み立て、ログメッセージの生成、バリデーションエラーメッセージの動的生成など |
sprintf()との違い | 値を可変長引数ではなく配列としてまとめて渡せる |
vprintf()との違い | 結果を画面に出力せず、文字列として返す |
| 注意点 | プレースホルダーと配列要素数の一致、配列の並び順への依存、出力時のエスケープ処理 |
vsprintf() は、既に配列として持っているデータを、副作用なく文字列に組み立てたいときに最適な関数です。メール送信やログ記録、APIレスポンスの構築など、「その場で表示せず後で使う」文字列処理において積極的に活用していきましょう。
