Convert string to binary then back again using PHP
Asked Answered
G

11

70

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

Glyphography answered 17/6, 2011 at 7:36 Comment(3)
Are you trying to encode/decode string ? Why are you trying to convert string to binary ?Prakash
What's the problem you run into with what you want to do?Bargainbasement
Hashing is different to encrypting in that hashing is a one-way process. (You can't decrypt a hash, only attempt to match it).Atelier
C
73

You want to use pack and base_convert.

// Convert a string into binary
// Should output: 0101001101110100011000010110001101101011
$value = unpack('H*', "Stack");
echo base_convert($value[1], 16, 2);

// Convert binary into a string
// Should output: Stack
echo pack('H*', base_convert('0101001101110100011000010110001101101011', 2, 16));
Compliance answered 17/6, 2011 at 7:52 Comment(6)
strlen('101001101110100011000010110001101101011')==39? shouldn't this be even?Gossamer
You're absolutely correct! It's missing the leading 0, sorry about that.Compliance
what's worse, strlen(base_convert($value[1],16,2)) ==39 !Gossamer
Why why why 39 ?Comedian
I tried to convert this 001106130720160454 to binary using your code. It gives me zeros onlyCisalpine
this does not work with "0123456789" and the resulting binary of '0011001000110011001101000011010100110110000000000000000000000000'Xylon
K
45

Yes, sure!

There...

$bin = decbin(ord($char));

... and back again.

$char = chr(bindec($bin));
Keloid answered 17/6, 2011 at 7:47 Comment(4)
For string concatenation, maybe add a str_pad(..., 8, 0, STR_PAD_LEFT) :)Delia
@Delia Thanks for completing my answer... @cfarm54 Of course what I said up there is just for one single character!Keloid
@Delia can you give a full example of how this is done please?Camporee
Only works if the initial binary string has a length divisible by 8Urbanism
J
11

A string is just a sequence of bytes, hence it's actually binary data in PHP. What exactly are you trying to do?

EDIT

If you want to store binary data in your database, the problem most often is the column definition in your database. PHP does not differentiate between binary data and strings, but databases do. In MySQL for example you should store binary data in BINARY, VARBINARY or BLOB columns.

Another option would be to base64_encode your PHP string and store it in some VARCHAR or TEXT column in the database. But be aware that the string's length will increase when base64_encode is used.

Joyous answered 17/6, 2011 at 7:40 Comment(2)
Yes my database has a column defined with BINARY type, but if I'm to pass a variable to the database (the password) it should also be the binary type. I'm asking how to make this conversion.Glyphography
How are you passing the data to the database (function used)? Which database do you use?Joyous
B
7

Your hash is already binary and ready to be used with your database.

However you must need to convert it into a format the database column definition expects.

Any string in PHP (until 5.3 and beyond) is a binary string. That means it contains only binary data.

(This has stayed the same, PHP 6 never made it, the rest of this answer is perhaps only of historic interest, it may still work but IIRC the b'string' has been removed - or not.)

However because of backwards compatibility with PHP 6 you can already cast your string explicitly as binary:

$string = 'my binary string';
$binary = b'my binary string';

But that is merely for compatibility reasons, in your code you can just do:

$string = $binary; // "convert" binary string into string
$binary = $string  // "convert" string into binary string

Because it's the same. The "convert" is superfluous.

Bargainbasement answered 17/6, 2011 at 7:56 Comment(5)
If I store data in DB in binary format, how can I specify where condition from php (without converting DB field into String). I.e. WHERE field = binary_value_from_php? (And in php, user inputs string, ofcourse). Is there any string_to_mysql_binary function in php?Lysozyme
@Andrew: Strings in PHP are binary. The mysql driver will do that (non-) conversion then, that is, leave the data unchanged as you told mysql the column is a binary string. More clear?Bargainbasement
but it doesn't work. When i store word car in the db, it is not stored as 'car' (literally), so when i want to find this record, i can not use where field = 'car', because it won't find it....Lysozyme
Rest assured it does perfecly work. It's perhaps just an issue with understanding of what those commands do and what string encoding in the database actualy is? The search you name suggests you're not looking for binary encoding btw, you normally use it for encoding the database does not support well and then you don't use the database to search for it. Just saying.Bargainbasement
yes, you are right, it works automatically, all is ok. I had an issue elsewhere, with another issue (possible mysql bug or some other setting), so sorry for confusion. ThanksLysozyme
P
5

easiest way I found was to convert to HEX instead of a string. If it works for you:

$hex = bin2hex($bin); // It will convert a binary data to its hex representation

$bin = pack("H*" , $hex); // It will convert a hex to binary

OR

$bin = hex2bin($hex); // Available only on PHP 5.4
Pirnot answered 26/7, 2013 at 17:17 Comment(0)
M
2

I would most definitely recommend using the built in standard password libraries that come with PHP - Here is a good example on how to use them.


For those coming here to figure out how to go from Binary Strings to Decimals and back, there are some good examples below.

For converting binary "strings" to decimals/chars you can do something like this...

echo bindec("00000001") . "\n";
echo bindec("00000010") . "\n";
echo bindec("00000100") . "\n";
echo bindec("00001000") . "\n";
echo bindec("00010000") . "\n";
echo bindec("00100000") . "\n";
echo bindec("01000000") . "\n";
echo bindec("10000000") . "\n";
echo bindec("01000001") . "\n";

# big binary string
echo bindec("111010110111011110000110001")."\n";

The above outputs:

1
2
4
8
16
32
64
128
65
123452465

For converting decimals to char/strings you can do this:

# convert to binary strings "00000001"
echo decbin(1) . "\n";
echo decbin(2) . "\n";
echo decbin(4) . "\n";
echo decbin(8) . "\n";
echo decbin(16) . "\n";
echo decbin(32) . "\n";
echo decbin(64) . "\n";
echo decbin(128) . "\n";

# convert a ascii character
echo str_pad(decbin(65), 8, 0, STR_PAD_LEFT) ."\n";

# convert a 'char'
echo str_pad(decbin(ord('A')), 8, 0, STR_PAD_LEFT) ."\n";

# big number...
echo str_pad(decbin(65535), 8, 0, STR_PAD_LEFT) ."\n";
echo str_pad(decbin(123452465), 8, 0, STR_PAD_LEFT) ."\n";

The above outputs:

1
10
100
1000
10000
100000
1000000
10000000
01000001
01000001
1111111111111111
111010110111011110000110001
Moorer answered 11/6, 2015 at 14:11 Comment(0)
S
1

Strings in PHP are always BLOBs. So you can use a string to hold the value for your database BLOB. All of this stuff base-converting and so on has to do with presenting that BLOB.

If you want a nice human-readable representation of your BLOB then it makes sense to show the bytes it contains, and probably to use hex rather than decimal. Hence, the string "41 42 43" is a good way to present the byte array that in C# would be

var bytes = new byte[] { 0x41, 0x42, 0x43 };

but it is obviously not a good way to represent those bytes! The string "ABC" is an efficient representation, because it is in fact the same BLOB (only it's not so Large in this case).

In practice you will typically get your BLOBs from functions that return string - such as that hashing function, or other built-in functions like fread.

In the rare cases (but not so rare when just trying things out/prototyping) that you need to just construct a string from some hard-coded bytes I don't know of anything more efficient than converting a "hex string" to what is often called a "binary string" in PHP:

$myBytes = "414243";
$data = pack('H*', $myBytes);

If you var_dump($data); it'll show you string(3) "ABC". That's because 0x41 = 65 decimal = 'A' (in basically all encodings).

Since looking at binary data by interpreting it as a string is not exactly intuitive, you may want to make a basic wrapper to make debugging easier. One possible such wrapper is

class blob
{
    function __construct($hexStr = '')
    {
        $this->appendHex($hexStr);
    }

    public $value;

    public function appendHex($hexStr)
    {
        $this->value .= pack('H*', $hexStr);
    }

    public function getByte($index)
    {
        return unpack('C', $this->value{$index})[1];
    }

    public function setByte($index, $value)
    {
        $this->value{$index} = pack('C', $value);
    }

    public function toArray()
    {
        return unpack('C*', $this->value);
    }
}

This is something I cooked up on the fly, and probably just a starting point for your own wrapper. But the idea is to use a string for storage since this is the most efficient structure available in PHP, while providing methods like toArray() for use in debugger watches/evaluations when you want to examine the contents.

Of course you may use a perfectly straightforward PHP array instead and pack it to a string when interfacing with something that uses strings for binary data. Depending on the degree to which you are actually going to modify the blob this may prove easier, and although it isn't space efficient I think you'd get acceptable performance for many tasks.

An example to illustrate the functionality:

// Construct a blob with 3 bytes: 0x41 0x42 0x43.
$b = new blob("414243");

// Append 3 more bytes: 0x44 0x45 0x46.
$b->appendHex("444546");

// Change the second byte to 0x41 (so we now have 0x41 0x41 0x43 0x44 0x45 0x46).
$b->setByte(1, 0x41); // or, equivalently, setByte(1, 65)

// Dump the first byte.
var_dump($b->getByte(0));

// Verify the result. The string "AACDEF", because it's only ASCII characters, will have the same binary representation in basically any encoding.
$ok = $b->value == "AACDEF";
Sickler answered 6/1, 2017 at 12:37 Comment(1)
Hm, I am learning this stuff as I go along, and just discovered you can in fact specify the binary content of a string directly using the escape sequence \x. So $s = "\x41" is equivalent to $s = "A" (provided the source code is stored in an encoding where A is the byte 0x41).Sickler
D
1

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

December answered 10/2, 2021 at 21:11 Comment(0)
V
0

i was looking for some string bits conversion and got here, If the next case is for you take //it so... if you want to use the bits from a string into different bits maybe this example would help

$string="1001"; //this would be 2^0*1+....0...+2^3*1=1+8=9
$bit4=$string[0];//1
$bit3=$string[1];
$bit2=$string[2];
$bit1=$string[3];//1
Vistula answered 9/7, 2014 at 17:51 Comment(0)
H
0

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
Headcloth answered 26/10, 2017 at 13:34 Comment(0)
L
-5

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.

Lester answered 28/10, 2019 at 12:48 Comment(2)
why should one needlessly implement javascript if serverside logic is needed anyway? the poster asked for PHP help and that's it.Disclose
Vue is less then 16 KB. So, there won't be any issue if the user implements Javascript on the front-end. And, this question was asked many years ago(When there were js frameworks) while today there are so many light-weight frameworks available so, why should one use the back-end when such operations can be performed on the front-end?Lester

© 2022 - 2024 — McMap. All rights reserved.