PHPによるWebアプリケーションの開発
前節までにMySQLとnginxとPHP関連モジュールが稼働するコンテナイメージができたので、次はPHPアプリケーションが稼働するコンテナイメージを作成します。最終的にIBM Containers上で稼働させるコンテナです。
ホストOS上で適当なディレクトリを作成します。
$ mkdir ~/iotkaji_docker2
作成したディレクトリにDockerfileとリリース用のソースファイルを置きます。
先ほどのsusumutani3/iotkaji:v3をベースにしますが、稼働確認のために使用したtest.phpはサーバー情報が見えてしまい望ましくありません。IBM Containers上で稼働させるコンテナイメージからは削除します。
まずDockerfileは下記のとおりです。
# Dockerfile for IoTかじってみよう PHPアプリケーション FROM susumutani3/iotkaji:v3 MAINTAINER Susumu Taniguchi # test.phpを削除します。 RUN rm /usr/share/nginx/html/test.php ADD writedb.php /usr/share/nginx/html/writedb.php ADD readdb.php /usr/share/nginx/html/readdb.php EXPOSE 80 CMD ["/usr/local/bin/init.sh"]
PHPアプリケーションのwritedb.phpとreaddb.phpもいっしょに置きます。
冒頭で述べたように今回のレシピでは、mbedからHTTPのPOSTリクエストでデバイスID(deviceid)、温度(temperature)、メモ(memo)の3項目を受信して、既に作成したデータベースのテーブルへINSERTするためのPHPのアプリケーションであるwritedb.phpと、同じデータベースからデータを読み込んで表示するためのPHPのアプリケーションであるreaddb.phpを作成します。それぞれソースコードを下記に記載します。
ちょっと長いように見えますが、PHPのおさらいとして主要な部分の内容を確認していきます。
<?phpから?>までの間にはさまれた部分がPHPアプリケーションです。
- (1)PHPでは、POSTリクエストで送信されたデータを$_POST['名称']で参照します。その際に、htmlspecialchars()関数を使用して特殊文字をHTMLエンティティへ変換しておくことで、後続の処理で特殊文字による不具合が発生することを防ぎます。
- (2)MySQLの拡張モジュールであるmysqliクラスを使用してMySQLサーバーへ接続します。前節までで作成したデータベースとユーザーを使用します。
- (3)MySQLサーバーへの接続で失敗した場合は終了します。
- (4)テーブルへデータをINSERTするSQLは、mysqli::prepare()メソッドを使用してプリペアードステートメントとして用意しておきます。このとき変数の部分にはプレースホルダ「?」を使います。
- (5)mysqli::bind_param()メソッドを使用して、プリペアードステートメントのSQLとhtmlspecialchars()で処理済みの変数をバインドします。
- (6)続いてmysqli::execute()メソッドでSQLを実行します。
- (7)最後にデータベースとの接続をクローズします。
なお、//はコメントアウトです。このほかにもシェル型の#やC型の/* */のコメントアウトが使用できます。
<?php
// (1)POSTされたデータ$_POST['名称']の文字列中の特殊文字を、HTMLエンティティに変換します。
$deviceID = htmlspecialchars($_POST['deviceid'],ENT_QUOTES); // deviceidとして送られたデータを取り込みます。
$temperature = htmlspecialchars($_POST['temperature'],ENT_QUOTES); // temperatureとして送られたデータを取り込みます。
$memo= htmlspecialchars($_POST['memo'],ENT_QUOTES); // memoとして送られたデータを取り込みます。
if(!is_numeric($temperature)) $temperature=0.0; // $temperatureが数字であることの確認、違う場合は0.0にします。
try{
// (2)mysqliクラスを使用してMySQLサーバーへ接続します。
$conn = new mysqli('localhost','user_iot','pass_iot','SampleIotDB'); // 前節までで作成したデータベースとユーザーを使用します。
// (3)MySQLサーバーへの接続で失敗した場合の処理を記載します。
if($conn->connect_errno){
echo 'connection failure';
die('error message: ' . $conn->connect_errno);
}
// (4)テーブルへデータをINSERTするSQLをプリペアードステートメントとして用意します。
// 「?」の部分はプレースホルダです。
if(!($stmt = $conn->prepare("INSERT INTO sample1_tbl(deviceid,temperature,memo) VALUES(?,?,?)"))){
echo 'sqlstatement prepare failure';
printf("error message: %s<br>",$mysqli->errno);
}
// (5)プリペアードステートメントのSQLに変数をバインドします。
if(!$stmt->bind_param("sds",$deviceID,$temperature,$memo)){ // "sds"は、変数がstring,double,stringであることを意味します。
echo 'SQL bind failure';
printf("error message: %s<br>",$mysqli->errno);
}
// (6)SQLを実行します。
if(!$stmt->execute()){
echo 'SQL execute failure';
printf("error message: %s<br>",$mysqli->errno);
}
}finally{
// (7)MySQLサーバーへの接続を終了します。
$conn->close();
}
?>
データベースからデータを読み出して表示するreaddb.phpは次の通りです。
- (1)writedb.phpと同様にMySQLの拡張モジュールであるmysqliクラスを使用してMySQLサーバーへ接続します。
- (2)MySQLサーバーへの接続で失敗した場合は終了します。
- (3)テーブルへデータをSELECTするSQLを用意します。
- (4)SQLを実行します。クエリの結果をmysqli_resultクラスの変数$resultにセットします。
- (5)mysqli_resultクラスのmysqli_fetch_assocメソッドで、クエリの結果の行を連想配列で取得して表示します。
- (6)最後にデータベースとの接続をクローズします。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>IoTをかじってみよう3章readdb</title>
</head>
<body>
<?php
try{
// (1)mysqliクラスを使用してMySQLサーバーへ接続します。
$conn = new mysqli('localhost','user_iot','pass_iot','SampleIotDB'); // 前節までで作成したデータベースとユーザーを使用します。
// (2)MySQLサーバーへの接続で失敗した場合の処理を記載します。
if($conn->connect_errno){
echo 'connection failure';
die('error message: ' . $conn->connect_errno);
}
// (3)テーブルへデータをSELECTするSQLを用意します。
$sql1 = 'SELECT deviceid,temperature,memo,datetime FROM sample1_tbl';
// (4)SQLを実行します。
$result = $conn->query($sql1);
printf("result is<br>\n");
// (5)クエリの結果の行を連想配列で取得して表示します。
while( $row = $result->fetch_assoc() ){
printf("%s %f %s %s<br>\n", $row['deviceid'],$row['temperature'],$row['memo'],$row['datetime']);
}
}finally{
// (6)MySQLサーバーへの接続を終了します。
$conn->close();
}
?>
</body>
</html>
作成したDockerfileを使用して起動シェルの含まれたイメージを作成します。
$ sudo docker build -t susumutani3/iotkaji:v4 ~/iotkaji_docker2
イメージが作成できていることを確認します。
$ sudo docker images
作成したイメージのコンテナを実行します。
$ sudo docker run --name testing -it -p 80:80 susumutani3/iotkaji:v4
ここまでできたらサーバー側は完了ですが、念のためテストしておきましょう。
curlの準備
今後のデバッグのために、ここでホストOS側にcurlというツールを導入しておきます。これは、URLを指定するとHTTPやHTTPSなどのプロトコルでデータを転送してくれるツールです。後々mbedからのPOST送信した結果が正しいかどうかを調べる可能性があるため用意します。
$ sudo apt-get install curl
それではホストOS側からcurlコマンドでデータをPOSTしてみます。複数回実行してかまいません。
$ curl --data "deviceid=MBED1111&temperature=12.345&memo=test" http://localhost/writedb.php
このコマンドでwritedb.phpにPOSTできます。先ほどと同じくホストOS側のブラウザでhttp://localhost/readdb.phpにアクセスしても確認できますが、w3mでも次のコマンドで確認できます。
$ w3m http://localhost/readdb.php
PHPアプリケーションの機能の確認ができました。
