Asial Blog

Recruit! Asialで一緒に働きませんか?

FirebugでPHPをデバッグするツールまとめ

カテゴリ :
バックエンド(プログラミング)
タグ :
Tech
Firefox
エクステンション
ログ
デバッグ
こんにちは、亀本です。最近は体調がすこぶる絶不調です。季節の変わり目なので、皆さんも気をつけてください。

さて、ちょろちょろと話題に上ることの多いFirefoxを使ったPHPのデバッグ手法ですが、いくつか出てきたのでこの辺でサクッとまとめておこうかと思いました。
結果的に、だいぶ膨らみましたが。。。まとめ力ないなorz

1.Buggy クラス



AJAX magazine というサイトで公開されていたサンプルスクリプトで、クラス1つだけの小さなライブラリです。
以前、PHPプロ!ニュースでもご紹介した方法です。

この方法は、PHPのエラーをハンドリングし、その内容をscriptタグに出力し、console.info()を用いてFirebugのコンソールに渡す、という仕組みです。そのため、Firebugは必須です。

記事掲載当時は、buggy.class.phpがダウンロードできたのでしょうか。。。リンク先には今はダウンロードリンクがなく、サンプルコードが掲載されているだけなので、コピペするなりして保存してください。
一応、利便性のために現時点でのbuggy.class.phpのコードをこちらのブログにも掲載しておきます。

  1. <?php
  2. /**
  3.  * Buggy inside Firebug - Advanced
  4.  * 
  5.  * @package GONX
  6.  * @author hatem <hatem at php dot net>
  7.  * @website http://phpmagazine.net
  8.  * @copyright Copyright (c) 2005-2007
  9.  * @version $Id: buggy.class.php,v 2.2 2007/02/10 04:10:12 hatem Exp $
  10.  * @access public
  11.  **/
  12. class buggy {
  13.  
  14.   function _init(){
  15.     $old_error_handler = set_error_handler(array("buggy","ErrorHandler"));
  16.     define ("FATAL",E_USER_ERROR);
  17.     define ("WARNING",E_USER_WARNING);
  18.     define ("NOTICE",E_USER_NOTICE);
  19.     // configure reporting level
  20.     error_reporting (FATAL | WARNING | NOTICE);  
  21.   }
  22.  
  23.   /**
  24.    * Buggy::getmicrotime()
  25.    * 
  26.    * @return 
  27.    **/
  28.   function getmicrotime(){
  29.     $mtime = microtime();
  30.     $mtime = explode(" ",$mtime);
  31.     $mtime = $mtime[1] + $mtime[0];
  32.     return ($mtime);
  33.   }
  34.  
  35.   /**
  36.    * Buggy::SetMicroTime()
  37.    * 
  38.    * @param $module
  39.    * @return 
  40.    **/
  41.   function SetMicroTime($module)
  42.   {
  43.     global $Buggy;
  44.     $Buggy[$module] = Buggy::getmicrotime();
  45.     return $Buggy[$module];
  46.   }
  47.   
  48.   /**
  49.    * Buggy::GetExecutionTime()
  50.    * 
  51.    * @param $module
  52.    * @return 
  53.    **/
  54.   function GetExecutionTime($module)
  55.   {
  56.     global $Buggy;
  57.     $end = Buggy::getmicrotime();
  58.     $res = $end - $Buggy[$module];
  59.     return $res;
  60.   }
  61.  
  62.   /**
  63.    * Buggy::Track()
  64.    *
  65.    * @param $module
  66.    * @param $msg    additional message to display
  67.    * @return 
  68.    **/
  69.   function Track($module, $msg = '') {
  70.   
  71.     global $Buggy;
  72.   
  73.     if (!isset($Buggy[$module])) {
  74.       
  75.       Buggy::SetMicroTime($module);
  76.       
  77.     } else {
  78.       
  79.       $duration = Buggy::GetExecutionTime($module);
  80.       Buggy::logMessage($module,'Notice',$msg,$duration);
  81.     
  82.     }
  83.   
  84.   }
  85.   
  86.   /**
  87.    * Buggy::logMessage()
  88.    * 
  89.    * @param string $module
  90.    * @param string $type
  91.    * @param string $message
  92.    * @param string $duration
  93.    * @return 
  94.    **/
  95.   function logMessage($module = '', $type = '', $message = '', $duration = ''){
  96.     if ($module == 'PHPError') {
  97.     
  98.       if ($type == 'WARNING') {
  99.         echo "<script>console.warn(\"[Buggy] - $module [$type] - $message\")</script>\n";
  100.       } elseif ($type == 'Fatal') {
  101.         echo "<script>console.error(\"[Buggy] - $module [$type] - $message\")</script>\n";
  102.       }else{
  103.         echo "<script>console.info(\"[Buggy] - $module [$type] - $message\")</script>\n";
  104.       }
  105.         
  106.     } else {
  107.       echo "<script>console.info(\"[Buggy] - $module [$type] - $message - Execution Time: $duration \")</script>\n";
  108.     }
  109.   }
  110.   
  111.   /**
  112.    * Buggy errors manager
  113.    * 
  114.    * @param $errno
  115.    * @param $errstr
  116.    * @param $errfile
  117.    * @param $errline
  118.    * @return 
  119.    **/
  120.   function ErrorHandler ($errno, $errstr, $errfile, $errline) {
  121.     switch ($errno) {
  122.       case FATAL:
  123.     Buggy::logMessage('PHPError', 'Fatal', "{$errno} : $errstr - Fatal error in line ".$errline." of file ".$errfile);
  124.     exit(1);
  125.     break;
  126.     
  127.     case WARNING:
  128.       Buggy::logMessage('PHPError', 'WARNING', "{$errno} : $errstr - WARNING error in line ".$errline." of file ".$errfile);
  129.     break;
  130.     
  131.     default:
  132.       Buggy::logMessage('PHPError', 'Notice', "{$errno} : $errstr - Notice error in line ".$errline." of file ".$errfile);
  133.     break;
  134.     }
  135.   }
  136.  
  137. }
  138. ?>

使い方は単純で、buggy.class.phpをインクルードしてInit()メソッドを実行したら、あとはエラーが発生した個所にscriptタグが挿入されます。
  1. <?php
  2. require_once "buggy.class.php";
  3.  
  4. Buggy::Init();
  5.  
  6. trigger_error("わーにんぐ", WARNING);
  7. trigger_error("のーてぃす", NOTICE);
  8. trigger_error("ふぇーたるえらー", FATAL);
  9. ?>
ハンドリングしているのは上記の3つのエラーログのみです。
このサンプルはtrigger_errorでエラーを発生させているので、当然ですがFATALで止まります(^^;

かなり偏った実装なので、このままだとデバッグを行うというよりは、エラーログを取得するのが主目的になります。
ですが、ソースは単純で拡張も容易なので、logMessageメソッドを拡張したり、独自のデバッグ情報取得メソッドを実装してもよいでしょう。

簡易なライブラリですが、ややこしいことはほとんど考える必要もないので、小さなプログラムの開発を行う時には、わりと気楽に使えそうです。


2.PEAR::Log



言わずとしれたPEARライブラリのLogクラスですが、実はFirebugにログを出力する機能を備えていました。
これは先日、こちらの記事によって有名になった機能です。
案としては結構前からあり、今年の5月にリリースされた1.9.11での実装だったんですが、あまり知られていない機能だったようです。

かくいう自分もこんな話は忘却の彼方でした(^^;
こういうものをパッと発掘して試せる人ってすごいですよね。

基本的な発想はBuggyクラスと同じで、PEAR::Logで発生させたエラーログを、console.info()等を使ってFirebugのコンソールに渡しています。

使い方は・・・上記のサイトを見てもらった方が早いかもしれませんが、せっかくなので載せておくと
  1. <?php
  2. require_once "Log.php";
  3. $firebug = &Log::singleton('firebug', 'test', 'PHP LOG',  array('buffering' => true), PEAR_LOG_DEBUG);
  4.  
  5. $firebug->log('わーにんぐ', PEAR_LOG_WARNING);
  6.  
  7. $firebug->debug('でばっぐー');
  8. $firebug->info('いんふぉ');
  9. $firebug->err('えらー!!');
  10. $firebug->crit('くりてぃかる!!');
  11. $firebug->warning('これもわーにんぐ');
  12.  
  13. $firebug->debug($_SERVER);
  14.  
  15. ?>
これを実行すると、Firebugのコンソールに各種ログが出力されます。

最初にsingletonメソッドでインスタンスを生成しています。
singletonメソッドは、第1引数に出力ログの種類を指定し、第2引数にその名前、第3引数に識別子、第4引数に出力ログのオプション、第5引数に出力するログレベルの下限を指定します。

後はひたすらlogメソッドでログを出力させています。
debugなどの各メソッドは、logメソッドの第2引数を省略する際のショートカットになっています。

発生させることのできるログの種類は、PEAR::LogライブラリのユーザードキュメントにあるLog Levelsの項を参照してください。

最後のdebugメソッドのように変数を出力しておくことで、デバッグ情報をいちいちwindow上に表示させずにFirebugコンソールで確認する、といったデバッグ手法が利用できます。singletonメソッドの第5引数を変えるだけで出力されるエラーレベルが変更できるので、環境による使い分けも楽に出来ます。


なお、このPEAR::Logライブラリは、以前PHPプロ!Tips MLでも取り上げましたが、このFirebugでのログ取得以外にも多くのログ出力機構をもっています。
Tips MLの紹介内容は少々古く、現在ではPHPプロ!Tips MLの掲載内容に加えて、daemon/display/error_log/mdb2/win(window)/null/sqlite/firebug といった方式が追加されています。
さらに、compositeというハンドラを用いることで、複数の出力を組み合わせて使用することが可能になるなど、かなり多機能です。

Firebugで出せる事自体よりも、そういった高機能ロギングライブラリとして、PEAR::Logはお勧めです。
# Firebugで出す機能がしょぼい、ってことじゃないですよ(^^;

3.FirePHP



FirePHPは、PHPライブラリとFirefoxのエクステンションを併用することで、Firebugに便利なPHP専用コンソールを増設してくれるライブラリ群です。

上記2つがscriptタグを用いてログをJavaScriptで出力する方式だったのに対し、こちらはFirefoxエクステンションを用いることで、HTMLに一切の変更を加えることなくデバッグ情報をやり取りします。
また、そもそも中心となる目的も上記ふたつとは少し異なっており、ログを出力するよりもデバッグ情報をやり取りする事に重点を置いています。

方式としては、PHP側とFirefoxエクステンション側で共通に使用するヘッダとして、「X-PINF-org」接頭辞のついた独自ヘッダを利用してデータのやり取りを行います。
必要な情報は全てHTTPヘッダでやり取りしてしまうので、表示側には余計なデータが現れません。

エクステンションとPHPのPEARパッケージはこちらのページからインストールとダウンロードが可能です。

実行するサンプルは以下です。
  1. <?php
  2. require_once "FirePHP_Build/Init.inc.php";
  3.  
  4. FirePHP::Init();
  5.  
  6. $hoge1 = "ほげ1";
  7. $hoge2 = "ほげ2";
  8. $hoge3 = "ほげ3";
  9.  
  10. FirePHP::SetVariable(true, array('REQUEST','TEST1'), $hoge1);
  11. FirePHP::SetVariable(true, array('SESSION','TEST2'), $hoge2);
  12. FirePHP::SetVariable(true, array('APPLICATION','TEST3'), $hoge3); // これは動かない。。
  13.  
  14. ?>

Init()メソッドを呼び出し、あとは適宜SetVariableメソッドでFireBugに渡すようにアサインするだけです

FirePHPエクステンションをインストールしたFirefoxのFirebug内に、FirePHPのタグが出来上がっており、上記のスクリプトにアクセスすると、デフォルトでアサインされているスーパーグローバル変数群とともに、SetVariableメソッドでアサインしたデータが表示されます。

なお、デバッグデータの受け渡しが行われるため、セキュリティを気にする必要がある場合にはCookieによるアクセスキー設定が可能です。Init()メソッドの前で
FirePHP::SetAccessKey('適当な文字列');
としてアクセスキーとなる文字列を設定し、これをFirePHP-AccessKeyというクッキーの値として持たせておくことで、クッキーの持ち主にのみデータを送信するようになります。


などなど、色々と面白い機能が搭載、と完璧にいってほしいところなのですが。。。現時点ではところどころ残念なバグもあります(^^;

まず、上記のスクリプトですが、私が試した環境では3つ目のAPPLICATION指定がうまく動きませんでした。
恐らくFirefoxエクステンション側にバグがあるのか、はたまた指定の仕方が違うのか。。。
区分けがわかりにくくなる、という程度の問題ですが、もったいない感じです。

また、実はこのPEARクラス、ところどころ実装が荒っぽかったりするもので、ちょくちょくNoticeを吐くことがあります。。。
この時に、display_errorsがOnだと、そのエラーが出力されることが原因でheaederの受け渡しがうまく行えなくなり、正常に動作しない場合があります。
# 検証の際、最初はデータのやり取り方式を理解していなかったので、これに結構はまりました(^^;
そのため、E_Noticeを表示しない、display_errorsをOffにする、など、何かしらの気遣いは必要になるかもしれません。

ただ、そういった部分を総合的に考えても、このFirePHPは十分に便利なライブラリだと思います。

おまけ:Xdebug Helper



Xdebug HelperはFirefoxからXdebugの操作をするFirefoxエクステンションです。
これも以前、PHPプロ!ニュースでご紹介したことがあります。
Firebugでデバッグをするわけでもないし、XdebugのON/OFFをFirefoxから行えるというだけなんですが(笑
ついでなので、おまけ的に紹介します。

このエクステンションは、インストールするとFirefoxのステータスバーにアイコンが追加されます。
このアイコンをクリックすることでXdebugのデバッグセッションのON/OFFを切り替え、ONにしておくとXDEBUG_SESSION=(設定値)というクッキーを送信してくれるようになります。
大した機能ではないですが、手動でやるといちいち面倒なので、気軽にXdebugによるデバッグが実行できるようになるのはわりと助かりものです。


結局、まとめの割に文章量が多くてまとまっていないというオチに。。。orz
まぁ、なんにせよ(^^;
こういったライブラリが快適なデバッグ環境構築の一助になれば幸いです。