まず、修正方法は非常に簡単です。IPv4アドレスとIPv6アドレスの両方を保存する場合は、VARBINARY(16)
を使用する必要があります。 BINARY(16)
の代わりに 。
ここで問題が発生します。BINARY(16)
で期待どおりに機能しないのはなぜですか。 ?
テーブルips
があるとします。 1列のみip BINARY(16) PRIMARY KEY
。デフォルトのローカルIPv4アドレスを
$stmt = $db->prepare("INSERT INTO ips(ip) VALUES(?)");
$stmt->execute([inet_pton('127.0.0.1')]);
データベースで次の値を見つけます。
0x7F000001000000000000000000000000
ご覧のとおり、これは4バイトのバイナリ値です(0x7F000001
)16バイトの固定長列に合うようにゼロが右に埋め込まれます。
あなたが今それを見つけようとするとき
$stmt = $db->prepare("SELECT * FROM ips WHERE ip = ?");
$stmt->execute([inet_pton('127.0.0.1')]);
次のようになります。PHPは値0x7F000001
を送信します 保存された値0x7F000001000000000000000000000000
と比較されるパラメータとして ただし、長さが異なる2つのバイナリ値が等しくなることはないため、WHERE条件は常にFALSEを返します。
SELECT 0x00 = 0x0000
0
を返します (FALSE)。
注:固定長の非バイナリ文字列(CHAR(N)
では、動作が異なります。 。
回避策として明示的なキャストを使用できます:
$stmt = $db->prepare("SELECT * FROM ips WHERE ip = CAST(? as BINARY(16))");
$stmt->execute([inet_pton('127.0.0.1')]);
そしてそれは行を見つけます。しかし、私たちが得たものを見ると
var_dump(inet_ntop($stmt->fetch(PDO::FETCH_OBJ)->ip));
表示されます
string(8) "7f00:1::"
しかし、それは(実際には)私たちが保存しようとしたものではありません。そして今、7f00:1::
を保存しようとすると 、重複キーエラーが発生します 、IPv6アドレスはまだ保存していませんが。
もう一度:VARBINARY(16)
を使用します 、コードをそのままにしておくことができます。多くのIPv4アドレスを保存すると、ストレージを節約することもできます。