ゼロからはじめるぜ! Twilio – シーズン2 第6回

20140219-01.jpg

こんにちわ。ネットワークエンジニアのまさです。
今日は Perl スクリプトを用いて、前回までに作ってきた MySQL データベース上のテーブルを引いたり更新したりする部位を作っていきます。
ちなみに写真は、我が Twilio 事業部のボス 小出大先生と、Twilio Inc 創設者の 1 人 エヴァンです。

ちなみにこの尻洗浄便座遠隔操作は、エヴァンが昨年日本に来た時、ラーメン屋で
「ねえ、ウォシュレット試してみた?」
「いや、ちょっとアレは ...」
「ちょ、なんで試さないの!? あれは日本が誇る世界的大発明なんだぜ!」
という雑談をしている最中に思いついたネタなんです。

 

20140219-02.jpg

毎度のことながら、今回作る範囲を把握しておきます。今回は、左図の右側にある緑の枠の部分です。
前にも少し触れましたが、今回の範囲を PHP で書けるよ!という方は PHP で実現してももちろん OK です。私は Perl でのやり方しか知らないので Perl でやっています。

 

正直言ってしまうと、今回の単元は全く初めてプログラミング言語に触れる方には少し敷居が高いです。データベースアクセス用のモジュール独自のお作法に沿って命令を書く必要があります。
1 行 1 行が理解できなくても構いません。ざっとこのスクリプトで何を書かなければいけないのかは順を追って説明しますので、余裕がある方は Perl の「DBI」というモジュールについて勉強してみるとより一層理解が深まります。
私もこのモジュールの詳細まで学習しているわけではなく、見よう見真似で書いてますので(ノ∀`)

 

この Perl でやること

とは言っても、いきなりスクリプトを読んでも全体がつかみにくいでしょうから、この Perl スクリプトで何をしなければいけないのか簡単に概要をお話します。

 

上図の通り、この Perl はメインメニューを実現する PHP から呼ばれて動作をします。ということは、PHP 側の GATHER による分岐部(PHP の SWITCH 文)で、何らかの形で Perl を呼び出すことになります。

そしてメインメニュー中、複数の箇所から緑の点線が伸びているので、いくつか呼び出し方がある、ことを意味しています。

私のスクリプトでは、Perl を呼び出す際に引数を渡して、それによって処理を変えるようにしました。まさしく GATHER における SWITCH 文と同じです。

 

この Perl でやらせたいことは以下の通りです。

  • トイレの状態を確認する際、データベースを参照する(SELECT する)
  • Armed モードの切り替えをする際、データベースを更新する(UPDATE する)
  • Armed モードのトイレを把握する際、データベースを参照する(SELECT する)

ということで、UPDATE 文と SELECT 文を実行し、結果を元の PHP、つまりメインメニューを実現する PHP に返す、という動作をします。
「メインメニューを実現する PHP」については先の回で一緒に作りますので、まずは上記 3 つの役割を果たす Perl スクリプトを作って、「メインメニューを実現する PHP」に返せるようにすればいいんだなと思って下さい。

データベースにアクセスする Perl スクリプト

では、実際に私が作ったスクリプトを一緒に見て行きましょう。結構長いですので、別ウィンドーのテキストエディタ等にペーストして読むとわかりやすいかもしれません。行番号が出せるテキストエディタだと尚よいです。

 

  • require'/data/scripts/cgi-lib.pl';

この Perl スクリプトは引数なりパラメータなりを受け取って条件分岐をさせたいので、この受取る処理を簡単にするために cgi-lib.pl という外部のスクリプトを利用することにしました。cgi-lib.pl は一般公開されているものです。

Google で調べて入手、このスクリプトをご自身のサーバにアップロードし、そこまでのフルパスを上記のように指定します。

この時、ドキュメントルート(Webから直接アクセスできる領域)には置かないでください。
PHP は Web からアクセスできる領域に配置しますが、この Perl スクリプトと cgi-lib.pl は、直接不正な方法でアクセスされてデータベースに危害が加わる可能性を考慮にいれ、Web から直接アクセスできない場所に配置します。

 

私の場合は、/data/html/ がドキュメントルートですので、/data/scripts というドキュメントルート外にディレクトリを作り、ここに Perl 関連のスクリプトを配置することにしました。

  • use DBI;
  • use Switch;

 

データベースにアクセスするためのモジュールと、SWITCH 文を実現するモジュールを呼び込みます。DBI モジュールはデフォルトで入ってないため、Cent OS の場合は以下のコマンドでインストールしましょう。第 4 回の記事で一緒にインストールした人は不要です。root 権限で実行してください。

# yum install perl-DBI

依存性があるパッケージがあるよというメッセージには Y で回答し、必要なものも合わせてインストールします。

  • &ReadParse(*data);
  • $action=$data{ACTION};
  • $sta_no=$data{STANO};

メインメニューからこの Perl スクリプトを呼び出す時に、ACTION=XXXX、STANO=XXXX というハッシュ型のパラメータを受け取ります。&ReadParse() 関数は cgi-lib.pl に書かれており、ここでハッシュ型のパラメータを data の中に次々と格納しています。この data は好きな名前にしても構いません。
そして $data{ キーの名前 } という形で取り出します。もし data を hoge という名前にしているなら $hoge{ キーの名前 } で取り出すことになります。

 

メインメニューの PHP では、ACTION というキーのパラメータ(処理内容を伝える)と、STANO というキーのパラメータ(トイレ番号を指定する)を使う予定ですので、これらのパラメータをこの Perl 内の変数 $action と $sta_no に代入します。

  • $DB_source='DBI:mysql:db_masa';
  • $DB_user='XXXXX';
  • $DB_pass='XXXXX';
  • $DB_table='twimllet';
  • $MYDB=DBI->connect($DB_source,$DB_user,$DB_pass);

 

DBI モジュールに渡すデータベース関連の情報です。詳しいことは DBI モジュールの使い方をみなさん各自勉強していただくとして、ここでは、おまじないだと思ってこのように指定するんだと考えてください。 
上から
「DBI 使うぜ、mysql だぜ、データベース名は db_masa だぜ」
「MySQL のデータベースログインユーザは XXXXX だぜ」
「MySQL のデータベースログインパスワードは XXXXX だぜ」
「アクセスするテーブル名は twimllet だぜ」
という形で変数に入れていき、実際に $MYDB = DBI->connect(省略)が MySQL をけしかける部分になります。データベース名や XXXXX の部分はみなさんが実際に作成したユーザ名なりパスワードの文字列に読み替えてください。

 

この後続くプログラム部分でこの $MYDB に更に命令をくっつけてデータベースを操作することになります。$MYDB はいわば、そのデータベースアクセス部分の根っこだと思っていればひとまず OK です。

  • sub modify_flag(){ (中略) }

この括りが modify_flag という名前のサブルーチン(ファンクション)になっています。
ざっと説明すると最初に $sta_no が定義されているかを調べ、もし定義されていれば各種 SQL を準備して実行、もし定義されていなければトイレが指定されてないよとエラーを吐くというものです。

 

DBI では
$SQL_select=$MYDB->prepare("SELECT flag from $DB_table where sta_no = $sta_no");
のように、変数の中に SQL を準備し、これをオブジェクトのよう扱います。実行するときは、このオブジェクトに execute という命令を渡します、
$SQL_select->execute();
のように。
データベースから戻ってきた各種結果も $SQL_select に入っていますので、例えば返ってきた結果を取得するなら
$get_sta_no=$SQL_select->fetchrow_array();
のようにします。この場合だと、先ほどの SELECT 文で引っ張ってきた結果が $get_sta_no という変数に代入されます。

 

このファンクションは、メインメニューからトイレ番号を指定し、Armed モードの ON / OFF を行う時に呼ばれます。ユーザがプッシュしたトイレ番号は $sta_no に入っていますので、これを条件に $SQL_count を実行し、件数を数えます。
もしこの値が 0 であれば、その指定したトイレ番号は存在しないので、「そんなトイレないよ」と print で返します。
もし 0 ではなければ、$SQL_select を実行し、その番号の Armed モード を調べます。もし Armed なら $SQL_modify_off を実行して解除し、そうではないなら $SQL_modify_on を実行して Armed モードにします。

そしてそもそも $sta_no が指定されていないなら「トイレ番号が指定されてないよ」と Print します。

  • sub show_online(){ (中略) }

 

この括りが show_online という名前のファンクションです。メインメニューで各トイレの状態を把握する選択をした場合に呼ばれる部分です。データベースアクセス方法は前述のファンクションと同じです。stat が ONLINE か IN USE になっているトイレの一覧を $SQL_select1 で取得、IN USE になっているトイレの一覧を $SQL_select2 で取得 しています。

ちなみに、この SQL 自体がエラーになっていないかを調べる命令が ->err です。この値を調べてもし定義されている場合、このSQLがエラーになったことを示しています。

勉強のためにちょっとこれを調べるスクリプトも作ってみました。このファンクションの中で if($SQL_select1_errcode) { ~ }の部分が該当します。もしエラーであれば「データベースアクセスがエラーだったよ」と print します。

そして、この $SQL_select1 と $SQL_select2 の件数を調べる命令も実行しており、これは ->rows() という部分です。ここにデータベースから返ってきた結果の件数が入っています。結果を全て抜き出す際にこの件数の値が必要です。
この件数だけ for 文で回して「1 行ずつ結果を取る」という行為で結果を全て持ってきます。

それが for ($i = 0; $i < $rowsX ; $i ++) { ~ } の部分です。このファンクションでは2箇所にこの処理を入れています。
$SQL_select1 側では stat が ONLINE であるトイレ番号を羅列して、その後 "now online" という文言を付与して print しています。
$SQL_select2 側では、stat が IN USE のトイレ番号を羅列して、その後 "now in use"という文言を付与して print しています。

 

例えば、各トイレのステータスが以下のような場合、

  • sta_no = 1, stat = ONLINE
  • sta_no = 2, stat = OFFLINE
  • sta_no = 3, stat = IN USE
  • sta_no = 4, stat = ONLINE
  • sta_no = 5, stat = OFFLINE

このファンクションは最終的に「station 1、station 4、now online、and station 3 now in use.」という文言を print で返してきます。

  • sub show_armed(){ (中略) }

 

このファンクションは、メインメニューで Armed になっているトイレを確認する、を選んだ時に呼ばれます。
先ほどのファンクションと同じように flag が armed になっているトイレ番号を引っ張ってきて、for 文により全ての該当番号を羅列した上で 「now in armed and under your control」という文言を付与して print で返しています。もし Armed のトイレがなければ「No stations in armed」を返します。

if($action){

 switch($action){
  case"SUMMARY"{&show_online();}
  case"ARMED"{&show_armed();}
  case"MODIFY"{&modify_flag();}
  else{&show_online();}
 }
}else{
 print"Error, unauthorized access detected.\n";
}

 

実はここからがスクリプトのメイン部分です。これまではファンクションの定義です。
1 行目から 22 行目を処理して、途中の 26 行目から100 行目まではファンクションとして記憶し、104 行目から処理を継続します。順を追って説明しますね。

最初に cgi-lib.pl で取得した $action の値を調べて分岐します。
もし SUMMARY なら show_online ファンクションを実行、
ARMED なら show_armed ファンクションを実行、
MODIFYなら modify_flag ファンクションを実行、
それ以外なら show_online ファンクションを実行します。
これらの $action の値は、もちろん次回以降で作るメインメニューから指定するものです。

 

そして最後の else 文「もし $action が未定義であれば」ですが、そもそもまともにアクセスしていれば、この $action が未定義になることは無いのです。メインメニュー側でこの Perl スクリプトにアクセスする際に必ず何かしらのパラメータが渡るように作りますので。
では未定義になってしまうケースはどんな場合かという話ですが、これこそ不正アクセスなのです。簡単な対策ですが、もしこのパラメータ無しでこのスクリプトが呼ばれるようなことがあろうものなら、確実におかしな呼び出し方なので「不正なアクセスだな!」と威嚇するようにしてみました。

では、この Perl スクリプトを twimllet_db.pl という名前で保存してアップロードしましょう。

20140219-03.jpg冒頭の方で話した通り、ドキュメントルートにおいてはいけません。Web から直接アクセスできないディレクトリに配置します。私の場合は cgi-lib.pl と同じ /data/scripts に配置しました。

 

今回はちょっと長かったですね(汗)
細かいところまで暗記したり追いつく必要は全くないです。この Perl スクリプトが大体何をやるものなのか、なんとなくイメージが掴めてれば OK です。今回で大きな山は越えましたよ!

次回は、いよいよメインメニューの作成に入ります。
といっても、実はもうシーズン 2 第 3 回で半分作ってしまったようなものです。お楽しみに!

 

おまけのひとこと

女子モーグルの上村プロ! 遂に最後のオリンピックで 1 段、ないしは 2 段・3 段とあがってメダルに届いたあああ!! と確信した滑りでしたよ。
言いたいことは山ほどありますけど省略し、本人が一番悔しかったんじゃないかと思います。心中察するに余る。
しかしね、さすが上村プロですよ、スポーツマンシップに溢れたコメントに全俺が泣いた。
もう金メダルでいい。

この記事をシェア


最新記事

すべての記事へ