2

Description: I am creating an IP class that first finds the visitor's IP address (using $_SERVER['REMOTE_ADDR'] or getenv('REMOTE_ADDR')), makes sure it's a valid IP, set's the version (IPv4 or IPv6) to a data member, then it either returns the IP address, or returns the SQL parameter if it's for an SQL query (see createQueryParam() function below for available returns).

Question: If the server is not running MySQL 5.6.3+, how can I convert a IPv6 binary stored in the database table (varbinary(16)) to a string?

For an IPv6 and on systems not running MySQL 5.6.3+ I'd like to return a MySQL function, or is it possible to do a bitwise operation? Otherwise, I'd have to do another call after the findIP() to use PHP's inet_ntop() function and I really rather not do that.

PHP:

class IP
{    
    protected $ip=NULL;
    protected $ip_version=NULL;

    /**
     * setIP
     *
     * Sets the data member $ip.
     *
     * @param   $ip
     * @access  protected
     */
    protected function setIP($ip)
    {
        # Check if the passed value is empty.
        if(!empty($ip))
        {
            # Clean it up.
            $ip=$ip;
        }
        else
        {
            # Explicitly set it to NULL.
            $ip=NULL;
        }
        # Set the data member.
        $this->ip=$ip;
    } #==== End -- setIP

    /**
     * setIPVersion
     *
     * Sets the data member $ip_version.
     *
     * @param   $ip_version
     * @access  protected
     */
    protected function setIPVersion($ip_version)
    {
        # Set the Validator instance to a variable.
        $validator=Validator::getInstance();

        # Check if the passed value is NULL.
        if($ip_version!==NULL)
        {
            # Clean it up.
            $ip_version=trim($ip_version);
            # Check if the passed value is an integer.
            if($validator->isInt($ip_version)===TRUE)
            {
                # Explicitly make it an integer.
                $ip_version=(int)$ip_version;
            }
            else
            {
                throw new Exception('The passed IP version was not a number!', E_RECOVERABLE_ERROR);
            }
        }
        else
        {
            # Explicitly set it to NULL.
            $ip_version=NULL;
        }
        # Set the data member.
        $this->ip_version=$ip_version;
    } #==== End -- setIPVersion

    /**
     * getIP
     *
     * Returns the data member $ip.
     *
     * @access  public
     */
    public function getIP()
    {
        return $this->ip;
    } #==== End -- getIP

    /**
     * getIPVersion
     *
     * Returns the data member $ip_version.
     *
     * @access  public
     */
    public function getIPVersion()
    {
        return $this->ip_version;
    } #==== End -- getIPVersion

    /**
     * createQueryParam
     *
     * Determines what MySQL function should be used based on MySQL version.
     *
     * @param   $ip                     The visitor's IP address or ip field in database table.
     * @access  public
     * @return  string
     */
    public function createQueryParam($ip, $select_query=FALSE)
    {
        # Set the Database instance to a variable.
        $db=DB::get_instance();
        # Get the database client version.
        $client_version=$db->client_version;

        # If the client version is 5.6.3+ use INET6_ATON.
        if($client_version>=50603)
        {
            # If this is not for a select query.
            if($select_query===FALSE)
            {
                return " INET6_ATON('".$ip."')";
            }
            # This is for a select query.
            return ' INET6_NTOA(`'.$ip.'`)';
        }
        # Else we assume PHP has IPv6 support and use PHP's inet_pton() to convert the IP to a binary.
        else
        {
            # If this is not for a select query.
            if($select_query===FALSE)
            {
                # If IPv4 then use MySQL function.
                if($this->getIPVersion()==4)
                {
                    return " INET_ATON('".$ip."')";
                }
                else
                {
                    # Supports IPv4 & IPv6 (if PHP has IPv6 supprot enabled).
                    return inet_pton($ip);
                }
            }
            # NOTE: How to handle select queries for IPv6 if MySQL version is less then 5.6.3?
            # Can a MySQL bitwise operation be used here?
            # IPv4 Only.
            //return ' INET_NTOA(`'.$ip.'`)';
            # For IPv6 I could use PHP's inet_ntop() but we can't return it here.
        }
    } #=== End -- createQueryParam

    /**
     * findIP
     *
     * Returns the IP of the visitor.
     * Throws an error if the IP address is not valid.
     *
     * @param   bool $for_sql_query     Convert IP addresss to binary for database.
     * @access  public
     * @return  string
     */
    public function findIP($for_sql_query=FALSE)
    {
        # Get the visitor's IP addreess.
        #   Use $_SERVER over getenv() since it's more server compatible.
        #   If $_SERVER['REMOTE_ADDR'] is empty, use getenv().
        $ip=$ip=(!empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : getenv('REMOTE_ADDR');
        # Check if the IP Address is valid.
        #   Throws an error if the IP is not valid.
        if($this->ipValid($ip)===TRUE)
        {
            # If we need to create the IP to a binary.
            if($for_sql_query===TRUE)
            {
                # Convert the visitor's IP to a binary.
                $ip=$this->createQueryParam($ip);
            }
            # Set the IP address to the data member.
            $this->setIP($ip);
        }
        # Return the data member.
        return $this->getIP();
    } #==== End -- findIP

    /**
     * ipValid
     *
     * Will determine if a given IP address is valid or not.
     * Will set the version of the IP address to the $ip_version data member.
     * Throws an error if the IP is not valid.
     *
     * @access  public
     * @param   $ip                     The IP address to validate.
     * @return  boolean
     */
    public function ipValid($ip)
    {
        # Detect if it is a valid IPv4 Address
        if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
        {
            # This is an IPv4 address.
            $version=4;
        }
        # Detect if it is a valid IPv6 Address
        elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
        {
            # This is an IPv6 address.
            $version=6;
        }
        if(isset($version))
        {
            $this->setIPVersion($version);
            return TRUE;
        }
        else
        {
            throw new Exception('The IP address was not valid!', E_RECOVERABLE_ERROR);
        }
        return FALSE;
    } #==== End -- ipValid
} #=== End IP class.

Examples:

Visitor's IP is 2001:cdba:0000:0000:0000:0000:3257:9652

The MySQL or PHP function used for an INSERT:

# Create a new IP object.
$ip_obj=new IP();
# This is for an SQL query.
$ip=$ip_obj->findIP(TRUE);
# On systems not running MySQL 5.6.3+ it uses PHP's inet_pton().
#    $ip =  ͺ2W�R
$sql='INSERT INTO `ip_log` (`ip`) VALUES ('.$ip.')';
# $sql = INSERT INTO `ip_log` (`ip`) VALUES ( ͺ2W�R);

The MySQL or PHP function used for a SELECT:

# Create a new IP object.
$ip_obj=new IP();
# Will return the correct MySQL function to use.
#    `ip` is the field in the `ip_log` table.
$ip_field=$ip_obj->createQueryParam('ip', TRUE);
# On systems not running MySQL 5.6.3+ I'd like to return a MySQL function, or is it possible to do a bitwise operation? Otherwise, I'd have to do another call in the PHP to use PHP's inet_ntop() function.
# This is where I need help.
Draven
  • 1,467
  • 2
  • 19
  • 47
  • You could use a UDF for that: https://bitbucket.org/watchmouse/mysql-udf-ipv6 – Dekel Jul 05 '15 at 12:18
  • @Dekel I saw that but it relies on me having access to install it. I rather not do it that way if I don't have to. Thanks though. – Draven Jul 05 '15 at 13:27
  • If the only thing that you want is to convert the binary data inside your `varbinary(16))` column to a string you can use the `hex` function (which available in all MySQL5+). Your problem in this case is that you will probably want to do the conversion using PHP (The result in your example will be `'2001CDBA000000000000000032579652'`) – Dekel Jul 05 '15 at 14:47

0 Answers0