思いがけないエラー原因

メルアドというのはある個人の IDとしても使用できます 中にはもちろん自分個人のメルアドを作らず、一つのメルアドを共有されている方々もおられますが、この場合どんなプライベートな連絡もその人々で共有されてしまいますので、プライバシーを保つことができません

そんなメルアドの特性からメルアドは IDとして良く使用されます そして、その人が登録されているか否かの判定にはデータベースをメルアドで検索し、見つかった個数が 0であれば登録されていない、という事実を利用します つまりプログラムに書けば以下のようになります

$stmt = $pdo->prepare( "SELECT COUNT(*) FROM `doctor_tbls` WHERE `email` = :email;" );  // (A)
$stmt->bindValue( ":email", $test-email-address, PDO::PARAM_STR );   // $test-email-addressをテストするためにpdoで変数をバインドする
$flag = $stmt->execute();  
// 検索を実行する
if ( !$flag ) {        
// エラーが無いかチェックする
  $infor = $stmt->errorInfo();
  print_r( $infor );
  exit;
}
$already_registered = $stmt->fetchColumn(); 
// COUNT(*)によりそのメルアドがDBに存在するか調べる 既に登録済のメルアドであれば、0ではない
if ( intval($already_registered) === 0 ) { 
// ここからメルアドが登録されているか否かのチェックとなる
}


if ( $already_registered === 0 ) { 
// こうするとこのブロックの中に入らない
}


if ( $already_registered == 0 ) { 
// こうするとこのブロックの中にきちんと入る
}

PHPには同一性判定として ==と ===の2つがあります ===は値のみならずその型もチェックします ですから、===の方が論理的に合っていますので、通常は ===を使用すべきです しかしながらこのように DBより読み込んできたりすると見かけは数字の0であったとしても型が異なるのです ですから、きちんと作動するようにするためには intva()関数により整数型に変換せねばならないのです これに気づくのに相当の時間がかかりました

Excelデータを二次元配列に読み込む

今 成田空港に向かう NEXの中でテストを走らせています もちろん Objectとして Excelの行や列を扱うのも良いのですが、MySQL側はあくまでも二次元配列という概念ですので、今後のために2次元配列に変換します その部分を作成しました

require '../vendor/autoload.php';

use PhpOffice\ PhpSpreadsheet\ Spreadsheet;
use PhpOffice\ PhpSpreadsheet\ Writer\ Xlsx as Writer;
use PhpOffice\ PhpSpreadsheet\ Reader\ Xlsx as Reader;
use PhpOffice\ PhpSpreadsheet\ Cell\ Coordinate;

$reader = new Reader();
//$reader->setInputEncoding('SJIS');
$spreadsheet = $reader->load( 'poster2018.xlsx' );
//$reader->setInputEncoding('SJIS');
$sheet = $spreadsheet->getActiveSheet();

$lines = array();
$max_row = 0;	// 今rowの最大数値
$max_column = 0;	// 	columnの最大値
$nlines = array(); // Columnを数字にした二次元配列

$row_counter = 0;
$column_counter = 0;
foreach($sheet->getRowIterator() as $row) {
  foreach($sheet->getColumnIterator() as $column) {
    $lines[$row_counter][$column_counter] = $sheet->getCell( $column->getColumnIndex() . $row->getRowIndex() )->getValue();
    // これで$linesという2次元配列に Excel dataが集積された
    // これからは $lines[n][m]のような二次元配列として扱うことが可能となる
    $column_counter++;
  }
  $row_counter++;
}
$max_row = $row_counter;
$max_column = $column_counter;

ここでキモとなるのが、Excelではそれぞれのセル座長は A1より始まる表記となっていることです 通常の二次元配列では[0][0]より始まる配列ですので、この変換が必要となります

また Excelファイルを扱う時何時も問題となるのが、文字コードです Excelでは DOSからの伝統で日本語文字コードは Shift-JISであり、現在のプログラミングで標準的な UTF-8とは合いませんし、文字化けが起こります

そこで、PhpSpreadSheetにも

$reader->setInputEncoding('SJIS');

というメソッドが定義されているのですが、最近の Excelで改善されたのか、何しろそのようなメソッドを使用せずに自動的に UTF-8に変換されるようです ですからこのメソッドは不要でした

この10日間 悩み抜いたバグがようやく解決

さて、https://www.kamakuralive.net/ のWeb programを作る段階で、コメコメ部門のポスター発表部分がまだ実装できていませんでした その理由は唯一つ、僕がズルというか楽をしたいからだったのです

既にポスター演題が 20集まっており、それをコメコメ委員の方々が Excel形式で集計してくださっていました しかし、それを Web databaseである MySQLデータベースに読み込まねばなりません 実は既に昨年、 Excel (これはShift-J文字コード) の xlsx形式-> Text形式 (.txt)に変換 -> 文字コードを UTF-8に変換 -> .txtを MySQL databaseに読み込み という部分は作成していました

まあ作成といっても、foreachでループを回して読み込むだけなのですが・・・

今回は折角ですので、自分の勉強のためにもPHPSpreadSheetという Excelファイルを PHPで読み込むライブラリを用いることにしたのです

その部分は案外簡単に Excel file読み込みに成功したのですが、肝腎の MySQL databaseに書き込む SQL文がエラーが出るのです その部分のバグとりが全くうまくいかず 10日間以上を無駄に過ごしてしまいました いや 決して人生において無駄というのは無いでしょう そう思わねばやってらんないよっ

結局その部分を簡略化して出しましょう

try {
  // MySQLサーバへ接続
  $pdo = new PDO( "mysql:host=$db_host;dbname=$db_name_sessions;charset=utf8", $db_user, $db_password );
  // 注意: 不要なspaceを挿入すると' $db_host'のようにみなされ、エラーとなる
} catch ( PDOException $e ) {
  die( $e->getMessage() );
}

for ( $rowNo = 1; $rowNo < 24; $rowNo++ ) {
  $stmt = $pdo->prepare( "SELECT COUNT(*) FROM `doctor_tbls` WHERE `email` = :email;" );
  $stmt->bindValue( ":email", $nnlines[ $rowNo ][ 12 ], PDO::PARAM_STR );
  $stmt->execute();
  $count_same_email = $stmt->fetchColumn();

  if ( $count_same_email < 1 ) { // 既にこのメルアドは登録されているのでスキップ
     $stmt_dr = $pdo->prepare( "INSERT INTO `doctor_tbls` (`english_sirname`, `english_firstname`, `is_male`, `email`, `changed`) VALUES (:enlish_sirname, :enlish_firstname, :is_male, :email, :changed);" ); 
    $stmt_dr->bindValue( ":english_sirname", $nnlines[ $rowNo ][ 1 ], PDO::PARAM_STR );
    $stmt_dr->bindValue( ":english_firstname", $nnlines[ $rowNo ][ 2 ], PDO::PARAM_STR );
    
    if ( $nnlines[ $rowNo ][ 3 ] === 'M' ) {
      $sex = 1;
    } else {
      $sex = 0;
    }
    
    $stmt_dr->bindValue( ":is_male", $sex, PDO::PARAM_INT );
    $stmt_dr->bindValue( ":email", $nnlines[ $rowNo ][ 12 ], PDO::PARAM_STR );
    $stmt_dr->bindValue(":changed", date('Y-m-d H:i:s'), PDO::PARAM_STR);
    
    $flag = $stmt_dr->execute();
    
    if ( !$flag ) {
      echo "<h2 style='color:red;'>Error</h2>";
      $infor = $stmt_dr->errorInfo();
      print_r($infor);
      exit( $infor[ 2 ] );
    }
    
  }
}

これがエラーで書き込めません、色々と試みたのがうまく行きませんので、最後に  print_r($infor)の一行を加えて、SQLエラー出力を行いました

そうすると MySQL error code

SQLSTATE[HY093]

というのが出てくるのです これは検索すると パラメータの数が合わないなのですが、目を更にして見てみてもそんな訳はありません しかし、この 10日間の苦難の末ようやく発見しました

 

VALUES (:enlish_sirname, :enlish_firstname, :is_male, :email, :changed)

この部分で english_ とすべきを enlishとしているのですね このためパラメータの数が合わないため SQL errorとなっていたのです なんだかなあ パカみたい 10日間損した気分 でも自分が招いたものだから仕方ありません

本日はこれから 成田空港に向かいます このエラーを解決したのでポスター・セッション部分のプログラム完成できますね

どうりで

先週末より kamakuralive.net, kamakuraheart.orgなどのサーバーに対する作業がすばやく反映されないとか、うまくいかないことが続きました

そして、先週開催した鎌倉ライブデモンストレーション実行委員会での Webを通じたプログラム更新作業が完全に破綻したのです

どうやらローカルでバグをとったプログラムを updateしたにもかかわらず server上では更新されていないことを掴みました しかも、その過程で明確なエラーが表示されなかったのです

訳が分からず色々なテストプログラムを書いてようやく、その原因の少なくとも1つが、Zenlogic側の FTPがうまく動作しないことによると掴みました そして、それを自ら改善してようやく正常なプログラムを uploadすることができるようになったのです そして本日やはりこんなメールが送られてきました

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 
【Zenlogic】FTP接続障害発生のお知らせ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━201

ご利用いただいておりますZenlogicホスティングにおいて、先日実施いたしま
したFTPサーバー(ProFTPd)のバージョンアップの影響により、FTP接続ができ
ない事象が発生しております。
                記 

■日時: 2018年10月26日(金) 20時00分頃 ~

 ※時間は24時間表記です。
 ※上記日時から10月28日(日) 06時00分までの間で順次、FTPサーバー
  (ProFTPd)のバージョンアップを実施いたしましたので、発生日時は
  ご利用サーバーによって異なります。

■対象: Zenlogicホスティングをご利用のお客様

■原因: FTPサーバー(ProFTPd)のバージョンアップの影響によって
     サーバー上に設置されているシンボリックリンクが認識されない

■発生している事象:
 接続先ディレクトリを「www」および「ssl」に設定している場合、FTP接続
 ができない(接続先ディレクトリが見えない/エラーが発生する)

 ※WEBサイトの表示やPHP等のプログラムの動作に影響はございません。

もちろん上の表記ではずいぶんとはしょりました 本当に困りました

cssを用いて formを横並びで表示するには?

<form></form>は基本的に縦にしか並びません 何というのでしょうか ブロック要素と呼ぶのでしょうか

これを cssを用いて横並びに表示するのはどうすれば良いか、色々と検索したところここにヒットしました ありがたいことです それで早速用いました こんな感じです

<div style="display:inline-flex;">
  <form method="post" action="action1.php" class="form-inline text-left" style="margin-right: 0.3em;">
    <button type="submit" class="btn btn-sm" name="session" value="<?= $row['id'] ?>">Chair追加</button>
    <input type="hidden" name="role_kind" value="1"/>
    <input type="hidden" name="sessionNo" value="<?= _Q($row['sessionNo']); ?>"/>
  </form>
  <form method="post" action="action2.php" class="form-inline text-left">
    <button type="submit" class="btn btn-sm" name="session" value="<?= $row['id'] ?>">Chair追加 (Role数)</button>
    <input type="hidden" name="role_kind" value="1"/>
    <input type="hidden" name="sessionNo" value="<?= _Q($row['sessionNo']); ?>"/>
  </form>
</div>

これによりこんなふうに表示されて、2つのボタンが並びます

formを横並び表示する

ヘルシンキから神戸 そして WSL

WSLという言葉をご存知でしょうか Windows Subsystem for Linuxのことです

Microsoftは数年前より急速に Open Softwareに舵を切り替え、その代表として WSLという仕組みを開始したのです これは Windows10というこれまで Microsoftを支えてきた OSの中に Linuxという Open Softwareの OSをシームレスに走らせる仕組みです

もちろんこれまでに Windows10の上では、VirtualBoxや、VMWareといった有名な仮想マシン、あるいは Dockerなどのコンテナと呼ばれる仮想マシンの小型版がフリーで走っていました しかし、VirtualBoxや VMWareは Windows10の中に新たにコンピューターシステムを立ち上げるもので、マシンに対する負荷が大きいものでした

これに対して WSLというのは Windows10 OSの上に Linuxの APIを emurateする型で Linux OSを立ち上げるものです ここいら辺に関しては僕も知識がいい加減であり間違ったことも言っていると思いますので、それぞれが調べて下さい 何れにしても WSLを立ち上げたのです

その方法はネットで検索すると色々出てきますが、僕の場合、このサイトに従いました 少なくとも 2018/SEP/13の最新 Windows10 version 1803においてはこれで正しくインストールできます

とは言うもののこのサイトの後ろ半分に書かれている内容はかなり高度なものであり難解です とりあえず僕はここまで つまり、<Hyper の設定 for WSL>という部分まで行い結果的にこのような画面となりました

Linux (Ubuntu) on Windows10
Linux (Ubuntu) on Windows10 on MacOS

さて昨日は成田空港から羽田空港に移動し、伊丹空港に飛び、それから神戸の高橋病院に入りました そこで三例のPCIを行わせて頂きました

楽しく過ごしました

こっ こっ こっ これは素晴らしい

javascriptは関数型言語として捉えるとものすごく先進的な言語であるらしいのです もっとも ECMA Script 6 (ES6)あるいは  ECMAScript2015とも呼ばれるもの以降の話のようですが・・・

てなわけで少しだけ勉強しようと思っているのですが、何はともあれ javascript実行環境が無いと練習できません もちろんそのような意味では各種Browserには javascriptが内蔵されているのでそこで consoleを叩けば良いのですが、なかなか面倒ですよね

あるいは $node としてnode.jsを立ち上げその中で実行するのもありですが、これは文ごとでの実行となり今一つですよね

ところが VSCodeでは簡単に実行できることが分かりました もちろん node.jsがインストールされていないと駄目なのでしょうかね なかなか素晴らしいですよ

我ながら役立つこともあるのですね

2014年にこのブログに uploadした Qtでの DIB class に関する C++のコードに対して それから 4年経ってコメントがありました  Thanks というものです これは外国からのコメントでした

正直この頃は Qtで何とか C++により DICOM Viewerを作ろうとしていたのでしょうね いやあ その頃のことは忘れてしまいました その後、僕は急速に Script言語による Web programmingに傾倒して行き C++なんて忘れてしまいました ああ

でも Internetのつながりとは大きいですね

本日は Dockerのお勉強

Dockerというのは今話題の何と言ったらばいいのでしょうかね 自分でそんなに理解していないので表現できないのだけど、とにかくイメージとしては 「自分自身のパソコン上で Linuxを自在に走らせることを可能にする」ものです

例えば、自分の MacOSの上で少しの手間で Linux上で走る Web Serverやら SQL databaseを走らせ、MacOSではその Web Serverやら MySQLと通信してあたかも仮想のインターネットが自分の MacOS上に形成されるのです

自分で Webのプログラム組む時にテストとして 仮想インターネット環境を作らねばならず、この目的では 以前より使用されていた XAMPとか 今も盛んに使用されていて自分も使っている MAMPがあるのですが、そんなことせずに(多分)そのような環境を作ることができるのです しかも、それで動作すれば、そのファイル(イメージ)を持ち込めば世界中どのマシンでも動くらしいのです

うーん自分でも良く分からないので説明できないのですが・・・

これまで何冊かの書籍を Kindleで購入してきたのですが、どうも今ひとつピント来ず、またサンプルが動作しないのでつまづいてきました

今回、「Docker/Kubernetes実践コンテナ開発入門」という書籍を Kindleで購入して少し読み始めました

Docker書籍

この書籍は Just Publishedであり、昨日 download可能となりました それでテストしているのですが、なかなかこの書籍は書かれている内容が Docker初心者には理解し難いものがあり、色々とテストしてようやく最初のサンプルプログラムが走りました

#! main.go -- go言語でサーバーを立ち上げるためのプログラムを書く

package main

import (
  "fmt"
  "log"
  "net/http"
)

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    log.Println("received request")
    fmt.Fprintf(w, "Hello Docker from DOCKER/KUBERNETTES BOOK!!")
  })

  log.Println("Server was started.")
  server := &http.Server{Addr: ":8080"}
  if err := server.ListenAndServe(); err != nil {
    log.Println(err)
  }
}

このGO言語で書かれたプログラムは port番号8080で Web Serverを立ち上げるものです なんだか色々と難しいことが書いてあるけど、やってるのは もともと GO言語のライブラリとして用意されている fmt, log, net/http といったライブラリを読み込んでそのライブラリに既に用意されている命令を呼び込んでいるたけです

そしてこれをコンパイルするために用意されている Dockerfile という拡張子無いファイルが重要なのです

FROM golang:1.9

RUN mkdir /echo
COPY main.go /echo

CMD ["go", "run", "/echo/main.go"]

このファイルを同じフォルダに Dockerfile という拡張子の無いファイル名で置くのです

このファイルでは書いてある通りに、 goloang version 1.9という既に定義されている色々な仕掛けを読み込み、それから Dockerが作成した特殊環境 Conatinerの中に /echo というフォルダを作成し、それからそのフォルダにさっき、compileした main.goという実行形式をコピーし、それから go して run してサーバーを立ち上げろ ということなのです

しかし、ここまで理解するのに色々とハマりました まず第一に、これらのファイルをどこに配置すれば良いのか、その説明が本には無いのです

仕方なく、僕は 文書フォルダの中に DOCKERKUBERNETES-BOOKというフォルダを作成し、そこに2つのファイル Dockerfileと main.goを置きました そして

$cd Documents/DOCKERKUBERNETES-BOOK

として current folderをこのフォルダに移動させそこで terminalより 以下の Docker commandsを打ちました

$docker image build -t example/echo:latest . // 実は最後のピリオドが重要です これが current directoryを表しているのですよ

これにより コンテナの中で go言語で書かれたサーバー立ち上げプログラムがコンパイルされてサーバーが立ち上がりました しかし、一発で立ち上がればいいのですが、main.goの中で typo (打ち間違い)が当然あります その時にeditorで何回もさっきの image buildを行っても エラーが出るのです

何回見直して typoを修正しきってもエラーを吐くのです これにはまいりました インターネットで原因を調べても良く分かりません

ふと思いついて ひょっとしてコンパイルされたイメージがコンテナの中に既に存在するのでエラーになるのでは? と思いました あたりでしたね

 

$docker image build --no-cache -t exmaple/echo:latest .

とすれば再びビルドして新たなイメージを作り直します

またこのようにしてできたイメージは

$docker container run example/echo:test

として簡単にスタートできますね そしてこのWeb Serverに外部から port 9000でアクセスさせるためには まず走っている イメージ-サーバーを停止せねばなりません

$docker ps

により走っているイメージプロセスを表示させます その最後に nameというタグがありますので、そのネームを用いて

$docker stop [nameの部分に表示される名前] //この名前は dockerが不規則に作成しているものです

としてそのプロセスをまず停止します それから再度 port forwardingしながら WEb Serverを立ち上げるために 再度以下のコマンドによりサーバーを立ち上げます

$docker container run -d -p 9000:8080 example/echo:latest

こうすることにより dockerで走っているサーバーの 8080 portがメインマシン ここでは僕の macOSですがこの port 9000と繋がります( = port forwarding) 結果的にはこの macOSでブラウザから localhost:9000とすれば、メッセージが表示されるのです

フーッ ここまで来るのに一時間使いましたね

全く馬鹿なことをしてきた

この暑さに参っているのか頭脳の回転が遅くなっている 自分でもなんて馬鹿なんだと思うほど頭脳の回転が遅くなっている 知らない内に脳梗塞でも起こしているのだろうか?

自分の年齢から考えれば、さらには CHADS-VASCなども考慮すればその可能性は妥当である でも「自分だけはそんなことないよね」などとのほほんと構えている 自分がそんな人間だから患者さんの気持ちは痛いほど分かる そしてその気持が分かれば分かる程に患者さんのことを思う気持ちが高まっていく

とは言っても自分の頭の回転が悪いのは自分で許せない、何とか回転を早くせねばならない そんな時に立ち向かうのはやはりプログラミング でも自分はプロのプログラマではないので、アマチュアのプログラマなので、自称「アマグラマ」と呼んでいる

さてそのアマグラマが何とか少し頭の回転を取り戻したのが次の Web上での MySQLデータ修正の php program このデータというのは 実は 鎌倉ライブのweb公開用予定表 でも現在公開しているものとは不完全に修正書き換えしてあり、昨年の予定表も混在しているので混乱の的かも? まあしかし鎌倉ライブなんてそんなに世間の注目集めないのでこのまま混在していても開催までに修正すれば実害は無いのだろうと思って勝手に割り切っている

そもそも今年は鎌倉ライブ予定表のデータ構造を徐々に変更しようとしているのだ 何故ならばもっと論理的な整合性のあるデータ構造にすれば、その先修正はいらなくなるだろう と期待しているから

とりあえず、テーブルのカラムを一つ増やし、そのカラムに別のカラム上のデータを変換しながらコピーするプログラムを phpで書いたのである ばっかみたいなプログラムだけと暫くプログラム書くことから遠ざかっていたので書いている内にプログラミングの感覚が戻ってきてなんとなく自分が賢くなってきたように思うのだ

ということでそのプログラムの一部

$stmt = $pdo->prepare( "SELECT * FROM `session_tbls2018` WHERE '1' = '1';" );

try {
  $pdo->beginTransaction();
  $flag = $stmt->execute();
  if ( !$flag ) {
    $infor = $stmt->errorInfo();
    exit( $infor[ 2 ] );
  }

  $pdo->commit();
} catch ( Exception $e ) {
  $pdo->rollBack();
  echo "Failed to update Database" . $e->getMessage();
}
$rows = $stmt->fetchAll( PDO::FETCH_ASSOC );
$stmt1 = $pdo->prepare("UPDATE `session_tbls2018` SET `beginDate` = :beginDate WHERE `id` = :id;");

foreach ( $rows as $row ) { // 全てのデータ
  if ( ( $row[ 'id' ] > '171' )AND( $row[ 'id' ] < '186' ) ) {	//2017年 座学修正用
    $begintime = date( 'Y/m/d H:i:s', strtotime( '2017/12/09' . $row[ 'begin' ] ) );
    $stmt1->bindValue( ":beginDate", $begintime, PDO::PARAM_STR );
    $stmt1->bindValue( ":id", $row[ 'id' ], PDO::PARAM_INT );
    $stmt1->execute();
    try {
      $pdo->beginTransaction();
      $flag = $stmt1->execute();
      if ( !$flag ) {
        $infor = $stmt1->errorInfo();
        exit( $infor[ 2 ] );
      }

      $pdo->commit();
    } catch ( Exception $e ) {
      $pdo->rollBack();
      echo "Failed to update Database" . $e->getMessage();
    }
    echo $begintime . "<br>";
  }
  if ($row['id'] == 185) echo "座学が修正されました<br><br>";