XAMPPを用いた動作環境でPHP言語によるMySQLへのアクセスと実行動作のログ機能を実装します。すでに「環境データ保存用データベースの作成」でMySQLへのアクセスを実装していますが、この処理を見直してログ機能を追加します。

データベースの準備

MySQLのデータ管理アプリ「phpMyAdmin」を使用して、データベースにテーブルを作成します。phpMyadminのSQLの画面を使用して、SQL「show create table power;」を入力すると作成されているテーブルの構造が表示されます。

また、次のSQL文をphpMyadminのSQLの画面で実行して、テーブルを作成します。

CREATE TABLE IF NOT EXISTS `power` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `datetime` datetime NOT NULL,
  `value1` float(11) NOT NULL ,
   ・・・
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;

PHPログ機能について

PHPログ機能は、別ファイルに関数化し、require関数により呼び出します。ディレクトリ「log」にログを保存し、ログサイス「 1024 * 1024」で3世代管理します。SetLogFile関数によりログファイル名を設定し、WriteLog関数のパラメータで保存するログを指定します。

logoutput.php

<?php
const LOG_DIRECTORY = './log/';           // ログディレクトリ
const MAX_LOTATES = 3;                      // ログファイルを残す世代数
const MAX_LOGSIZE = 1024 * 1024;      // 1ファイルの最大ログサイズ(バイト)
$LOG_FILEPATH = "";

function SetLogFile($filename) {
  global $LOG_FILEPATH;
  $LOG_FILEPATH = LOG_DIRECTORY . $filename;
}

function WriteLog($strlog) {
  global $LOG_FILEPATH;

// 保存先ディレクトリを作成
  if (!file_exists(LOG_DIRECTORY)) {
    mkdir(LOG_DIRECTORY);
  }

// ログのローテート
  if (file_exists($LOG_FILEPATH)) {
    if (filesize($LOG_FILEPATH) > MAX_LOGSIZE) {

      // 最古のログを削除
      @unlink(LOG_FILEPATH . strval(MAX_LOTATES));

      // ログをリネーム .log → .log_01
      for ($i = MAX_LOTATES - 1; $i >= 0; $i--) {
        $bufilename = ($i == 0) ? $LOG_FILEPATH : $LOG_FILEPATH . strval($i);
        rename($bufilename, $LOG_FILEPATH . strval($i + 1));
      }
    }
  }

// ログ出力 
  $strlog = mb_convert_encoding($strlog, "UTF-8", "SJIS");
  file_put_contents($LOG_FILEPATH, date('y-m/d-H:i:s ') . $strlog . "\n", FILE_APPEND | LOCK_EX);
}

データベース書込みスクリプトの作成

データベース書込みスクリプト「wdatabase.php」を次に示します。

  • 2行目でログスクリプト「logoutput.php」をrequireで読み込み、8行目でSetLogFile関数を使ってログファイル名「dbwrite.log」を設定します。14行目でWriteLog関数を使って取得したヘッダの内容をログ保存します。
  • 18行目でデータベースのアクセスを設定し、20行目で例外の発生を許可します。「’SET NAMES utf8’」は日本語のテーブル項目のアクセスを可能とします。データベースの例外は39行目から記述される「PDOException」でキャッチされます。
  • 53行目のgetfloat関数は浮動小数点型文字列のエラーチェックを行う関数で、空文字もしくは浮動小数点型の文字列化をチェックし、エラーの場合は例外を発生します。
  • 64行目のgettime関数はdatetime型文字列のエラーチェックを行う関数で、空文字もしくはdatetime型の文字列化をチェックし、エラーの場合は例外を発生します。

wdatabase.php

<?php
require("logoutput.php");

$DB_DSN = "mysql:host=localhost; dbname=xxxx";
$DB_USER = "xxx";
$DB_PASSWD = "xxx";

SetLogFile("dbwrite.log");
try {
  $headers = getallheaders();
  $post_body = file_get_contents('php://input');

  foreach ($headers as $name => $value) {
    WriteLog("$name: $value");
  }
  WriteLog("$post_body");

  $pdo = new PDO($DB_DSN, $DB_USER, $DB_PASSWD,
          array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL作成
  $csvdata = explode(',', $post_body);

  $datetime = gettime($csvdata[0]);
  $value1 = getfloat($csvdata[1]);

  $sql = "INSERT INTO xxx (
      datetime,value1
    ) VALUES (
      '$datetime',$value1
    )";
  // SQL実行
  $res = $pdo->query($sql);

  header("HTTP/1.1 200 OK");
  header("Status: 200");
  header("Content-Type: text/plain");
  echo "OK";
} catch (PDOException $e) {
  WriteLog($e->getMessage());
  header("HTTP/1.1 500 NG");
  header("Status: 500");
  header("Content-Type: text/plain");
  echo "Internal Server Error";
} catch (Exception $e) {
  WriteLog($e->getMessage());
  header("HTTP/1.1 501 NG");
  header("Status: 501");
  header("Content-Type: text/plain");
  echo "Internal Server Error";
}

function getfloat($floatdata) {
  if (empty($floatdata)) {
    throw new Exception("getfloat1 null data");
  }
  if (!is_numeric(trim($floatdata))) {
    throw new Exception("getfloat2 " . $floatdata);
  }
  $value = floatval(trim($floatdata));
  return $value;
}

function gettime($timedata) {
  if (empty($timedata)) {
    throw new Exception("gettime1 null data");
  }
  if ($timedata === date('Y/m/d H:i', strtotime($timedata))) {
    return trim($timedata);
  } else {
    throw new Exception("gettime2 " . $timedata . ':' . date('Y/m/d H:i', strtotime($timedata)));
  }
}

データベース書込みスクリプトの実行

作成したサーバスクリプトの動作を確認するために、ブラウザからPOSTメソッドを送信できるChrome拡張「Advanced REST client」を使用します。インストールは、「Advanced REST client」に移動し、「CHROMEに追加」をクリックすると、アプリの一覧に「Advanced REST client」が追加されます。「Advanced REST client」をクリックして、次のように設定してHTTPリクエストのPOSTデータでテキストデータを送信します。

次のログが「dbwrite.log」に保存されます。。

20-01/02-21:09:15 HOST: 192.168.10.6
20-01/02-21:09:15 content-type: text/plain
20-01/02-21:09:15 content-length: 24
20-01/02-21:09:15 2019/01/06 02:40,11.23

データベース読込みスクリプトの作成

データベース読込みスクリプト「rdatabase.php」を次に示します。

  • 2行目でログスクリプト「logoutput.php」をrequireで読み込み、8行目でSetLogFile関数を使ってログファイル名「dbread.log」を設定します。21行目でWriteLog関数を使ってMySQLから取得したデータをログ保存します。
  • 118行目でデータベースのアクセスを設定し、13行目で例外の発生を許可します。「’SET NAMES utf8’」は日本語のテーブル項目のアクセスを可能とします。データベースの例外は23行目から記述される「PDOException」でキャッチされます。

rdatabase.php

<?php
require("logoutput.php");

$DB_DSN = "mysql:host=localhost; dbname=xxx";
$DB_USER = "xxx";
$DB_PASSWD = "xxx";

SetLogFile("dbread.log");
WriteLog("start");
try {
  $pdo = new PDO($DB_DSN, $DB_USER, $DB_PASSWD,
          array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  $sql = "SELECT datetime, value1 FROM xxx";
  $stmt = $pdo->query($sql);

  while ($row = $stmt->fetch()) {
    $datetime = $row['datetime'];
    $value1 = $row['value1'];
    WriteLog($datetime . ',' . $value1);
  }
} catch (PDOException $e) {
  WriteLog($e->getMessage());
} catch (Exception $e) {
  WriteLog($e->getMessage());
}

データベース読込みスクリプトの実行

作成したデータベース読込みスクリプトのURL「http://192.168.10.6/datadisplay/rdatabase.php」にアクセスすると、次のログが「dbread.log」に保存されます。

20-01/02-21:12:51 start
20-01/02-21:12:51 2019-01-06 02:40:00,11.23