Why does the '<<' operator sometimes behave differently in Javascript and PHP? - php

Take the following code snippet:
<script>
var x = 25;
document.write(x << 9);
</script>
<?php
$x = 25;
echo ($x << 9);
?>
This outputs: 12800 12800
OK. Normal so far... Now lets try this:
<script>
var x = 12345678;
document.write(x << 9);
</script>
<?php
$x = 12345678;
echo ($x << 9);
?>
This time the output is 2026019840 6320987136
Why are the latter two values different? And, most importantly (to me), how do I get the PHP implementation to do what the Javascript implementation does? In other words, I want my PHP code to output 2026019840 instead of 6320987136

use (x << 9) % 0x100000000 or its PHP equivalent. (or what you just said)

PHP is giving you a 64 bit result. Javascript is just giving you the lower 32bits. If you use (x << 9) & 0xFFFFFFFF in PHP you'll get the low 32 bits.
you may run into problems with the sign bit though. Try (23456789 << 9)

Related

PHP equivalent of bitwise operation NodeJs script

I'm trying to convert a pretty simple function from NodeJs to PHP.
I think I found out how to convert 80% of it, but I am stuck around bitwise operators.
This is the original NodeJs script:
function convert(encodedString) {
let bytes = new Buffer.from(encodedString, 'base64')
let code = bytes[bytes.length - 1]
code ^= 91
for (let i = 0; i < bytes.length - 1; ++i) {
bytes[i] = ((bytes[i] ^ (code << (i % 5))) | 0)
}
return bytes
}
And this is the converted PHP version
function convert($encoded)
{
$bytes = unpack('C*', base64_decode($encoded));
$code = $bytes[count($bytes)];
$code ^= 91;
for($i = 0; $i < count($bytes) - 1; ++$i) {
$bytes[$i + 1] = (($bytes[$i + 1] ^ ($code << ($i % 5))) | 0);
}
return $bytes;
}
This somehow works until the bitwise part. I get the correct result for the first element of the array, but all the consequent values are wrong. I also had to adapt indexing because with the unpack method i get an base-1 index array.
If I loop each array's value before the conversion, I have the same result on both scripts, so I think is correct until that.
I was able to reproduce the issue on NodeJs, if for example I define a normal array (with same values) instead using Buffer.
I don't really know how to reproduce the same behaviour in PHP.
Any help is appreciated!
Bitwise operators in Javascript and PHP handle calucations differently.
Example:
Javascript:
1085 << 24 = 1023410176
PHP:
1085 << 24 = 18203279360
Bitwise operations PHP and JS different
You should write your own function for bitwise operators in PHP.
What is JavaScript's highest integer value that a number can go to without losing precision?

Why is my php code not outputting the same as my C++ using chr

I am converting a working C++ program to php. Now even though most of the program works I am having trouble with one aspect of it.
The code that works in C++ is not giving the same results in php.
This is for writing to a binary file later in my code.
Here is the C++ code and the result.
C++
char test;
test = 16;
test+=(char)131;
std::cout << (int)test << endl;
Result = -109
and here is the equivalent code in php.
PHP
$test = chr(0);
$test = 16;
$test+= chr(131);
echo (int)$test;
Result = 16
I am guessing this might have something to do with unicode encoding giving me the wrong result. The result should be -109 as my C++ program works correctly, but I am not getting the same result in php.
When I cout (int)(char)131 in C++ I get -125, but if I echo chr(131) in php I get 0;
Is there anyway to make my php output the same as my C++?
You can achieve this with:
<?php
$test = 16;
$test = ($test + 131) % 256;
if ($test > 127) {
$test -= 256;
}
echo $test;
?>
The addition
$test+= chr(131);
doesn't do what you expect. PHP tries to convert the string/character chr(131) into number. It can't convert it because the string doesn't contain any digits so the statement is equivalent to
$test+= 0;
I think the clean way is the c++ expression (int)(char)131 first to correctly convert to PHP integer and then adding the values. The conversion realizes the function signByteToInt():
<?php
function signByteToInt($byteVal){
$byteVal &= 0xff;
if($byteVal & 0x80) $byteVal -= 0x100;
return $byteVal;
}
Example of use
$test = signByteToInt(16); //16
$add = signByteToInt(131); //-125
echo $test+$add; //-109

Why 64 times bit shift doesn't give me 0?

I'm reading php_architects.zend.php_.5.certification.study.guide.2006 and want to play with bit shifts. I have Ubuntu 64bit. And here is my code:
$x = 1;
echo ($x << 64) . "\n";
echo $x * pow(2, 64) . "\n";
The output of this script is:
1
1.844674407371E+19
The second result is float due to type conversion, it's clear. But for first one I would expect 0 value.
Why I'm getting 1 instead of 0?
UPD
Made one more test:
echo ($x << 67) . "\n";
Gives:
8
Looks like cycling, but I would expect bit removal.
In plain english you are moving the '1's to the left.
So $x is 0001 and $x << 1 is 0010. When you do this 64 times you turn were you started 0001.
You are just moving the bits, never removing, so you will never get zero.
More info at http://php.net/manual/en/language.operators.bitwise.php

Modified PHP MD5 gives different hashes

I have a modified MD5 hash function which I am using in PHP and VB.NET. When I run the PHP code on my local server (WAMP) I get a different result to the VB version. I have tried running the script on phpfiddle which gives the same result as the VB version.
I am thinking the problem could lie with my PHP settings on the WAMP server?
If I run the script below on my PC running WAMP the result I get is:
e5c35f7c3dea80fc68a4031582f34c25
When I run the exact same script on phpfiddle or php sandbox the result I get is (this is the expected result):
6337a43e8cd36058e80ae8cb4f465998
Setting aside for a moment the fact that what you are doing here sounds like a bad approach what ever the actual problem is that you are trying to solve, here is a direct answer to the question.
As I already outlined in a comment above, the root cause of the problems you are having is that PHP has no concept of unsigned integers, and it handles this by converting numbers that overflow the bounds of an integer to floating point (which doesn't play nice with bitwise operations). This means that, on 32-bit systems, your code won't work correctly, as MD5 works with unsigned 32-bit integers.
You will need to ensure that your code is "binary safe" - so that all numbers are represented as if they were unsigned 32-bit integers.
To do this you will need to re-implement the addition operator, and (with your current implementation) the bindec()/hexdec() functions. It's worth noting that your current approach to certain procedures is very inefficient - all that converting to/from hex strings, and places where binary is represented as ASCII strings - but I'll gloss over that for now while I show you how to quick-fix your current implementation.
Firstly let's take a look at the addition operation:
private function binarySafeAddition($a, $b)
{
// NB: we don't actually need 64 bits, theoretically we only need 33
// but 40 bit integers are confusing enough, and 33 bits is unrepresentable
$a = "\x00\x00\x00\x00" . pack('N', $a);
$b = "\x00\x00\x00\x00" . pack('N', $b);
$carry = $a & $b;
$result = $a ^ $b;
while ($carry != "\x00\x00\x00\x00\x00\x00\x00\x00") {
$shiftedcarry = $this->leftShiftByOne($carry);
$carry = $result & $shiftedcarry;
$result ^= $shiftedcarry;
}
return current(unpack('N', substr($result, 4)));
}
private function leftShiftByOne($intAsStr)
{
$p = unpack('N2', $intAsStr);
return pack('N2', ($p[1] << 1) | (($p[2] >> 31) & 0x00000001), $p[2] << 1);
}
private function add()
{
$result = 0;
foreach (func_get_args() as $i => $int) {
$result = $this->binarySafeAddition($result, $int);
}
return $result;
}
The real nuts-and-bolts of this routine is shamelessly stolen from here. There's also a helper function to perform the left-shift, because PHP doesn't let you left-shift strings, and a convenience wrapper function, to allow us to add an arbitrary number of operands together in a single clean call.
Next lets look at the bindec() and hexdec() replacements:
private function binarySafeBinDec($bin)
{
$bits = array_reverse(str_split($bin, 1));
$result = 0;
foreach ($bits as $position => $bit) {
$result |= ((int) $bit) << $position;
}
return $result;
}
private function binarySafeHexDec($hex)
{
$h = str_split(substr(str_pad($hex, 8, '0', STR_PAD_LEFT), -8), 2);
return (hexdec($h[0]) << 24) | (hexdec($h[1]) << 16) | (hexdec($h[2]) << 8) | hexdec($h[3]);
}
Hopefully these are reasonably self explanatory, but feel free to ask about anything you don't understand.
We also need to replace all those 0xffffffff hex literals with a binary safe implementation, as these will also result in a float on 32-bit systems. Here is a safe way to get the right-most 32 bits set in an integer, that will work on 32- and 64-bit systems:
private $right32;
public function __construct()
{
$this->right32 = ~((~0 << 16) << 16);
}
There's one other method we need to re-implement, and that's rotate(). This is because it uses a right-shift, and this shifts a copy of the sign bit on from the right. This means that the left-hand side of the rotated block will end up with all it's bits set, and this is obviously not what we want. We can overcome this by creating a number with only the target bits for the right-hand side set, and ANDing the right-hand side operand with it:
private function rotate ($decimal, $bits)
{
return dechex(($decimal << $bits) | (($decimal >> (32 - $bits)) & (~(~0 << $bits) & $this->right32)));
}
When you put all this together you come up with something like this, which works for me on 32- and 64-bit systems.

Getting different results in PHP and JS when bit shifting

I'm getting some odd results where 2 identical functions (one in PHP and one in javascript) are returning different results.
The input for both of these lines of code is identical:
a = 4653896912;
b = 13;
I have double checked the variable types and both variables are numbers in JS and integers in PHP.
The line of code for PHP is this:
$a = $a >> $b;
For Javascript it's this:
a = a >> b;
You'd expect a to have the same value after both, but I'm getting the following:
PHP: $a = 568102
JS: a = 43814
Which has completely baffled me at this point.
Turns out this is definitely an issue of PHP using 64 bit integers and JS only using 32 bit. The problem I face now is that I need to get PHP to use 32-bit integers for these calculations. I found a function someone else wrote that looks like it should work, but it doesn't seem to be changing the output at all for me.
private static function toInt32(&$x) {
$z = hexdec(80000000);
$y = (int) $x;
if($y ==- $z && $x <- $z){
$y = (int) ((-1) * $x);
$y = (-1) * $y;
}
$x = $y;
}
The below code demonstrates masking the upper 32 bits of the number to retrieve only the lower 32 bits to use in your calculations. 4294967295 is 2^32 - 1. I think that if you mask all values that could be greater than 32 bits in this manner, then you can get the same results from your php and javascript.
<?php
$php_a = 4653896912;
$php_b = 13;
//convert $php_a into a 32 bit val
$php_a = $php_a & 4294967295;
$a = $php_a >> $php_b;
echo "PHP: \$a = $a <br />";
?>
<script type="text/javascript">
var a = 4653896912;
var b = 13;
var a = a >> b;
alert('Javascript A value is ' + a);
</script>
4653896912 is more than 32 bits.. unpredictable results are likely. I get $a = 43814 for PHP, but that is actually 358929617 >> 13, so in all likelihood PHP is doing 64 bit operations but JavaScript is only 32 bit.
I believe it's because you're a is above the limit of a 32-bit signed integer for [PHP][1].
The highest value possible is about 2 million, and a is over 4 billion.
When you're rolling over because of space limitations, results can be unpredictable (or at least, very difficult to figure out).
If your server is on a 64-bit version of PHP then it'll max out much higher than than, but javascript is limited by what the end-user is running.
You can read up on PHP on their integers page.

Categories