bbPress をローカルの Windows 8 上で動かしている場合、bbAttachments で画像を投稿した時に上記のように、画像が出てこなくなった。
デバックした結果、環境依存で、IPv6のアドレスから投稿していると認識された時にこのエラーが起きる事がわかった。投稿者のループバックアドレスの「::1」が見えている。
結構、苦労したので作業をメモ。
エラーの原因
エラーの原因は、以下の inet_aton という名前の関数の部分がエラーになったせいで、以下の INSERT 文が上手く動かないのが原因だった。
Debug は、あちこちに Debug 文を入れて、正常に動いてないこの SQL を突き止めた後、phpMyAdmin の管理画面から、実際に失敗している SQL 文を入力して SQL文のどこがエラーになっているか確認した。
$failed=$bbdb->get_var("
INSERT INTO ".$bb_attachments['db']." ( time , post_id , user_id, user_ip, status , size , ext , mime , filename )
VALUES ('$time', '$post_id' , '$user_id' , inet_aton('$user_ip') , $status, '$size', '".addslashes($ext)."', '$mime', '".addslashes($filename)."')
");
IPを DBに格納する際のお作法
MySQL では、IP Address を VARCHAR で、文字列でDBに格納するとスペースが持ったいないので、INT型に変更して格納する事ができるようだ。
MySQL では、inet_aton もしくは inet_ntoa という関数が用意されている。この関数が今回のエラーポイントだった。関数仕様については以下のサイトが詳しかった。
ATON || NOTA ? Ip address IPV4 ? IPV6 ?
※inet_aton( ) の aton は、Address to Numeric Numberの略のようだ(12.16. Miscellaneous Functions)。
IP address を渡すと、人間では読めない数字列に変換される。人間が読める IP Address に戻すには、inet_ntoa( ) を使う。この関数は MySQL 5.6.3 から追加されているとある(INET6_ATON(expr))。残念ながら MySQL をアップグレードできないので、inet_ntoa の使用は断念。INT型での格納は諦めてVARCHAR で格納する事にする。
PHPで IPv6 を String 変換する
MySQLが古くSQL文の中で変換(inet_ntoa) できないので、PHP側で変換してからSQL文に代入する事にする。
PHPでは、inet_pton という関数が使えるようだ。IPv4 と IPv6 の両方に対応だが、戻り値はバイナリ形式になる。
string inet_pton ( string
$address
)この関数は、人間が読める形式の IPv4 あるいは IPv6 (PHP が IPv6 サポートを有効にしてビルドされている場合) のアドレスを 32 ビットあるいは 128 ビットのバイナリ形式に変換します。
bb-attachments-init.php を変更する。
まず変換したバイナリの IP アドレスを格納するために bb_attachments テーブルに 「user_ip_bin (varchar (16))」を追加した。
IPv6の場合は、128bit バイナリという事で、8で割り算して 16Byte あれば足りるかな・・という事で、VARCHAR(16)にしてみた。
コードは bb-attachments-init.php の INSERT文周辺をざっくりと消して以下のように書き換えた。
ただし、テストはループバックアドレス(::1 だけしかテストしていないので、それ以外だと動かないかも。
// IP`v4 の場合と IPv6の場合で、処理を分割
if (strpos($user_ip, '.') !== FALSE) { # ipv4
$int_ipv4=ip2long($user_ip); // user_id 列が integer で定義されている。
$user_binip=inet_pton($user_ip);
}elseif(strpos($user_ip, ':') !== FALSE) { # ipv6
$int_ipv4="0"; // dummy numeric because of "user_ip" column is defined as "not NULL".
$bin_ip=inet_pton($user_ip); // IPv6 is converted to 128 bit binary.
}// データのINSERT
$sql="INSERT INTO ".$bb_attachments['db']." ( time , post_id , user_id, user_ip, user_ip_bin, status , size , ext , mime , filename )
VALUES ('$time', '$post_id' , '$user_id' , $int_ipv4, '$bin_ip', $status, '$size', '".addslashes($ext)."', '$mime', '".addslashes($filename)."')";
$failed=$bbdb->get_var($sql);