Change IPv4 to IPv6 string

ipv6, php, string, transform

Solution

First of all: why would you care how the address is written? inet_pton() will parse all variations for you and give you a consistent result, which you can then transform into binary, hex, or whatever you want.

All the code for converting things like `::192.168.0.1` to `0000:0000:0000:0000:0000:0000:c0a8:0001` was actually in my post. That's exactly what my example function does.

If you feed `0000:0000:0000:0000:0000:0000:192.168.0.1` to inet_pton() and then to inet_ntop() you'll get the canonical IPv6 notation, which is `::192.168.0.1` in this case. If that string begins with `::` and the rest contains no `:` and three dots then you can be pretty sure it's an IPv4 address ;-)

To combine the answer to your previous question with this question:

function expand_ip_address($addr_str) {
  /* First convert to binary, which also does syntax checking */
  $addr_bin = @inet_pton($addr_str);
  if ($addr_bin === FALSE) {
    return FALSE;
  }

  $addr_hex = bin2hex($addr_bin);

  /* See if this is an IPv4-Compatible IPv6 address (deprecated) or an
     IPv4-Mapped IPv6 Address (used when IPv4 connections are mapped to
     an IPv6 sockets and convert it to a normal IPv4 address */
  if (strlen($addr_bin) == 16
  &&  substr($addr_hex, 0, 20) == str_repeat('0', 20)) {
    /* First 80 bits are zero: now see if bits 81-96 are either all 0 or all 1 */
    if (substr($addr_hex, 20, 4) == '0000')
    ||  substr($addr_hex, 20, 4) == 'ffff')) {
      /* Remove leading bits so only the IPv4 bits remain */
      $addr_bin = substr($addr_hex, 12);
    }
  }

  /* Then differentiate between IPv4 and IPv6 */
  if (strlen($addr_bin) == 4) {
    /* IPv4: print each byte as 3 digits and add dots between them */
    $ipv4_bytes = str_split($addr_bin);
    $ipv4_ints = array_map('ord', $ipv4_bytes);
    return vsprintf('%03d.%03d.%03d.%03d', $ipv4_ints);
  } else {
    /* IPv6: print as hex and add colons between each group of 4 hex digits */
    return implode(':', str_split($addr_hex, 4));
  }
}

Problem

Sander Steffann mentioned in a previous question of mine: Addresses like 0000:0000:0000:0000:0000:0000:192.168.0.1 are written as 0000:0000:0000:0000:0000:0000:c0a8:0001 which is exactly the same address but in hex notation. How do I detect in PHP if an address was written like eg.: `::0000:192.168.0.1` or `0000::0000:192.168.0.1` or `0000:0000:0000:0000:0000:0000:192.168.0.1` etc.? Is it enough to check if an IP-based string has '.' AND ':' ? And how do I change this to the full string `0000:0000:0000:0000:0000:0000:c0a8:0001`? Am I correct, to change this to IPv4 will be something like: ``` <?php $strIP = '0000:0000:0000:0000:0000:0000:192.168.0.1'; $strResult = substr($strIP, strrpos($strIP, ':')); echo $strResult; //192.168.0.1 ? ?> ``` ... or are correct IP string representations more complex than what this snippet could do?

Original source