デバッグし易くしよう! 5:  処理時間を計測

プログラムを実行するサーバーによっては、ある一定時間以上連続して動作して動くプログラムが強制切断されてしまうようです。その意味で自分が作成したプログラムが処理にどれくらいかかるのか知りたいことが多いと思いますので、ここでは、プログラムの開始時間、終了時間、処理時間を表示させる例をご紹介します。

Step5 プログラムの処理時間を処理終了時に表示させてみる

現在時刻を取得する関数にはtime()関数(マニュアル)がありますが、timeでは、秒単位までしか分かりません。ここではマイクロ秒単位まで返してくれるmicrotime()関数(マニュアル)を利用します。

<?php
$start_time=microtime(true);
echo "開始時間: ".date('Y-m-d H:i:s',(int)$start_time)."<br>";

//メイン処理

$end_time=microtime(true);
echo "終了時間: ".date('Y-m-d H:i:s',(int)$end_time)."<br>";
$syori_zikan=$end_time - $start_time;
echo "処理時間:".sprintf('%0.5f',$syori_zikan)."秒<br>";
exit();
?>

開始、終了時刻をマイクロ秒で表示しても何が何だかわかりませんので実際にはdate()関数(マニュアル)を利用して'Y-m-d H:i:s'形式で表示させています。microtime(true)とした場合microtime()関数の戻り値はfloat型となりますが、date()関数の引数として利用できるのは、time()関数と同じint型のため、上の例では(int)でmicrotime関数の結果をcastしています。
処理時間については、sprintf()関数(マニュアル)で、小数点以下5桁表示にしています。

結果は以下のようになります。

開始時間: 2010-01-12 00:45:07
終了時間: 2010-01-12 00:45:07
処理時間:0.00012秒

開始、終了時刻もやはり詳細が見たいので、少し変更

<?php
$start_time=microtime(true);
echo "開始時間: ".formatMicrotime($start_time,'Y-m-d H:i:s')."<br>";

//メイン処理

$end_time=microtime(true);
echo "終了時間: ".formatMicrotime($end_time,'Y-m-d H:i:s')."<br>";
$syori_zikan=$end_time - $start_time;
echo "処理時間:".formatMicrotime($syori_zikan)."秒<br>";
exit();


function formatMicrotime ( $time, $format = null )
{
   if (is_string($format)) {
            $sec  = (int)$time;
         $msec = (int)(($time - $sec) * 100000);
            $formated = date($format, $sec). '.'. $msec;
     } else {
         $formated = sprintf('%0.5f', $time);
    }
    return $formated;
}
?>

処理結果は次になります。 なお上記の function formatMicrotime()は*1を参考にさせていただきました。

開始時間: 2010-01-12 00:51:40.54924
終了時間: 2010-01-12 00:51:40.54937
処理時間:0.00013秒

デバッグし易くしよう! 4:  変数、配列の中身確認 var_dump関数等

プログラムの処理が進んでいる中で、必要な値が正しく変数、配列にセットできているかどうかは大切なことです。これもデバッグ処理時は表示するなり何なりして確認したいものです。

Step4 変数、配列の中身を確認

恐らく、これは良く紹介されている関数だと思いますが、変数に関する情報をダンプする関数 var_dump関数(マニュアル)を利用します。これを利用すると引数として指定した変数の値をデータの型と値を返してくれます。配列にも利用することが可能です。

例を見ていただく方が良いと思いますが、下の例のように文字列、数字(INT,float)、bool値を代入した変数と、配列をvar_dump関数を使って表示させると

<?php
$hensu1="文字をいれました";
$hensu2=125;
$hensu3=123456789012345;
$hensu4=FALSE;
$arr1=array("たま"=>array("syurui"=>"","age"=>1,"かわいいよ"),
            "ぽち"=>array("syurui"=>"","age"=>3,"食いしん坊だよ")
            );
var_dump($hensu1);
var_dump($hensu2);
var_dump($hensu3);
var_dump($hensu4);
var_dump($arr1);
?>

以下のようにデータ型(桁数)値 の形で処理結果が表示ます。

string(24) "文字をいれました"
int(125)
float(1.2345678901234E+14)
bool(false)
array(2) {
  ["たま"]=>
  array(3) {
    ["syurui"]=>
    string(3) "猫"
    ["age"]=>
    int(1)
    [0]=>
    string(15) "かわいいよ"
  }
  ["ぽち"]=>
  array(3) {
    ["syurui"]=>
    string(3) "犬"
    ["age"]=>
    int(3)
    [0]=>
    string(21) "食いしん坊だよ"
  }
}

変数の値だけを取得したい場合には、var_export関数(マニュアル)があります。先ほどのコードの後半をvar_exportで下のように書き変えて実行すると

<?php
$hensu1="文字をいれました";
$hensu2=125;
$hensu3=123456789012345;
$hensu4=FALSE;
$arr1=array("たま"=>array("syurui"=>"","age"=>1,"かわいいよ"),
            "ぽち"=>array("syurui"=>"","age"=>3,"食いしん坊だよ")
            );
var_export($hensu1);
var_export($hensu2);
var_export($hensu3);
var_export($hensu4);
var_export($arr1);
?>

次のように、データ型などの情報は含まず、変数、配列の値だけが戻されます。

'文字をいれました'1251.2345678901234E+14falsearray (
  'たま' => 
  array (
    'syurui' => '猫',
    'age' => 1,
    0 => 'かわいいよ',
  ),
  'ぽち' => 
  array (
    'syurui' => '犬',
    'age' => 3,
    0 => '食いしん坊だよ',
  ),
)


試しに以前から利用しているサンプルコードで、var_dumpを利用してみると

続きを読む

デバッグし易くしよう! 3:  デバッグ時と本番運用時の処理を簡単に切り替える

Step1で、デバッグ用にかなり沢山のecho文を追加しましたが、本番時には無用の長物。しかしながらプログラムの変更を行ったりする場合には、やはり表示できた方が便利。ここでは、定数の値を変更することで、デバッグ用の処理を実行するかどうかを簡単に切り替える方法を説明します。

Step3 デバッグ/運用モードの簡単切り替え

ここではdefine関数(マニュアル)を利用する方法をご紹介します。

まず、define関数で、デバッグモードであるか否かを識別するための定数を定義します。

define('DEBUG', TRUE);

デバッグ時にのみ行いたい処理が 定数DEBUG=TRUEの場合にのみ実行されるようにコードを書きなおします。

if (DEBUG) { デバッグ処理 };

運用時には、最初のdefineの部分を書き変えて定数DEBUG=FALSEとしれば、デバッグ用のコードは実行されません。

define('DEBUG', FALSE);


参考までに、サンプルコードをこれを利用して書き直すと次の通りになります。

続きを読む

デバッグし易くしよう! 2:  PHPのエラー表示レベルを変更 error_reporting を使う

Step2 PHPのエラー表示レベルをerror_reporting($level)で変更

PHPでは、error_reporting($level) という関数を利用すると、引数である$levelに設定する値により、どのレベルのエラー、警告までを表示するかを制御することができます。
詳しくはPHPマニュアルをご覧頂けるとよいのですが、

level 表示対象 補足説明
E_ALL ^ E_NOTICE 変数、変数名のスペルミスなど以外の全てのエラーを表示 通常php.iniのデフォルト設定
E_ALL 全てのPHPエラーを表示  
0 全てのPHPエラーを表示しない  

例えば、デフォルトの状態で、プログラムの8行目の最後の”;”をわざとはずすと
致命的エラー(Parseエラー)となるので下のようにエラーが表示され実行がそこで終了します。

Parse error: syntax error, unexpected T_VARIABLE in C:\(中略)\test_update_fopen.php on line 8

開発段階では、”E_ALL”に設定しておくと色々教えてくれるので便利だと思います。

<?php
error_reporting(E_ALL);


?>

(余談)ちなみに自分が利用しているさくらインターネットではerror_reporting(E_ALL);を設定しても何も表示されません少なくともPHP5の場合。php.iniを編集すれば表示できるようですが、本番環境で色々表示されるのも厄介なのでローカルでデバッグできれば、まあ、いいやと自分は、放置しています


自分のローカル・テスト環境で、PHPのエラーは取り去っておきたいものです。

デバッグし易くしよう! 1: 処理を表示する

はじめに

プログラムにデバッグ向けのコードを入れておくと、開発時はもちろん、運用開始後も、何か問題が発生した場合に、それが、コーディングの問題なのか、データの問題なのか、あるいはTwitter側の問題なのかを切り分ける作業と判断に必要な時間を大幅に短縮できます。
しかし、案外PHPの入門書にも、ネットを見渡してみてもまとまった説明が見当たらないようでしたので、いくつか簡単なものをご紹介してみることにしました。
なお、自分の環境が PHP5であるため前提が PHP5となってしまう点はご了承ください。

サンプル・コード

 デバッグ・コードを追加していくサンプルとして、今回は以下のfopenを利用してTwitterのステータスを更新するコードを利用してみます。

<pre>
<?php
//ユーザー名、パスワード
$username='Twitterユーザ‐名';
$password='Twitterパスワード';

$message='テストメッセージ!!';

$ret= tweet_fop($message, $username, $password);

exit(); //プログラム終了

//fopenを利用したtweet function
function tweet_fop($message, $username, $password)
{
    $url = "http://twitter.com/statuses/update.json?";
    $params = "status=". rawurlencode($message);

    $st = stream_context_create(array(
        "http" => array(
            "method" => "POST",
            "header" => "Authorization: Basic ". base64_encode($username. ":". $password)
        )
    ));

if(($fp = fopen($url.$params, 'r', false, $st)) == false){
      return(false);
}
$contents=stream_get_contents($fp);
fclose($fp);
return ($contents);

}
?>
</pre>

しかし、上のプログラムを実行しても、画面上には何も表示されず、Twitter側を確認して、初めて上手くステータスの更新が出来たのか否かが分かります。


Step1 : どの処理が行われたか表示

例えば、Web経由でツイッターにアクセスした場合でも、手動で”投稿”ボタンを押してもなかなか反映されずに終わってしまう経験をされた方も多いのではないでしょうか?
サンプル・プログラムも特に問題がなければ、すぐにexit()処理に辿りつきますが、もし何か問題が生じていると中々処理が終わらない場合もあるかもしれません。
もし、現在どの処理が実行中かを表示できれば、処理が順調に進んでいるのか、どこかで引っかかってしまっているのか簡単に分かりますね!
・・・という訳でいくつか echo 文を追加してみます。

<?php
echo "プログラム開始<br>";
//ユーザー名、パスワード
$username='Twitterユーザ‐名';
$password='Twitterパスワード';

echo "     ユーザー名、パスワードセット完了<br>";

$message='投稿テストメッセージ!!';
echo "     投稿メッセージのをセット完了<br>";

$ret= tweet_fop($message, $username, $password);

echo "プログラム終了<br>";
exit();


//fopenを利用したtweet function
function tweet_fop($message, $username, $password)
{
echo "     関数tweet_fop:START<br>";

    $url = "http://twitter.com/statuses/update.json?";
    $params = "status=". rawurlencode($message);

    $st = stream_context_create(array(
        "http" => array(
            "method" => "POST",
            "header" => "Authorization: Basic ". base64_encode($username. ":". $password)
        )
    ));

echo "     fopen実行<br>";

if(($fp = fopen($url.$params, 'r', false, $st)) == false){
echo "     fopen 失敗のため 関数tweet_fopから戻る<br>";
      return(false);
}

echo "     fopen成功<br>";

$contents=stream_get_contents($fp);

echo "     fclose実行<br>";
fclose($fp);

echo "     関数tweet_fop:END<br>";
return ($contents);

}
?>

これを実行すると、正常に処理が進んだ場合は下のように画面にメッセージが表示され、処理が最後まで正常に進んだことが確認できます。

プログラム開始
     ユーザー名、パスワードセット完了
     投稿メッセージのをセット完了
     関数tweet_fop:START
     fopen実行
     fopen成功
     fclose実行
     関数tweet_fop:END
プログラム終了

以降こんな感じで少しづつ デバッグのためにプログラムに追加してみると良さそうなことをまとめて見たいと思います。

実験メモ:いろいろな方法で statuses/update

同じことをするにも色々方法がある。

ということで、Twitterに発言するという同じことがどんなコードになるか、試してみた。

  1. Services_Twitter
  2. fopen
  3. file_get_contents
  4. cURL
  5. twitteroauth

1.Services_Twitter

<?php
require_once "Services/Twitter.php";
//ユーザー名、パスワード
$username='Twitterユーザー名';
$password='Twitterパスワード';
//メッセージ
$message='テストメッセージ Services_Twitter 利用です';

$st =& new Services_Twitter($username, $password);

$result = $st->setUpdate($message);

echo $result;

?>


2.fopen
 ※参考にさせていただきました ( PHPTwitterに投稿 http://d.hatena.ne.jp/hirataka522/20080126/1201300282 )

<?php
//ユーザー名、パスワード
$username='Twitterユーザ‐名';
$password='Twitterパスワード';

$message='テストメッセージ fopen 利用';

$ret= tweet_fop($message, $username, $password);
echo "結果=[".print_r($ret)."]<br>";

//fopenを利用したtweet function
function tweet_fop($message, $username, $password)
{
    $url = "http://twitter.com/statuses/update.xml?";
    $params = "status=". rawurlencode($message);

    $st = stream_context_create(array(
        "http" => array(
            "method" => "POST",
            "header" => "Authorization: Basic ". base64_encode($username. ":". $password)
        )
    ));

if(($fp = fopen($url.$params, 'r', false, $st)) == false){
      return(false);
}
$contents=stream_get_contents($fp);
fclose($fp);
return ($contents);

}
?>

3.file_get_contents
 ※参考にさせていただきました ( PHPTwitterに投稿 http://d.hatena.ne.jp/hirataka522/20080126/1201300282 )

<php
//ユーザー名、パスワード
$username='Twitterユーザ‐名';
$password='Twitterパスワード';

$message='テストメッセージ file_get_contents 利用';

$ret= tweet_fgetcon($message, $username, $password);
echo "結果=[".print_r($ret)."]<br>";

//file_get_contentsを利用したtweet function
function tweet_fgetcon($message, $username, $password){
  $context = stream_context_create(array(
    'http' => array(
    'method'  => 'POST',
    'header'  => sprintf("Authorization: Basic %s\r\n", base64_encode($username.':'.$password)).
      "Content-type: application/x-www-form-urlencoded\r\n",
    'content' => http_build_query(array('status' => $message)),
    'timeout' => 5,
    ),
  ));
  $ret = file_get_contents('http://twitter.com/statuses/update.xml', false, $context);
 
  return false !== $ret;
}
?>


4.cURL
 ※参考にさせていただきました ( たったこれだけでPHPからTwitterに投稿できる関数 http://www.multiburst.net/ElectricBrain/2009/07/function-of-php-to-tweet )

<php
//ユーザー名、パスワード
$username='Twitterユーザ‐名';
$password='Twitterパスワード';

$message='テストメッセージ cURL 利用';

$ret= tweet_bycURL($message, $username, $password);
echo "結果=[".print_r($ret)."]<br>";

//cURLを利用したtweet function
function tweet_bycURL($message, $username, $password)
{
    $url = 'http://twitter.com/statuses/update.json';
    $fld = http_build_query(array('status' => $message));
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_USERPWD, $username.':'.$password);
    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $fld);
 
    $ret = curl_exec($ch);
 
     return false !== $ret;
}
?>

5.twitteroauth.php

<?php

//Consumer keyの値
$consumer_key = "コンシューマ・キー";
//Consumer secretの値
$consumer_secret = "コンシューマ・シークレット";
//Access Tokenの値
$access_token = "アクセス・トークン";
//Access Token Secretの値
$access_token_secret = "アクセス・トークン・シークレット";
 
require_once("./twitteroauth/twitteroauth.php"); 

    $message = 'テストメッセージ twitteroauthを利用';   

$connection = new TwitterOAuth($consumer_key,$consumer_secret,$access_token,$access_token_secret);

$result=$connection->post('statuses/update', array('status' => $message));

echo print_r($result);

?>

覚書的メモ:twitteroauth.php + OAuth.php 

自分のbotで実際に利用しているのはこの方法

TwitterREST APIPHPからOAuth認証で利用することを可能にしてくれるライブラリ
現時点の最新版は 2009/12/02 0.2.0 -beta2
最初にbotを作成した時点では 2009/11/04 0.2.0 -beta版を利用していたのですが、最近beta2対応
無印betaからbeta2への変更点は、使う側からみると
  1.最新の HTTP status codeが参照可能になった
  2. (beta版)json形式-> (beta2版)stdClass形式の配列 がTwitterAPI処理の処理結果として返ってくるようになった。

 なお、cURL関数が内部的に利用されているのでこれが利用できる環境である必要があります。
 また同じサイトで入手できますが OAuth.phpも必須

 これの使い勝手の良いところは関数が、Twitter APIのメソッドごとではなく

  function get($url, $parameters = array())
  function post($url, $parameters = array())

 といッた形で、get/post/deleteのメソッド単位で用意されているところ。
 $urlに使いたいTwitter APIのメソッドを$parametersに必要なパラメータを渡してあげればいいのでTwitter API Documentさえ側にあれば新しいメソッドも恐れなくて(多分)良い!

 使い方はこんな感じ

<?php
$consumer_key = "CONSUMER_KEY";
$consumer_secret = "CONSUMER_SECRET";
$access_token = "ACCESS_TOKEN";
$access_token_secret = "ACCESS_TOKEN_SECRET";

require_once("./twitteroauth/twitteroauth.php"); 
$message='テスト用メッセージ';
$connection = new TwitterOAuth($consumer_key,$consumer_secret,$access_token,$access_token_secret);

$result=$connection->post('statuses/update', array('status' => $message));
?>