はじめに
PHPで書式付きの文字列を出力する際、printf() はおなじみの関数です。しかし、printf("名前: %s, 年齢: %d", $name, $age) のように、埋め込む値の数が実行時まで分からない場合や、既に配列としてデータを持っている場合には、printf() に一つ一つ引数を書き並べるのは不便です。
そこで使うのが vprintf() 関数です。この関数は printf() とほぼ同じ機能を持ちながら、置き換える値を可変長引数ではなく配列としてまとめて渡せるという特徴があります。名前の “v” は “vector”(配列・ベクター)を意味しており、動的に組み立てた配列データをそのままフォーマット出力したい場面で非常に重宝します。本記事では基本的な使い方から実践的な活用例まで詳しく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | vprintf() |
| 所属拡張 | コア関数(標準で常に利用可能) |
| シグネチャ | vprintf(string $format, array $values): int |
| 引数1 | $format — 書式指定文字列(%s, %d などのプレースホルダーを含む) |
| 引数2 | $values — プレースホルダーに埋め込む値の配列 |
| 戻り値 | 出力された文字列の長さ(バイト数) |
| 対応バージョン | PHP 4.1.0以降 |
| 出力先 | 標準出力(直接画面に表示される) |
| 類似関数 | printf()(可変長引数版)、vsprintf()(出力せず文字列を返す版) |
出力の流れ(イメージ図)
書式指定文字列
"名前: %s さん、年齢: %d歳、身長: %.1fcm"
│
値の配列
["太郎", 30, 172.5]
│
▼
vprintf($format, $values)
│
┌──────────┴──────────────┐
│ 配列の要素を順番に │
│ プレースホルダーへ埋め込む │
└──────────┬──────────────┘
▼
画面に直接出力:
"名前: 太郎 さん、年齢: 30歳、身長: 172.5cm"
ポイントは、printf("...", $a, $b, $c) のように引数を個別に並べる代わりに、[$a, $b, $c] という配列を1つ渡すだけで済むという点です。値の数や順序が動的に変わる場合、あるいはすでに配列としてデータが手元にある場合に、コードがぐっとシンプルになります。
実践サンプル7選
例1:基本的な使い方
<?php
class BasicVprintfDemo
{
public function display(string $format, array $values): int
{
// 配列をそのままプレースホルダーへ展開して出力する
return vprintf($format, $values);
}
}
$demo = new BasicVprintfDemo();
$demo->display("名前: %s, 年齢: %d歳\n", ['太郎', 30]);
// 出力: "名前: 太郎, 年齢: 30歳"
例2:CSVデータを整形して一覧表示するクラス
<?php
class CsvRowFormatter
{
/**
* CSVから読み込んだ1行分の配列データを、
* 固定書式でそのまま出力する
*/
public function printRow(array $row): void
{
vprintf("| %-10s | %5d | %8.2f |\n", $row);
}
}
$formatter = new CsvRowFormatter();
$rows = [
['ノート', 50, 300.00],
['ペン', 200, 120.50],
];
foreach ($rows as $row) {
$formatter->printRow($row);
}
// | ノート | 50 | 300.00 |
// | ペン | 200 | 120.50 |
例3:ログメッセージのテンプレート出力クラス
<?php
class TemplatedLogger
{
/**
* 事前定義したログテンプレートに、
* 呼び出し側から配列で渡された値を埋め込んで出力する
*/
public function log(string $level, string $template, array $context): void
{
$prefix = sprintf('[%s] [%s] ', date('Y-m-d H:i:s'), strtoupper($level));
echo $prefix;
vprintf($template . "\n", $context);
}
}
$logger = new TemplatedLogger();
$logger->log('info', 'ユーザー %s (ID: %d) がログインしました', ['太郎', 42]);
$logger->log('error', '処理 %s が %d 回失敗しました(コード: %s)', ['決済処理', 3, 'E5001']);
例4:動的な数の値をフォーマットするレポート生成クラス
<?php
class DynamicReportGenerator
{
/**
* 商品数が実行時まで分からない場合でも、
* 配列をそのまま渡せるvprintf()なら柔軟に対応できる
*/
public function generateSummary(array $productNames): void
{
$count = count($productNames);
$format = "取扱商品({$count}件): " . implode(', ', array_fill(0, $count, '%s')) . "\n";
vprintf($format, $productNames);
}
}
$generator = new DynamicReportGenerator();
$generator->generateSummary(['ノート', 'ペン', '消しゴム']);
// 出力: "取扱商品(3件): ノート, ペン, 消しゴム"
例5:printfとvprintfの使い分けを比較するデモ
<?php
class PrintfVsVprintfComparator
{
/**
* 同じ出力結果を、printf()(可変長引数)と
* vprintf()(配列引数)それぞれで実現する比較
*/
public function demonstrateWithPrintf(string $name, int $age): void
{
printf("名前: %s, 年齢: %d歳\n", $name, $age);
}
public function demonstrateWithVprintf(array $data): void
{
vprintf("名前: %s, 年齢: %d歳\n", $data);
}
}
$comparator = new PrintfVsVprintfComparator();
$comparator->demonstrateWithPrintf('花子', 25);
$comparator->demonstrateWithVprintf(['花子', 25]);
// 両方とも同じ出力になる
例6:多言語対応のメッセージテンプレートを出力するクラス
<?php
class LocalizedMessagePrinter
{
private array $templates = [
'ja' => "%sさん、注文番号%dの商品が発送されました。\n",
'en' => "Dear %s, your order #%d has been shipped.\n",
];
/**
* 言語ごとに異なるテンプレートに、
* 共通の値配列を埋め込んで出力する
*/
public function printMessage(string $locale, array $values): void
{
$template = $this->templates[$locale] ?? $this->templates['en'];
vprintf($template, $values);
}
}
$printer = new LocalizedMessagePrinter();
$printer->printMessage('ja', ['太郎', 12345]);
$printer->printMessage('en', ['Taro', 12345]);
例7:フォーマット済み文字列の長さを利用して出力位置を揃える
<?php
class AlignedTableRenderer
{
/**
* vprintf()の戻り値(出力バイト数)を利用して、
* 罫線の長さを出力内容に合わせて動的に調整する
*/
public function renderRow(array $columns): void
{
$format = str_repeat('%-15s', count($columns)) . "\n";
$length = vprintf($format, $columns);
echo str_repeat('-', $length - 1) . "\n";
}
}
$renderer = new AlignedTableRenderer();
$renderer->renderRow(['商品名', '価格', '在庫']);
$renderer->renderRow(['ノート', '300円', '50個']);
関連関数との比較
| 関数 | 役割 | vprintfとの違い |
|---|---|---|
vprintf() | 配列を使ってフォーマット出力を直接行う | 本記事の対象。結果を画面に直接出力する |
printf() | 可変長引数を使ってフォーマット出力を直接行う | 値を個別の引数として渡す点が異なる |
vsprintf() | 配列を使ってフォーマット済み文字列を生成 | 出力せず、結果を文字列として返す点が異なる |
sprintf() | 可変長引数を使ってフォーマット済み文字列を生成 | 値を個別の引数として渡し、かつ文字列を返す |
str_repeat() / implode() | 文字列の繰り返しや結合 | フォーマット指定を伴わない、より単純な文字列操作 |
よくある落とし穴(注意点)
- 配列の要素数とプレースホルダーの数が一致しないとエラーになる 書式文字列中のプレースホルダーの数より配列の要素数が少ない場合、
ValueError(PHP 8以降)または警告が発生します。動的にデータを組み立てる場合は、要素数の整合性を事前に確認しましょう。 vsprintf()との使い分けを誤る 直接出力したいのか、文字列として後で使いたいのかによって適切な関数が異なります。ログファイルへの書き込みや文字列連結に使いたい場合はvsprintf()の方が適しています(例3のログクラスのような用途では、実際にはvsprintf()でファイルに書き込む設計の方が適する場合も多い点に留意してください)。- 戻り値を文字列だと誤解する
vprintf()の戻り値は出力された文字列の**長さ(バイト数)**であり、出力内容そのものではありません。文字列として結果が欲しい場合はvsprintf()を使う必要があります。 - マルチバイト文字を含む書式の幅指定がずれる
%-10sのような幅指定はバイト数ベースで計算されるため、日本語のようなマルチバイト文字を含む文字列では見た目の幅がずれることがあります。桁を揃えたい場合はmb_str_pad()など、マルチバイトを考慮した関数と組み合わせる工夫が必要です。 - プレースホルダーの型指定と実際の値の型が食い違う
%dに数値以外の文字列を渡すと、PHPは自動的に型変換を試みますが、意図しない結果(0になるなど)になることがあります。事前にバリデーションや明示的なキャストを行うと安全です。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | 書式指定文字列に対し、配列で渡した値を埋め込んで直接出力する |
| 主な用途 | CSVデータの整形表示、動的な数の値を持つレポート生成、テンプレートベースのログ出力など |
printf()との違い | 値を可変長引数ではなく配列としてまとめて渡せる |
vsprintf()との違い | 結果を直接出力する(文字列としては返さない) |
| 注意点 | プレースホルダーと配列要素数の一致、マルチバイト文字の幅ずれ、戻り値は出力バイト数であること |
vprintf() は、配列として既に持っているデータをそのままフォーマット出力したい場合に非常に便利な関数です。printf()・sprintf()・vsprintf() との違いを正しく理解し、値の由来(個別の引数か、配列か)と出力方法(直接出力か、文字列として取得か)に応じて適切に使い分けましょう。
