Pertama perbaikannya, yang cukup sederhana:Jika Anda ingin menyimpan keduanya, alamat IPv4 dan IPv6, Anda harus menggunakan VARBINARY(16)
bukannya BINARY(16)
.
Sekarang masalahnya:Mengapa tidak berfungsi seperti yang diharapkan dengan BINARY(16)
?
Anggap kita memiliki tabel ips
dengan hanya satu kolom ip BINARY(16) PRIMARY KEY
.Kami menyimpan alamat IPv4 lokal default dengan
$stmt = $db->prepare("INSERT INTO ips(ip) VALUES(?)");
$stmt->execute([inet_pton('127.0.0.1')]);
dan temukan nilai berikut dalam database:
0x7F000001000000000000000000000000
Seperti yang Anda lihat - Ini adalah nilai biner 4 byte (0x7F000001
) diisi dengan nol agar sesuai dengan kolom panjang tetap 16 byte.
Saat Anda sekarang mencoba menemukannya dengan
$stmt = $db->prepare("SELECT * FROM ips WHERE ip = ?");
$stmt->execute([inet_pton('127.0.0.1')]);
hal berikut terjadi:PHP mengirimkan nilai 0x7F000001
sebagai parameter yang kemudian dibandingkan dengan nilai yang disimpan 0x7F000001000000000000000000000000
.Tetapi karena dua nilai biner dengan panjang yang berbeda tidak pernah sama, kondisi WHERE akan selalu mengembalikan FALSE.Anda dapat mencobanya dengan
SELECT 0x00 = 0x0000
yang akan mengembalikan 0
(SALAH).
Catatan:Perilakunya berbeda untuk string non biner dengan panjang tetap (CHAR(N)
).
Kita bisa menggunakan casting eksplisit sebagai solusi:
$stmt = $db->prepare("SELECT * FROM ips WHERE ip = CAST(? as BINARY(16))");
$stmt->execute([inet_pton('127.0.0.1')]);
dan itu akan menemukan baris. Tetapi jika kita melihat apa yang kita dapatkan
var_dump(inet_ntop($stmt->fetch(PDO::FETCH_OBJ)->ip));
kita lihat saja
string(8) "7f00:1::"
Tapi bukan itu (sebenarnya) yang kami coba simpan. Dan saat kami mencoba menyimpan 7f00:1::
, kita akan mendapatkan kesalahan kunci duplikat , meskipun kami belum pernah menyimpan alamat IPv6.
Jadi sekali lagi:Gunakan VARBINARY(16)
, dan Anda dapat menyimpan kode Anda tidak tersentuh. Anda bahkan akan menghemat beberapa penyimpanan, jika Anda menyimpan banyak alamat IPv4.