Example:
Uuid generated from v4 of php :
8bc278cb-2fb6-413b-add6-8ba39bf830e8
I want to convert this into two 64 bit integers.
I've tried using hexdec of php but it's return value is of numbers. I want datatype integer.
Interestingly :
I have tried using hexdec with the above uuid and used this output to dechex. Some how, not getting the same value?
Any insights into this will be appreciated.
UUID is a 128bit data type. Excluding the 6 reserved bits, there are 122 data bits in it. Makes it impossible to fully convert any UUID to a 64bit integer. You'll at least need to store it as 2 64bit numbers or 4 32bit numbers.
You can unpack the UUID into binary, then unpack it as 4 32bit unsigned character:
function uuidToHex($uuid) {
return str_replace('-', '', $uuid);
}
function hexToUuid($hex) {
$regex = '/^([\da-f]{8})([\da-f]{4})([\da-f]{4})([\da-f]{4})([\da-f]{12})$/';
return preg_match($regex, $hex, $matches) ?
"{$matches[1]}-{$matches[2]}-{$matches[3]}-{$matches[4]}-{$matches[5]}" :
FALSE;
}
function hexToIntegers($hex) {
$bin = pack('h*', $hex);
return unpack('L*', $bin);
}
function integersToHex($integers) {
$args = $integers; $args[0] = 'L*'; ksort($args);
$bin = call_user_func_array('pack', $args);
$results = unpack('h*', $bin);
return $results[1];
}
$uuid = '1968ec4a-2a73-11df-9aca-00012e27a270';
var_dump($uuid);
$integers = hexToIntegers(uuidToHex('1968ec4a-2a73-11df-9aca-00012e27a270'));
var_dump($integers);
$uuid = hexToUuid(integersToHex($integers));
var_dump($uuid);
It will returns
string(36) "1968ec4a-2a73-11df-9aca-00012e27a270"
array(4) {
[1]=>
int(2764998289)
[2]=>
int(4245764002)
[3]=>
int(268479657)
[4]=>
int(120222434)
}
string(36) "1968ec4a-2a73-11df-9aca-00012e27a270"
$integers is an array 4 32bit numbers that represents the hex.
Reference
Stack Overflow: 16 bytes binary form of canonical uuid representation in php
Stack Overflow: How to convert byte array to integer in php?
PHP Manual: pack()
While it's common in other languages (like Java) to get the least significant and most significant bits of a UUID as two unsigned 64-bit integers, PHP has trouble with this because all integers in PHP are signed. Even if using a 64-bit build of PHP, you will run into integer overflows when trying to get a real integer value of the least or most significant bits of a UUID.
The maximum integer value on 64-bit PHP is 9,223,372,036,854,775,807, while an unsigned 64-bit integer has a max value of 18,446,744,073,709,551,615. A UUID as two 64-bit integers needs to support values up to 18,446,744,073,709,551,615.
As an earlier answer mentioned, it might be better to split up the UUID into four unsigned 32-bit integers, if you need to split it up like that. (The max value of an unsigned 32-bit integer is 4,294,967,295, which 64-bit PHP can support.)
If you're simply interested in storing a UUID in a more optimized way, you can convert it to a 16-byte binary string:
$uuid = '1968ec4a-2a73-11df-9aca-00012e27a270';
$binaryUuid = hex2bin(str_replace('-', '', $uuid));
Related
For a project I need to read in information from MQTT. The payload is filled with protobuf information, that needs to be converted.
For a certain value I receive 5.6904566139035E-28 as float. Using http://www.exploringbinary.com/floating-point-converter/ I can convert this when I tick single and raw hexadecimal value, then I receive 12345678, the value I should have (I know what is sent).
But now I need to do that conversion in PHP. I haven't any idea how this could be done. After some reading I figured out it is a Floating Point, but how to convert this like done on that website.
Is there someone that can help me with this!
Thanks a lot!
With the quite cryptic pack and unpack functions, it can be done in a one-liner:
function rawSingleHex($num) {
return strrev(unpack('h*', pack('f', $num))[1]);
}
This "packs" the number as its binary representation, then "unpacks" it in an array with one element: the binary representation in hexadecimal format. This format has the digits in the reversed order, so the function reverses that in the final result.
Call it by passing the floating point number:
echo rawSingleHex(5.6904566139035E-28);
Output:
12345678
Without pack/pack
(this was my original answer, but with the first option being available, this is not the advised way to proceed)
The binary format is explained in Wikipedia's article on the Single-precision floating-point format.
Here is a PHP function that implements the described procedure:
function rawSingleHex($num) {
if ($num == 0) return '00000000';
// set sign bit, and add another, higher one, which will be stripped later
$sign = $num < 0 ? 0x300 : 0x200;
$significant = abs($num);
$exponent = floor(log($significant, 2));
// get 24 most significant binary bits before the comma:
$significant = round($significant / pow(2, $exponent-23));
// exponent has exponent-bias format:
$exponent += 127;
// format: 1 sign bit + 8 exponent bits + 23 significant bits,
// without left-most "1" of significant
$bin = substr(decbin($sign + $exponent), 1) .
substr(decbin($significant), 1);
// assert that result has correct number of bits:
if (strlen($bin) !== 32) {
return "unexpected error";
}
// convert binary representation to hex, with exactly 8 digits
return str_pad(dechex(bindec($bin)), 8, "0", STR_PAD_LEFT);
}
It outputs the same as in the first solution.
The goal is to store the hash on a mysql database, using INT (not BIGINT or MEDIUMINT).
md5('string', true) returns binary data, 16 bytes of hash. I thought i could grep the first 4 bytes and convert it to an INT(32bit/4bytes) integer, but i don't know how to do it.
What do you suggest? Thanks.
Use crc32, it will return a 32bit int.
var_dump (crc32 ("hello world"));
var_dump (crc32 ("world hello"));
output
int(222957957)
int(1292159901)
PHP: crc32 - Manual
Generates the cyclic redundancy checksum polynomial of 32-bit lengths
of the str. This is usually used to validate the integrity of data
being transmitted.
Because PHP's integer type is signed, and many crc32 checksums will
result in negative integers, you need to use the "%u" formatter of
sprintf() or printf() to get the string representation of the unsigned
crc32 checksum.
The php hash() function will be even better than crc32():
hash("crc32b", $str);
And if an integer is required:
intval(hash("crc32b", $str), 16);
Compared to the plain crc32(), it will not be affected by the signed/unsigned integer inconsistency between 32-bit and 64-bit systems (see PHP docs for the details: crc32)
ord($hash[0]) * 16777216 + ord($hash[1]) * 65536 + ord($hash[2]) * 256 + ord($hash[3]) ;
Or:
unpack("L", substr($hash,0,4));
But Filip Roséen's solution is better.
Another alternative is to use a map to link your hash to integer, such as Redis.
L43F34FLK34FL3K4 ==> 1
LKZLCKCLK32 ==> 2
etc....
Here is Daniel J. Bernstein hash that returns an 32bit integer.
function djb_hash($str) {
for ($i = 0, $h = 5381, $len = strlen($str); $i < $len; $i++) {
$h = (($h << 5) + $h + ord($str[$i])) & 0x7FFFFFFF;
}
return $h;
}
I am trying to xor two values which are like below:
Variable 1 : 6463334891
Variable 2 : 1000212390
When i did xor with these values in php it gives me wrong answer.
It should give me "7426059853"
This is my code
$numericValue = (int)$numericValue;
$privateKey = (int)$privateKey;
echo "Type of variable 1 ".gettype($numericValue)."<br />";
echo "Type of variable 2 ".gettype($privateKey)."<br />";
$xor_val = (int)$numericValue ^ (int)$privateKey;
echo "XOR Value :".$xor_val."<br />";
Just a total stab into the dark...
You're doing this:
echo "6463334891" ^ "1000212390";
When you want to be doing this:
echo 6463334891 ^ 1000212390;
XOR is an operation on bytes. The byte representation of the integer 6463334891 and the string "6463334891" are very different. Hence this operation will result in very different values depending on whether the operands are strings or integers. If you get your numbers in string form, cast them to an int first:
echo (int)$var1 ^ (int)$var2;
That is because you re hitting the MAXIMUM INTEGER LIMIT which is 2147483647
From the PHP Docs...
The maximum value depends on the system. 32 bit systems have a maximum
signed integer range of -2147483648 to 2147483647. So for example on
such a system, intval('1000000000000') will return 2147483647. The
maximum signed integer value for 64 bit systems is
9223372036854775807.
Thus to handle such big integers you need to make use of an extension like (GMP) GNU Multiple Precision
<?php
$v1="6463334891";
$v2="1000212390";
$a = gmp_init($v1);
$b = gmp_init($v2);
echo gmp_intval($a) ^ gmp_intval($b); //"prints" 7426059853
Else , Switch to a 64-bit system.
my solution to maintain the value of big integers is to convert them to binary (with base_convert cause decbin doesnt work) and then make the xor for every bit, to finally convert the string to decimal.
function binxor($w1,$w2)
{
$x=base_convert($w1, 10, 2);
$y=base_convert($w2, 10, 2);
// adjust so both have same lenght
if (strlen($y)<strlen($x)) $y=str_repeat(0,strlen($x)-strlen($y)).$y;
if (strlen($x)<strlen($y)) $x=str_repeat(0,strlen($y)-strlen($x)).$x;
$x=str_split($x);$y=str_split($y);
$z="";
for ($k=0;$k<sizeof($x);$k++)
{
// xor bit a bit
$z.=(int)($x[$k])^(int)($y[$k]);
}
return base_convert($z,2,10);
}
Also, to adjust large numbers to 32 bits
bindec(decbin($number))
because decbin cuts the number to 32 automatically.
Why doesn't echo openssl_random_pseudo_bytes(12) print out anything but if I concatenate it with another string it does show the output? According to the manual the return type of openssl_random_pseudo_bytes is a string so why is there a problem? I tried type casting it with (string) and it didn't work.
The openssl_random_pseudo_bytes(...) function returns a binary number in the form of a string (i.e. ASCII value(s)) of the specified length.
For example, one possible output of:
$number_of_bytes = 1;
$bin = openssl_random_pseudo_bytes($number_of_bytes, $cstrong);
$hex=bin2hex($bin);
$dec=hexdec($hex);
could be:
var_dump($bin); // string(1) "ã"
var_dump($hex); // string(2) "e3"
var_dump($dec); // int(227)
var_dump($cstrong); // bool(true)
Notes:
$dec is an integer random value that can be equal to (at most) 2 ^ (8 * $number_of_bytes) - 1.
where one byte comprises 8 bits
PHP has a integer overflow limitation of at most 2^31-1 or 2^63-1 bits (the limits of signed integers which use 4 bytes or 8 bytes depending on whether you have a 32 or 64 bit platform respectively) , after which it overflows / casts into a float value (potentially limiting precision).
so calling with 4 (or 8) bytes, half of the time $dec would be a float
At higher numbers of bytes, the $bin and $hex values maintain their precision and accuracy (because all of the digits/bits are kept in a (variable length) string).
openssl_random_pseudo_bytes returns false when it fails.
$cstrong!==true indicates openssl_random_pseudo_bytes did not return a result generated by a cryptographically strong algorithm. (http://php.net/openssl_random_pseudo_bytes)
Example Function (demonstrates handling a false return value or when $cstrong is false)
class Random
{
public static function Get($number_of_bytes=4)
{
$binary_value = openssl_random_pseudo_bytes($number_of_bytes, $cstrong);
// Unable to produce a cryptographically strong value
if($binary_value==false || $cstrong!==true) return false; // failure
// other processing
$hexadecimal_value = bin2hex($binary_value);
$decimal_value = hexdec($hexadecimal_value);
// Returns a positive integer (or a float
// in the case of an integer overflow)
return $decimal_value;
}
}
manual: http://php.net/openssl_random_pseudo_bytes
Usage
echo Random::Get(12); // returns a large float value most of the time
I know this is a pretty silly question, but I don't know what to do.
I have an arbitrary binary number, say,
1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111
I want to convert it to Base 64 using PHP - and every way I try gives me a different result. Even different online converters convert it differently:
http://home2.paulschou.net/tools/xlate/
http://convertxy.com/index.php/numberbases/
PHP's base_convert only works up to base36, and base64_encode expects a string.
What do I do?
UPDATE: I implemented the solution functions suggested by #binaryLV, and it did work well.
However, I compared the results to PHP's built-in base_convert. It turned out that base_convert to base36 returns shorter values that the custom base64 function! (And yes, I did prepend a '1' to all the binary numbers to ensure leading zeros aren't lost).
I have noticed, too, that base_convert is quite innacurate with large numbers. So I need is a function which works like base_convert, but accurately and, preferably, up to base 64.
Length of a string in example is 160. It makes me think that it holds info about 160/8 characters. So,
split string into parts, each part holds 8 binary digits and describes single character
convert each part into a decimal integer
build a string from characters, that are made from ASCII codes from 2nd step
This will work with strings with size n*8. For other strings (e.g., 12 binary digits) it will give unexpected results.
Code:
function bin2base64($bin) {
$arr = str_split($bin, 8);
$str = '';
foreach ( $arr as $binNumber ) {
$str .= chr(bindec($binNumber));
}
return base64_encode($str);
}
$bin = '1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111';
echo bin2base64($bin);
Result:
kDICQIMBEFjDBxwMBJiAASBYwgc=
Here's also function for decoding it back to string of binary digits:
function base64bin($str) {
$result = '';
$str = base64_decode($str);
$len = strlen($str);
for ( $n = 0; $n < $len; $n++ ) {
$result .= str_pad(decbin(ord($str[$n])), 8, '0', STR_PAD_LEFT);
}
return $result;
}
var_dump(base64bin(bin2base64($bin)) === $bin);
Result:
boolean true
PHP has a built in base 64 encoding function, see documentation here. If you want the decimal value of the binary string first use bin2dec, there are similar functions for hexadecimals by the way. The documentation is your friend here.
[EDIT]
I might have misunderstood your question, if you want to convert between actual bases (base 2 and 64) use base_convert
$number = 1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111;
echo base64_encode ($number);
This is if you want the exact string be converted into Base 64.
To convert a binary number (2 base) to a 64 base use the base_convert function.
$number = 1001000000110010000000100100000010000011000000010001000001011000110000110000011100011100000011000000010010011000100000000000000100100000010110001100001000000111;
base_convert ($number , 2, 64);