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?
Related
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
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.
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.
please i need some help in converting a python code to a php syntax
the code is for generating an alphanumeric code using alpha encoding
the code :
def mkcpl(x):
x = ord(x)
set="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
for c in set:
d = ord(c)^x
if chr(d) in set:
return 0,c,chr(d)
if chr(0xff^d) in set:
return 1,c,chr(0xff^d)
raise Exception,"No encoding found for %#02x"%x
def mkalphadecryptloader(shcode):
s="hAAAAX5AAAAHPPPPPPPPa"
shcode=list(shcode)
shcode.reverse()
shcode = "".join(shcode)
shcode += "\x90"*((-len(shcode))%4)
for b in range(len(shcode)/4):
T,C,D = 0,"",""
for i in range(4):
t,c,d = mkcpl(shcode[4*b+i])
T += t << i
C = c+C
D = d+D
s += "h%sX5%sP" % (C,D)
if T > 0:
s += "TY"
T = (2*T^T)%16
for i in range(4):
if T & 1:
s += "19"
T >>= 1
if T == 0:
break
s += "I"
return s+"\xff\xe4"
any help would be really appreciated ...
i will help you a little. For the rest of it, please read up on the documentation.
function mkcpl($x){
$x=ord($x);
$set="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$set=str_split($set);
foreach($set as $c){
$d=ord($c)^$x;
if( in_array( chr($d) ,$set ) ){
return array(0,$c,chr($d));
}
if ( in_array( chr(0xff^d) ,$set ) ){
return array(0,$c,chr(0xff^$d));
}
}
}
function mkalphadecryptloader($shcode){
$s="hAAAAX5AAAAHPPPPPPPPa";
# you could use strrev()
$shcode=str_split($shcode);
$shcode=array_reverse($shcode);
$shcode=implode("",$shcode);
# continue on... read the documentation
}
print_r(mkcpl("A"));
mkalphadecryptloader("abc");
Python: PHP
len() - length of string/array. strlen(),count()
range() - generate range of numbers for($i=0;$i<=number;$i++)
<< <<
the rest of them, like +=, == etc are pretty much the same across the 2 languages.
the rest of them, like +=, == etc are
pretty much the same across the 2
languages.
Careful; in PHP string concatenation is accomplished using .= not +=. If you try to use += PHP will try to evaluate the expression mathematically (probably returning a null) and you'll be pulling your hair out trying to figure out what's wrong with your script.
As a fun side-project for myself to help in learning yet another PHP MVC framework, I've been writing Reversi / Othello as a PHP & Ajax application, mostly straightforward stuff. I decided against using a multidimensional array for a number of reasons and instead have a linear array ( in this case 64 elements long ) and a couple methods to convert from the coordinates to integers.
So I was curious, is there any other, possibly faster algorithms for converting an integer to a coordinate point?
function int2coord($i){
$x = (int)($i/8);
$y = $i - ($x*8);
return array($x, $y);
}
//Not a surprise but this is .003 MS slower on average
function int2coord_2($i){
$b = base_convert($i, 10, 8);
$x = (int) ($b != 0 ? $b/8 : 0); // could also be $b < 8 for condition
$y = $b % 10;
return array($x, $y);
}
And for posterity sake, the method I wrote for coord2int
function coord2int($x, $y){
return ($x*8)+$y;
}
Update:
So in the land of the weird, the results were not what I was expecting but using a pre-computed lookup table has predominantly shown to be the fastest, guess trading memory for speed is always a winner?
There was a table with times here but I cut it due to styling issues with SO.
Oh yes! This is a perfect example of binary:
function int2coord($i){
$x = $i >> 3;
$y = $i & 0x07;
return array($x, $y);
}
The reality is that a good compiler will find this optimization and use it, so it's not necessarily faster. Test and see if your compiler/interpreter does this.
It works because any binary division by 8 is the same as a right shift by 3 bits. Modern processors have barrel shifters that can do up to a 32 bit shift in one instruction.
The reverse is as easy:
function coord2int($x, $y){
return ($x << 3)+$y;
}
-Adam
I don't have the time to measure this myself right now, but I would suspect that a pre-computed lookup table would beat your solution in speed. The code would look something like this:
class Converter {
private $_table;
function __construct()
{
$this->_table = array();
for ($i=0; $i<64; $i++) {
$this->_table[$i] = array( (int)($i/8), (int)($i%8) );
}
}
function int2coord( $i )
{
return $this->_table[$i];
}
}
$conv = new Converter();
$coord = $conv->int2coord( 42 );
Of course, this does add a lot of over-head so in practice you would only bother to pre-compute all coordinates if you conversion code was called very often.
I'm not in a position to measure right now, but you should be able to eke out some additional speed with this:
function int2coord($i){
$y = $i%8;
$x = (int)($i/8);
return array($x, $y);
}
edit: ignore me -- Adam's bitshifting answer should be superior.
function int2coord_3($i){
return array((int) ($i / 8), ($i % 8));
}
this is a little faster because there is no var declaration and affectation.
I think most of your performance is lost by returning array(...) at the end. Instead, I propose:
* define two functions, one for x and one for y
or
* inline the bit arithmetic in code needing the calculation