2010-01-29
実験)DBを使わないで同じ処理をしようとすると・・・
自分のbotで、フォロワーさんからお願いされた事を覚えておいて、後でお仕事する的なことをやってみたことがあるのですがDBのテーブルを利用して実装してしまったので、もし普通のファイルを利用したらどうなるかなとちょっと考えてみました。
seriarizeかCSVファイル形式でデータを保持して、プログラム中で配列に展開してメンテナンスするのが、簡単かなとゴニョゴニョやってみましたが、出来上がったファイルを人間が見て直感的にわかりやすいと言う意味ではCSVが良さそうかなーと言うわけで
Replyするbotの構造
普通はプログラム等組む場合、仕様書を書いたりするのですが、Twitter botの場合、趣味の範疇なもので、イキナリ作っておりましたが、もし書いてみたらどんな感じかなという話。自己満足なので、適当に読み飛ばしていただけますと幸い。
最低限の機能を考えると…
0.準備処理:接続に必要な情報等々準備
1.Timeline取得:Twitter Timelineを取得する
2.返信対象があるか判断: なければ処理終了
3.返信準備:返信用のメッセージの準備
4.Status/update: Twitterで用意した返信メッセージでステータス更新
5.まだ返信処理を続行するなら2に戻る、なければ処理終了
2010-01-24
TwitterAPIでimageが上手く扱えない
Twitter APIで、account/update_profile_imageを使えば、アイコンが変更できる筈なのですが、今のところ不成功。
BASIC認証については、こちらを参考にさせていただいて上手くいったのですが、OAuthが今のところ上手くいっていません。
Googleのここを見る限りなんとかなりそうな雰囲気なのですが・・・
自分のメモ用に上記を日本語訳しておくと
2010-01-13
Twitter botの テスト環境、本番環境
他のbot作成者の方がどのように、コード管理や、テストを行われているか知らないのですが、自分はこんな感じでやっていますというお話。
[Twitter アカウント]
通常自分が使っているアカウントの他に
3.テスト用アカウント x 2
の4種類のアカウントを利用しています。
そのうち 1,2のbotアカウントについては、OAuth対応のため、アプリケーション登録済み。
[ローカル/サーバー]
ローカル(自宅PC)
- OS:Windows Vista Ultimate SP2
- XAMPP Window版 1.7.2
- PHP 5.3.0
サーバー(さくらインターネット)
※本当は、自宅PCの環境を本番環境として利用しているさくらと PHPのバージョンやら何やら一致させた方が安全なのですが、そこまで、最先端なことをしていないし、大丈夫かな?とずるずるそのままでやっています。
ただ、実際のところは、自宅PC上と、サーバに上げた時で動作が違うことも発生するので、そこは、try & errorで潰すという泥臭い作業を行っています。
[コード管理]
本当はSVN等利用して、きっちり管理すると良いのですが、1世代以上前に戻る必要が生じたことも現時点は、無いので、大きな変更を行う前にごっそりバックアップを取っておく位で、通常は、稼働中、開発中の二世代を違うディレクトリに入れて、開発、テストが終わったら本番(稼働中)ディレクトリに移すという原始的管理中。
ローカル、サーバそれぞれに、開発中、稼働中の2種類のディレクトリが存在し
- ローカル 開発中ディレクトリでコード変更
- ローカル環境 開発中ディレクトリ&テスト・アカウントで テスト
- ローカル 開発中ディレクトリ ⇒ サーバ 開発中ディレクトリ に変更したphpファイルコピー
- サーバー環境 開発中ディレクトリ&テスト・アカウントで テスト
- テストが全てOKであれば ローカル 開発ディレクトリから ローカル、サーバの稼働中コード用ディレクトリに ファイルコピー
- サーバ環境 本番用ディレクトリのコード&本番アカウント で最終チェック
といった、感じに開発したコードのテストと本番への実装を行っています。(ていう程偉そうなものでもないですが)
なお、ユーザーの接続情報は、よく配布されているコードですと、プログラムにベタ書きですが、自分の場合は、プログラム本体とは別のファイルに入れて、開発ディレクトリには、テスト・アカウントの情報、本番用ディレクトリには、本番アカウントの情報をあらかじめ仕込んでいます。
こうしておくと、開発用のディレクトリにあるコードを実行すれば、テスト・アカウントで接続し、本番用のディレクトリ内のコードを実行すれば本番用アカウントでTwitterに接続できるので、開発⇒本番 で、アカウント情報を書き変える手間が省けます。*1
2010-01-12
デバッグし易くしよう!(おまけ) testLog Class をサンプルコードに適用した例
初心者向, PHP, Twitter bot, デバッグ
前回のデバッグ用Class testLogをサンプルコードに組み込んでみた例です。
開発時、運用時などの状況により、表示の有無、出力範囲の制御を次の二つの定義を組み合わせで制御できます。
//DEBUG処理の切り替え 本番運用時にはFALSEに変更すること
define('DEBUG', TRUE); //デバッグ時
//define('DEBUG', FALSE); //本番運用時
testLog::setdispLogF( TRUE ); // ログを画面表示するか? TRUE/FALSE
また、メインプログラム側で、ログファイル名に日付を加え、日付ごとにログファイルが分かれるようにしています。
//ログ処理準備
$Kyou=date("Ymd",time());
$logDir='./log/'; //ログディレクトリ
$logFname=$logDir.'test_syori_log'.$Kyou.'.txt';//ログファイル名
require_once("test_log.php"); //test class
testLog::setLogfile($logFname); //ログファイル名の指定
デバッグし易くしよう! 7: デバッグ用のClass
初心者向, PHP, Twitter bot, デバッグ
特にStep6のログファイルの出力のことを書きましたが、
毎回error_log...で、ログファイルの出力先等を記載するのは面倒臭い。
ログファイルに出力したいことと、画面に表示したいことは同じ場合も多い。
ということで、実際の自分のプログラムではデバッグ処理用のClassを作成して使い回しています。本当に使っているものはもっと複雑というかラーメンかスパゲッティか的な物なのですが、少し単純化してみたものをご参考までに載せて説明させて頂きたいと思います。*1
Step7: デバッグ用Classを使う
仮に testLogと名付けたClassを作成しました。
このClassを利用してできることは以下の通りとなります。
1.任意のテキスト文字列を表示/ログファイルに出力する。
2.ログファイルへの出力に際しては、以下の内容も追加した状態で出力される。
a.処理が行われた時間
b.プログラム名
3.ログファイルに、
プログラムの開始、終了時刻および処理時間を出力することも可能
4.文字列の表示、非表示は切り替え可能
プログラム側でのこのClassの利用例は次のようになります。
<?php require_once("test_log.php"); //testLog class testLog::setLogfile('./logfile/test_log.txt'); //ログファイル名の指定 testLog::setProgname( 'testprogram_name' ); // プログラム名 testLog::setdispLogF( TRUE ); // ログを画面表示するか? TRUE/FALSE testLog::save_msg('処理スタート','====['.__FILE__.']=='); // メインの処理 testLog::save_msg('処理終了','================================='); testLog::keikazikanLog(); ?>
上のようなコードを含むプログラムを実行した場合。
./logfile/test_log.txtには、以下のような形式でログを出力することができます。
"2010-01-12 01:13:48","0.8619","testprogram_name","処理スタート","====[C:\(中略)\testprogram.php]==" "2010-01-12 01:13:48","0.72220","testprogram_name","処理終了","=================================" "2010-01-12 01:13:48","0.72433","testprogram_name","[ 2010-01-12 01:13:48.8614 ]-[ 2010-01-12 01:13:48.72419 ]"," 開始からの処理時間= [0.63805]sec"
画面の表示は下記のようになります。
testLog: 処理スタート,====[C:\(中略)\testprogram.php]== testLog: 処理終了,================================= testLog: [ 2010-01-12 01:13:48.8614 ]-[ 2010-01-12 01:13:48.72419 ], 開始からの処理時間= [0.63805]sec
見ておわかり頂けると思いますが、最初に出力対象のログファイル名、プログラム名、画面表示の有無を設定してしまえば後は、
testLog::save_msg('text1','text2');
のように、表示、出力したい情報を文字列形式にしてセットするだけです。なお、2つ目の項目は省略も可能です。
以下、実際のtestLog Class となります。
デバッグし易くしよう! 6: ログファイルに処理記録を残す
初心者向, PHP, Twitter bot, デバッグ
ここまでは、処理の進行状況や、変数の値などを表示する方法を紹介してきましたが、実際に本番サーバー上で、botの運用を開始してしまうと、毎回の処理状況を付きっきりで目視できる訳ではありません。
PHPのerror_log関数(マニュアル)を利用すると、ログとして任意のエラーメッセージをファイルに書きだすことができます。
これを利用して、処理のログをファイルに書き出してみることにします。*2
Step6 ログファイルに処理記録を残す
error_log関数を利用してログをファイルに書き出す方法は簡単です。例えば、syori.txtというファイルに"プログラムを実行しました”というメッセージを書き出したいのであれば、下記のように書くだけでOKです。
<?php error_log("プログラムを実行しました",3,'syori.txt'); ?>
これを今までも利用しているサンプルコードに埋め込むんで、step1で追加した、echo文をログファイルに出力するようにしてみると例えば、次のようになります。
デバッグし易くしよう! 5: 処理時間を計測
初心者向, PHP, Twitter bot, デバッグ
プログラムを実行するサーバーによっては、ある一定時間以上連続して動作して動くプログラムが強制切断されてしまうようです。その意味で自分が作成したプログラムが処理にどれくらいかかるのか知りたいことが多いと思いますので、ここでは、プログラムの開始時間、終了時間、処理時間を表示させる例をご紹介します。
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()は*3を参考にさせていただきました。
開始時間: 2010-01-12 00:51:40.54924 終了時間: 2010-01-12 00:51:40.54937 処理時間:0.00013秒
*1:http://www.sound-uz.jp/php/note/startDebug のdebugクラスを物凄く参考にさせていただいております。
*2:error_log ( string $message [, int $message_type = 0 [, string $destination [, string $extra_headers ] )関数
エラーメッセージを Web サーバのエラーログ、 TCP ポート、あるいはファイルに送ります
第2引数$mesage_typeに"3"をセットした場合に、第3引数Rdestinationで指定したログファイルに第1引数で指定したエラーメッセージが書き出されます。
2010-01-11
デバッグし易くしよう! 4: 変数、配列の中身確認 var_dump関数等
初心者向, PHP, Twitter bot, デバッグ
プログラムの処理が進んでいる中で、必要な値が正しく変数、配列にセットできているかどうかは大切なことです。これもデバッグ処理時は表示するなり何なりして確認したいものです。
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: デバッグ時と本番運用時の処理を簡単に切り替える
初心者向, PHP, Twitter bot, デバッグ
Step1で、デバッグ用にかなり沢山のecho文を追加しましたが、本番時には無用の長物。しかしながらプログラムの変更を行ったりする場合には、やはり表示できた方が便利。ここでは、定数の値を変更することで、デバッグ用の処理を実行するかどうかを簡単に切り替える方法を説明します。
Step3 デバッグ/運用モードの簡単切り替え
ここではdefine関数(マニュアル)を利用する方法をご紹介します。
まず、define関数で、デバッグモードであるか否かを識別するための定数を定義します。
define('DEBUG', TRUE);
デバッグ時にのみ行いたい処理が 定数DEBUG=TRUEの場合にのみ実行されるようにコードを書きなおします。
if (DEBUG) { デバッグ処理 };
運用時には、最初のdefineの部分を書き変えて定数DEBUG=FALSEとしれば、デバッグ用のコードは実行されません。
define('DEBUG', FALSE);
参考までに、サンプルコードをこれを利用して書き直すと次の通りになります。
デバッグし易くしよう! 2: PHPのエラー表示レベルを変更 error_reporting を使う
初心者向, PHP, Twitter bot, デバッグ
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: 処理を表示する
初心者向, PHP, Twitter bot, デバッグ
はじめに
プログラムにデバッグ向けのコードを入れておくと、開発時はもちろん、運用開始後も、何か問題が発生した場合に、それが、コーディングの問題なのか、データの問題なのか、あるいは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 プログラム終了
