views:

90

answers:

2

Hello!

I have a database table which contains an unsigned integer field to store the visitor's IP address:

`user_ip` INT(10) UNSIGNED DEFAULT NULL,

Here's the snippet of PHP code which tries to store the IP address:

$ipaddr = $_SERVER['REMOTE_ADDR'];
if ($stmt = mysqli_prepare($dbconn, 'INSERT INTO visitors(user_email, user_ip) VALUES (?,?)'))
{
    $remote_ip = "INET_ATON('$ipaddr')";
    mysqli_stmt_bind_param($stmt, 'ss', $email, $remote_ip);
    if (mysqli_stmt_execute($stmt) === FALSE) return FALSE;
    $rows_affected = mysqli_stmt_affected_rows($stmt);
    mysqli_stmt_close($stmt);
}

The INSERT operation succeeds, however the user_ip field contains a null value. I have also tried changing the parameter type in mysqli_stmt_bind_param() (which was set to string in the above example) to integer, i.e. mysqli_bind_param(... 'si',...) - but to no avail.

I've also tried using the following bit of code instead of mysql's INET_ATON() SQL function:

function IP_ATON($ipaddr)
{
    $trio = intval(substr($ipaddr,0,3));
    return ($trio>127) ? ((ip2long($ipaddr) & 0x7FFFFFFF) + 0x80000000) : ip2long($ipaddr);
}

It still doesn't work - the 'user_ip' field is still set to null. I've tried passing the $ip_addr variable as both integer & string in mysqli_bind_param() - to no avail.

It seems the problem lies with the parameterized insert.

The following "old-style" code works without any problem:

mysqli_query(..., "INSERT INTO visitors(user_email, user_ip) VALUES ('$email',INET_ATON('$ipaddr'))");

What am I doing wrong here?

Thanks in advance!

A: 

I think it should look like this:

$ipaddr = $_SERVER['REMOTE_ADDR'];
if ($stmt = mysqli_prepare($dbconn, 'INSERT INTO visitors(user_email, user_ip) VALUES (?, INET_ATON(?))'))
{
    mysqli_stmt_bind_param($stmt, 'ss', $email, $ipaddr);
    if (mysqli_stmt_execute($stmt) === FALSE) return FALSE;
    $rows_affected = mysqli_stmt_affected_rows($stmt);
    mysqli_stmt_close($stmt);
}

Note the change by the second ? and what parameters are sent into mysqli_stmt_bind_param.

Since the parameters you pass into mysqli_stmt_bind_param should be the actual values to be inserted, and not any functions.

becquerel
Wow! That was fast!Thanks! It now works!
invarbrass
Glad I could help :) Some of the point of binding the paramaters like that is to make them safe to insert in the database, to make sure that no values are accidently parsed as for example a MySQL function and to escape characters correctly.
becquerel
A: 

Bound SQL query parameters can be used only for a value, not an expression or a function call. So when you do this:

$remote_ip = "INET_ATON('$ipaddr')";

It binds the string "INET_ATON('value of $ipaddr')", not the result of a SQL function by that name. When a string value is inserted to an integer column, MySQL takes the numeric value of any leading digits, and in this case there are no leading digits. So the value 0 is inserted.

I would recommend you use PHP's built-in function ip2long().

$remote_ip = ip2long($ipaddr);
mysqli_stmt_bind_param($stmt, 'ss', $email, $remote_ip);

Although $remote_ip is now an integer, I've read that mysqli has trouble binding unsigned integers, so you should just use 's' as the type when you bind.


Re your comment: You're right, because PHP doesn't support unsigned integers, ip2long() will return a negative number if your IP address is 128.0.0.0 or greater. You can convert to a string representation of the unsigned integer with sprintf():

<?php

$addr = '192.168.1.1';

$ip = ip2long($addr);
var_dump($ip);
// int(-1062731519)

$ip = sprintf("%u", ip2long($addr));
var_dump($ip);
// string(10) "3232235777"

var_dump(long2ip($ip));
// string(11) "192.168.1.1"
Bill Karwin
Thanks! Indeed, becquerel's solution was correct.I've actually tried using ip2long(). But sometimes it returen signed integers - which don't work well with mysql (*the recommended field type to store IP addresses is INT(10) UNSIGNED*)I've posted the work-around in my original post (see the IP_ATON() function)
invarbrass