Convert string to binary then back again using PHP

Viewed 176225

Is there a way to convert a string to binary then back again in the standard PHP library?

To clarify what I'm trying to do is store a password on a database. I'm going to convert it first using a hash function then eventually store it as binary.


I've found the best way is to use this function. Seems to hash and output in binary at the same time.

http://php.net/manual/en/function.hash-hmac.php

11 Answers

Anyone who is here in 2021, can use SteeveDroz answer; but unfortunately, that is only for 1 character. So I put it into a for loop to loop through and change each character of the string.

EDIT: I just now realized the binary_encode function I made does NOT turn the characters into 8 bits (which is very important), it turns them into 6-7 bits but lucky all I needed todo was prepend them with the extra 0s needed to make it 8-bits. I updated the encode function below. Also I didn't need to fix the decode function as it works with the prepended 0s and without :)

The Functions (Updated):

function binary_encode($str){
    
    # Declare both Binary variable and Prepend variable
    $bin = (string)""; $prep = (string)"";
    
    # Iterate through each character of our input ($str) 
    for($i = 0; $i < strlen($str); $i++){
        
        # Encode The current character into binary
        $bincur = decbin( ord( $str[$i] ) );
        
        # Count the length of said binary
        $binlen = strlen( $bincur );
        
        # If the length of our character in binary is less than a byte (8 bits); Then
        # For how ever many characters it is short;
        # it will replace with 0's in our Prepend variable.
        if( $binlen < 8 ) for( $j = 8; $j > $binlen; $binlen++ ) $prep .= "0"; 
        
        # Build our correct 8 bit string and add it to our Binary variable
        $bin .= $prep.$bincur." ";
        
        # Clear our Prepend variable before the next Loop
        $prep = "";

    }

    # Return the final result minus the one whitespace at the end
    # (from our for loop where we build the 8 bit string
    return substr($bin, 0, strlen($bin) - 1);

}

function binary_decode($bin){
    $char = explode(' ', $bin);
    $nstr = '';
    foreach($char as $ch) $nstr .= chr(bindec($ch));
    return $nstr;
}

Usage:

$bin = binary_encode("String Here");
$str = binary_decode("1010011 1110100 1110010 1101001 1101110 1100111 100000 1001000 1100101 1110010 1100101");

Old Live Demo:

http://sandbox.onlinephpfunctions.com/code/2553fc9e26c5148fddbb3486091d119aa59ae464

New Live Demo:

http://sandbox.onlinephpfunctions.com/code/1d71888cd41371646431f9914ccd86cf5ef6303e

That's funny how Stefan Gehrig his answer is actually the correct one. You don't need to convert a string into a "011010101" string to store it in BINARY field in a database. Anyway since this is the first answer that comes up when you google for "php convert string to binary string". Here is my contribution to this problem.

The most voted answer by Francois Deschenes goes wrong for long strings (either bytestrings or bitstrings) that is because

base_convert() may lose precision on large numbers due to properties related to the internal "double" or "float" type used. Please see the Floating point numbers section in the manual for more specific information and limitations.

From: https://secure.php.net/manual/en/function.base-convert.php

To work around this limitation you can chop up the input string into chunks. The functions below implement this technique.

<?php

function bytesToBits(string $bytestring) {
  if ($bytestring === '') return '';

  $bitstring = '';
  foreach (str_split($bytestring, 4) as $chunk) {
    $bitstring .= str_pad(base_convert(unpack('H*', $chunk)[1], 16, 2), strlen($chunk) * 8, '0', STR_PAD_LEFT);
  }

  return $bitstring;
}

function bitsToBytes(string $bitstring) {
  if ($bitstring === '') return '';

  // We want all bits to be right-aligned
  $bitstring_len = strlen($bitstring);
  if ($bitstring_len % 8 > 0) {
    $bitstring = str_pad($bitstring, intdiv($bitstring_len + 8, 8) * 8, '0', STR_PAD_LEFT);
  }

  $bytestring = '';
  foreach (str_split($bitstring, 32) as $chunk) {
    $bytestring .= pack('H*', str_pad(base_convert($chunk, 2, 16), strlen($chunk) / 4, '0', STR_PAD_LEFT));
  }

  return $bytestring;
}

for ($i = 0; $i < 10000; $i++) {
  $bytestring_in = substr(hash('sha512', uniqid('', true)), 0, rand(0, 128));
  $bits = bytesToBits($bytestring_in);
  $bytestring_out = bitsToBytes($bits);
  if ($bytestring_in !== $bytestring_out) {
    printf("IN  : %s\n", $bytestring_in);
    printf("BITS: %s\n", $bits);
    printf("OUT : %s\n", $bytestring_out);
    var_dump($bytestring_in, $bytestring_out); // printf() doesn't show some characters ..
    die('Error in functions [1].');
  }
}


for ($i = 0; $i < 10000; $i++) {
  $len = rand(0, 128);
  $bitstring_in = '';
  for ($j = 0; $j <= $len; $j++) {
    $bitstring_in .= (string) rand(0,1);
  }
  $bytes = bitsToBytes($bitstring_in);
  $bitstring_out = bytesToBits($bytes);

  // since converting to byte we always have a multitude of 4, so we need to correct the bitstring_in to compare ..
  $bitstring_in_old = $bitstring_in;
  $bitstring_in_len = strlen($bitstring_in);
  if ($bitstring_in_len % 8 > 0) {
    $bitstring_in = str_pad($bitstring_in, intdiv($bitstring_in_len + 8, 8) * 8, '0', STR_PAD_LEFT);
  }

  if ($bitstring_in !== $bitstring_out) {
    printf("IN1  : %s\n", $bitstring_in_old);
    printf("IN2  : %s\n", $bitstring_in);
    printf("BYTES: %s\n", $bytes);
    printf("OUT  : %s\n", $bitstring_out);
    var_dump($bytes); // printf() doesn't show some characters ..
    die('Error in functions [2].');
  }
}

echo 'All ok!' . PHP_EOL;

Note that if you insert a bitstring that is not a multitude of 8 (example: "101") you will not be able to recover the original bitstring when you converted to bytestring. From the bytestring converting back, uyou will get "00000101" which is numerically the same (unsigned 8 bit integer) but has a different string length. Therefor if the bitstring length is important to you you should save the length in a separate variable and chop of the first part of the string after converting.

$bits_in = "101";
$bits_in_len = strlen($bits_in); // <-- keep track if input length
$bits_out = bytesToBits(bitsToBytes("101"));
var_dump($bits_in, $bits_out, substr($bits_out, - $bits_in_len)); // recover original length with substr

Why you are using PHP for the conversion. Now, there are so many front end languages available, Why you are still including a server? You can convert the password into the binary number in the front-end and send the converted string in the Database. According to my point of view, this would be convenient.

var bintext, textresult="", binlength;
    this.aaa = this.text_value;
    bintext = this.aaa.replace(/[^01]/g, "");
        binlength = bintext.length-(bintext.length%8);
        for(var z=0; z<binlength; z=z+8) {
            textresult += String.fromCharCode(parseInt(bintext.substr(z,8),2));
                            this.ans = textresult;

This is a Javascript code which I have found here: http://binarytotext.net/, they have used this code with Vue.js. In the code, this.aaa is the v-model dynamic value. To convert the binary into the text values, they have used big numbers. You need to install an additional package and convert it back into the text field. In my point of view, it would be easy.

Related