2009年01月12日

PHP:var_dump だって見やすくなりたい(デバッグ用 var_dump)

「この程度のスクリプトでデバッグ用ライブラリなんてかったりぃなぁ。」
ってな時などにvar_dumpをより見やすくする関数。
前回、var_dump が循環参照に意外と抵抗力があるってな記事を流したんで、これに乗じて今まで使ってた関数をアップグレードしました。しかも、調子こいて色付けやら強調やらも。
var_dump()を使う前に
http://blog.cles.jp/item/1512

pre タグでもいいのですが、string データに html ドキュメントや改行があるとそれもまた見難いので、ついでにそれも変換します。
PHPでvar_dump()を使ったお手軽デバッグコード by ウェブライフハック
http://www.mapee.jp/wlh/phpvar_dump.html

まー。実のところ、いままでは var_export の出力結果を整形してたんですが、これがいっつも循環参照でひっかかる。どうせデバッグ用なので、面倒がって見て見ぬ振りしましたが、とうとう我慢し切れず前回のテストに至った訳ですw
てことで var_dump を可視化エンジン(笑)にしたオブジェクトビジュアライズ関数(!)

■ やってくれること
・ WEBページで var_dump を見やすくする
・ 重要要素には文字修飾する

■ 出力例
object(stdClass)#1 (3) {
      ["ary"]=>
      array(3) {
          [0]=>
          int(100)
          [1]=>
          int(200)
          ["key"]=>
          string(5) "value"
      }
      ["txt"]=>
      string(34) "<div>html ドキュメント</div>"
      ["obj"]=>
      object(stdClass)#2 (1) {
          ["child"]=>
          object(stdClass)#3 (3) {
              ["name"]=>
              string(6) "太郎"
              ["old"]=>
              string(6) "4歳"
              ["recursive"]=>
              object(stdClass)#1 (3) {
                  ["ary"]=>
                  array(3) {
                      [0]=>
                      int(100)
                      [1]=>
                      int(200)
                      ["key"]=>
                      string(5) "value"
                  }
                  ["txt"]=>
                  string(34) "<div>html ドキュメント</div>"
                  ["obj"]=>
                  object(stdClass)#2 (1) {
                      ["child"]=>
                      object(stdClass)#3 (3) {
                          ["name"]=>
                          string(6) "太郎"
                          ["old"]=>
                          string(6) "4歳"
                          ["recursive"]=>
                          *RECURSION*
                      }
                  }
              }
          }
      }
  }


■ 出力例の試料
$a = new stdClass();
$a->ary = array(100, 200, 'key' => 'value');
$a->txt = '<div>html ドキュメント</div>';
$a->obj = new stdClass();
$a->obj->child = new stdClass();
$a->obj->child->name = '太郎';
$a->obj->child->old = '4歳';
$a->obj->child->recursive = $a;

var_html($a); // 出力


■ 自前の環境
PHP Version 5.2.3
System Windows NT
Server API Apache 2.0



 ビジュアライズ関数

// var_dump() for HTML
function var_html($obj, $get = false){
  $space = '  ';
  $indent = ' ';
  $return = "<br />";
  ob_start();
  var_dump($obj);
  $data = ob_get_clean();
  $data = trim($data);
  // string データ部を html エンティティに変換
  $pos = -1;
  while(($pos = strpos($data, 'string', $pos + 1)) !== false){
    // string の前方
    $tx1 = substr($data, 0, $pos);
    // string 以降
    $txx = substr($data, $pos);
    // string 以降から細部切出
    preg_match("/^(string\((\d+)\)\s\")([^\x1b]*)/", $txx, $mts);
    // string データ長
    $len = $mts[2];
    // string からデータ直前まで
    $tx2 = "string($len) " . '"';
    // string データ部
    $tx3 = substr($mts[3], 0, $mts[2]);
    // string データの後方
    $tx4 = substr($mts[3], $mts[2]);
    // データ部の変換
    $txm = str_replace(
      array("\n", ' '),
      array("\\n", ' '),
      htmlentities(
        $tx3,
        ENT_QUOTES,
        mb_internal_encoding()
      )
    );
    $data = $tx1 . $tx2 . $txm . $tx4;
    // string 検索開始位置更新
    $pos = $pos + (strlen(bin2hex($tx2 . $txm)) / 2);
  }
  // 以下可視性アップ
  // string データ部 \n を変換しないと string に騙される
  // 循環参照部分のチェック
  $data = preg_replace(
    "/(\n\s+)(\*RECURSION\*)(\n)/",
    '${1}<b style="color:red;">${2}</b>${3}',
    $data
  );
  // 参照渡し部分とオブジェクトの可視性を上げる
  $data = preg_replace(
    "/(&?)(array)(\(\d+\)\s\{\n)/",
    '<b style="color:purple;">${1}</b><b>${2}</b>${3}',
    $data
  );
  $data = preg_replace(
    "/(&?)(object\([^\n\s]+\))(#\d*\s\(\d+\)\s\{\n)/",
    '<b style="color:purple;">${1}</b><b>${2}</b>${3}',
    $data
  );
  // キーの可視性を上げる
  $data = preg_replace(
    "/(\s\[\"([^\n]+)\"\]=>\n)+?/",
    ' ["<b style="color:blue;">${2}</b>"]=>' . "\n",
    $data
  );
  $data = preg_replace(
    "/(\s\[(\d+)\]=>\n)+?/",
    ' [<b style="color:blue;">${2}</b>]=>' . "\n",
    $data
  );
  // <br />とインデントを追加
  $data = str_replace("\n", $return . "\n" . $indent, $data);
  // 2連の空白を $space に置換
  $data = str_replace(" ", $space, $data);
  if($get){
    return $indent . $data . $return . "\n";
  }else{
    echo $indent . $data . $return . "\n";
  }
}


■ 使い方
var_dump と同様です。
ただ var_export と同じように、出力せずに結果を返すようにすることも可能です。

// 出力します。
var_html($obj);
// 出力せず結果を返します。
$ret = var_html($obj, true);

それと出力例では失念しましたが、参照渡しだと「&」を紫で強調表示します。

■ 注意点

1.インデントの空白は全角空白に置換しています。
2連の&nbsp;でもいいのですが、大きなデータを処理する際、出力がやばくなります。

2.internal_encoding は設定しておいてください。
ま、日本語用のシステム作ってるならまず設定していると思いますが、htmlentities 関数を使ってるので設定してないと化けます。

3.出力データ量がそれほど大きくありません。
var_dump の結果を出力制御関数を使用してキャプチャーしているので、結局出力の制限に左右されます。

4.¥nを<br />に変換してますので環境によって差し替えてください。

あと。当然、言わずもがなですが、使用の場合は自己責任でw

自分は、デバッグ用に memory_get_usage 関連関数の結果と、作成したインスタンスの var_dump(いままでは var_export )の整形結果を自動で常にページ下部に配置して開発するスタイルなんですが、普通はどうしてるんでしょう。

まぁ、一応フレームワークがログからデバッグ画面生成したり、簡易ながら色々自動でやってくれるからそれに甘んじてたりします。
実行モードを設定できるようにして、デバッグモードで以下のようなエイリアスを絡めてもラクです。
PHPでデバッグを簡単にする方法。
http://www.php-seed.net/blog/archives/33

元零細からそのまんま独立してて、若輩ながら自分が指導する立場だったもんで一般が分かってない。(致命的w)
おまけに出身は化学&生命工学だし、何やってんだか分かんない。勝手気ままな人生ってサイコー。・・・でもふと妙な不安感にさいなまれることがありますw



posted by HiFa at 01:35 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする
>>> スパムコメントは消してますよん。 お互い無駄な労力は避けましょう。 <<<

この記事へのコメント

コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック