[PHP]__debugInfo()とは?var_dump()の出力内容をカスタマイズする方法を徹底解説

PHP

はじめに

以前の記事で、変数の型情報と中身を詳細に表示する var_dump() を解説しました。今回取り上げる __debugInfo() は、その var_dump()(および print_r() の一部の挙動)がオブジェクトをどのように表示するかをカスタマイズするための、デバッグ専用のマジックメソッドです。

デフォルトでは、var_dump($obj) はオブジェクトの全プロパティ(private/protectedを含む)をそのまま表示します。しかし、パスワードやAPIキーのような機密情報を含むオブジェクトをそのままダンプしてしまうと、ログやエラー画面に意図せず機密情報が露出するリスクがあります。また、巨大なデータを持つプロパティがあると、デバッグ出力が読みにくくなることもあります。__debugInfo() を実装することで、こうした問題を解決し、デバッグ時に本当に必要な情報だけを分かりやすく表示できます。本記事では基本的な使い方から実践的な活用例まで詳しく解説します。


メソッド概要

項目内容
メソッド名__debugInfo()
種別マジックメソッド
シグネチャpublic function __debugInfo(): ?array
引数なし
呼び出しタイミングvar_dump() がそのオブジェクトを出力しようとする時
戻り値表示したいプロパティの連想配列。nullを返すとデフォルトの挙動になる
対応バージョンPHP 5.6.0以降
影響範囲var_dump() の出力のみ(print_r() や var_export() には影響しない)

出力のカスタマイズの仕組み(イメージ図)

  class User {
      private string $name = '太郎';
      private string $passwordHash = '$2y$10$abc...';
  }

  var_dump(new User());
              │
              ▼
  ┌───────────────────────────┐
  │ 【__debugInfo()が未定義の場合】       │
  │ object(User)#1 (2) {                │
  │   ["name":"User":private]=> "太郎"   │
  │   ["passwordHash":"User":private]=>  │
  │     "$2y$10$abc..."  ← 機密情報が露出! │
  │ }                                    │
  └───────────────────────────┘

  ┌───────────────────────────┐
  │ 【__debugInfo()を実装した場合】       │
  │ object(User)#1 (2) {                │
  │   ["name"]=> "太郎"                  │
  │   ["passwordHash"]=> "***MASKED***"  │
  │ }                                    │
  │ ★この記事の対象                     │
  └───────────────────────────┘

ポイントは、__debugInfo() が実際のオブジェクトの状態を変更するわけではなく、あくまでvar_dump()に「見せる」内容だけをカスタマイズするという点です。実際のプロパティの値は一切変更されず、デバッグ表示上の見え方だけが制御されます。


実践サンプル7選

例1:基本的な使い方

<?php

class BasicDebugInfoDemo
{
    private string $name = '太郎';
    private int $internalCounter = 42;

    /**
     * var_dump()時に表示したい内容を配列として返す
     */
    public function __debugInfo(): array
    {
        return ['name' => $this->name];
        // internalCounterは表示されない
    }
}

var_dump(new BasicDebugInfoDemo());

例2:機密情報をマスキングするクラス

<?php

class UserCredentials
{
    public function __construct(
        private string $username,
        private string $apiKey,
        private string $passwordHash
    ) {
    }

    /**
     * APIキーとパスワードハッシュを、
     * デバッグ出力上はマスクして表示する
     */
    public function __debugInfo(): array
    {
        return [
            'username'     => $this->username,
            'apiKey'       => $this->maskSecret($this->apiKey),
            'passwordHash' => '***HIDDEN***',
        ];
    }

    private function maskSecret(string $value): string
    {
        return substr($value, 0, 4) . str_repeat('*', max(0, strlen($value) - 4));
    }
}

var_dump(new UserCredentials('taro', 'sk_live_abcdef123456', '$2y$10$hashedvalue'));

例3:巨大なデータを要約して表示するクラス

<?php

class LargeDataset
{
    private array $rows;

    public function __construct(array $rows)
    {
        $this->rows = $rows;
    }

    /**
     * 大量の行データをそのままダンプすると読みにくいため、
     * 件数とサンプルだけを表示する
     */
    public function __debugInfo(): array
    {
        return [
            'row_count'   => count($this->rows),
            'sample_rows' => array_slice($this->rows, 0, 3),
        ];
    }
}

$dataset = new LargeDataset(array_fill(0, 10000, ['value' => 'data']));
var_dump($dataset); // 全10000件ではなく、件数とサンプル3件のみ表示される

例4:計算済みのプロパティを追加して表示するクラス

<?php

class ShoppingCart
{
    private array $items = [];

    public function addItem(string $name, float $price, int $quantity): void
    {
        $this->items[] = ['name' => $name, 'price' => $price, 'quantity' => $quantity];
    }

    /**
     * 実際には保持していない「合計金額」のような
     * 計算結果をデバッグ表示に含める
     */
    public function __debugInfo(): array
    {
        $total = array_sum(array_map(
            fn (array $item) => $item['price'] * $item['quantity'],
            $this->items
        ));

        return [
            'items'       => $this->items,
            'total_price' => $total, // 実プロパティではなく計算値
            'item_count'  => count($this->items),
        ];
    }
}

$cart = new ShoppingCart();
$cart->addItem('ノート', 300, 2);
$cart->addItem('ペン', 120, 3);
var_dump($cart);

例5:循環参照を持つ構造をデバッグしやすくするクラス

<?php

class TreeNode
{
    public ?TreeNode $parent = null;
    /** @var TreeNode[] */
    public array $children = [];

    public function __construct(public string $label)
    {
    }

    public function addChild(TreeNode $child): void
    {
        $child->parent = $this;
        $this->children[] = $child;
    }

    /**
     * 親への参照を含めてダンプすると出力が無限に近い量になりがちなため、
     * 親はラベルのみの参照として簡略表示する
     */
    public function __debugInfo(): array
    {
        return [
            'label'    => $this->label,
            'parent'   => $this->parent?->label ?? null,
            'children' => array_map(fn (TreeNode $c) => $c->label, $this->children),
        ];
    }
}

$root = new TreeNode('root');
$child = new TreeNode('child');
$root->addChild($child);
var_dump($child); // parentが"root"という文字列として簡潔に表示される

例6:開発環境と本番環境でダンプ内容を切り替えるクラス

<?php

class EnvironmentAwareEntity
{
    public function __construct(
        private string $publicData,
        private string $internalDebugData,
        private bool $isDevelopment = false
    ) {
    }

    /**
     * 環境フラグに応じて、
     * 開発環境でのみ詳細なデバッグ情報を表示する
     */
    public function __debugInfo(): array
    {
        $info = ['publicData' => $this->publicData];

        if ($this->isDevelopment) {
            $info['internalDebugData'] = $this->internalDebugData;
        }

        return $info;
    }
}

$devEntity = new EnvironmentAwareEntity('公開データ', '内部デバッグ情報', true);
$prodEntity = new EnvironmentAwareEntity('公開データ', '内部デバッグ情報', false);
var_dump($devEntity);
var_dump($prodEntity);

例7:print_r()には影響しないことを確認するデモ

<?php

class PrintRComparisonDemo
{
    private string $secret = '機密情報';

    public function __debugInfo(): array
    {
        return ['secret' => '***MASKED***'];
    }
}

$obj = new PrintRComparisonDemo();
echo "--- var_dump() ---\n";
var_dump($obj); // __debugInfo()の内容が使われる(マスクされる)

echo "--- print_r() ---\n";
print_r($obj); // __debugInfo()は影響せず、実際のプロパティがそのまま表示される

関連機能との比較

機能役割__debugInfo()との違い
__debugInfo()var_dump()の出力内容をカスタマイズする本記事の対象。var_dump()のみに影響する
var_dump()変数を型情報付きで詳細に出力する__debugInfo()が定義されていればその内容を使って表示する
print_r()変数を人間が読みやすい形式で出力する__debugInfo()の影響を受けない(実プロパティがそのまま表示される)
__toString()オブジェクトの文字列表現を定義するデバッグ表示ではなく、一般的な文字列変換を対象とする
JsonSerializablejson_encode()時の出力を制御するシリアライズ形式がJSONである点が異なる、別の仕組み

よくある落とし穴(注意点)

  1. print_r()には影響しないことを理解する __debugInfo() は var_dump() の出力のみに影響し、print_r() や var_export() には一切影響しません。機密情報の保護を目的とする場合、print_r() 経由での出力にも別途注意する必要があります(例7を参照)。
  2. 機密情報のマスキングを過信しない __debugInfo() はあくまでデバッグ表示を制御するものであり、オブジェクト自体に機密情報が保持されていること自体は変わりません。本番環境のログに機密情報を含むオブジェクトを出力する運用そのものを見直すことも重要です。
  3. nullを返すとデフォルトの挙動に戻る __debugInfo() が null を返した場合、PHPは通常のプロパティダンプ処理にフォールバックします。意図的にデフォルトの挙動に戻したい特定のケースで活用できますが、通常は配列を返すのが一般的です。
  4. デバッグ用の加工処理が重くなりすぎないようにする __debugInfo() 内で複雑な計算やデータベースアクセスのような重い処理を行うと、単にデバッグのために var_dump() しただけで、予期しないパフォーマンス低下を招くことがあります。表示用の軽量な加工にとどめましょう。
  5. 本番環境での呼び出し自体を想定しておく 開発中のデバッグ用途として実装されることが多い __debugInfo() ですが、エラーログの出力やデバッガツールなど、本番環境でも何らかの形で var_dump() 相当の処理が走る可能性があります。常に安全な内容を返す設計を心がけましょう。

まとめ

観点まとめ
何をするメソッドかvar_dump()がオブジェクトを出力する際の表示内容をカスタマイズする
主な用途機密情報のマスキング、巨大データの要約表示、循環参照構造の簡略化
影響範囲var_dump()のみ。print_r()やvar_export()には影響しない
戻り値の挙動配列を返せばその内容が使われ、nullを返すとデフォルトの挙動に戻る
注意点print_r()での別途対策の必要性、重い処理を避けること、本番環境での呼び出しも想定すること

__debugInfo() は、デバッグ時の情報漏洩を防ぎつつ、本当に必要な情報を分かりやすく表示するための、実用性の高いマジックメソッドです。特にパスワードやAPIキーのような機密情報を扱うクラスでは、積極的に実装を検討し、安全で見やすいデバッグ体験を実現しましょう。

タイトルとURLをコピーしました