2

I have no idea why i get two different results from the same function, When i use it in local host i get a result and when i upload file to my host i get different result. I test it, i give it the same word and i get two different results. it's a function from CAPTCHA library: Captcha library

function rpHash($value) {
    $hash = 5381;
    $value = strtoupper($value);
    for($i = 0; $i < strlen($value); $i++) {
       $hash = (($hash << 5) + $hash) + ord(substr($value, $i));
    }
    return $hash;
}
ler
  • 1,466
  • 3
  • 20
  • 37
  • Give us the $value and the two different resulting $hash. What is your encoding on both machines?.Try `mb_strtoupper()` and specify the encoding. – mseifert May 11 '16 at 02:11
  • @mseifert i give it this word bababa in localhost i get -1565213183 and in live host i get 1149486022145 – ler May 11 '16 at 02:16
  • On a 64-bit system, take those two values, convert to binary, then take the last 32 bits of the string. You get `10100010101101001011111000000001` in both cases. – Niet the Dark Absol May 11 '16 at 03:33
  • @NiettheDarkAbsol what's the solution then – ler May 11 '16 at 03:34
  • Depends what you want. – Niet the Dark Absol May 11 '16 at 04:05
  • You can shift only 3 places `($hash << 3)` and stay within the 16 bit space. This should give you identical results on 16 and 32 bit servers. If this is not acceptable, you will have to manipulate the hash value and work with upper and lower 16 bits separately and store as a string. You can see the posts [on binary shifting](http://stackoverflow.com/questions/141525/what-are-bitwise-shift-bit-shift-operators-and-how-do-they-work) and [how to have 64 bit integer on PHP?](http://stackoverflow.com/questions/864058/how-to-have-64-bit-integer-on-php). Which direction you go depends on what you need – mseifert May 11 '16 at 05:28
  • @mseifert i have no idea what you mean. if you know how can i get the same result on live host as what i get in localhost please show me what to change in my code – ler May 11 '16 at 05:38

2 Answers2

2

The problem is that your localhost is 32bit and your host is 64bit. To understand why, read the accepted answer on this post. To summarize:

The << operator does a Left shift

Integers are stored, in memory, as a series of bits. For example, the number 6 stored as a 32-bit int would be represented in base 2 as:

00000000 00000000 00000000 00000110

Shifting this bit pattern to the left one position (6 << 1) would result in the number 12 and represented in base 2 as:

00000000 00000000 00000000 00001100

As you can see, the digits have shifted to the left by one position, and the last digit on the right is filled with a zero.

When you shift left, you are increasing the number by an order of ^2 for each shift.

On a 32 bit system, the maximum integer you can have is 2,147,483,647 (which is 01111111 11111111 11111111 11111111 (there are 31 bits available). That is how much room you have to shift left before you run out of room to store the number.

Your starting hash of 5381 is 13 bits long Your function shifts FIVE bits for each letter in the $value. You get to 31 bits after 3 letters (3*5) + 13 = 28

So you can only have 3 letters max in your CAPTCHA before it breaks. Your OPTIONS:

  1. Restrict your CAPTCHA to 3 characters or less and use the existing function.

  2. Shift using $hash << 3. This will get you 5 characters

  3. Shift using $hash << 2. This will get you 7 characters

  4. Use a different method

For example:

function rpHash($value) {
    $hash = 5381;
    $value = strtoupper($value);
    for($i = 0; $i < strlen($value); $i++) {
       $hash = (($hash << 2) + $hash) + ord(substr($value, $i));
    }
    return $hash;
}
mseifert
  • 5,390
  • 9
  • 38
  • 100
  • thank you so much i did $hash << 2 and it works cuz my captcha has 6 characters. – ler May 11 '16 at 15:07
1

It may have to do with whether or not the operating system is 32-bit or 64-bit. If you shift $hash too far left, you could end up with an overflow error, and wrap around to negative numbers.

The solution depends on your needs. If all you need is a positive integer, and it doesn't matter if the values are consistent across operating systems, just wrap the returned value with abs() to make sure the result is positive.

If you need the result to always be consistent, you'll probably need to truncate the result to be stored in 32 bits at most. Using something like:

$32bitresult = rphash($value) & 0xFFFFFFFF;

should work.

NOTE: You may want to add a check to see if the result of rphash is negative before AND-ing the two.

Take a look at this similar problem/answer:
Force PHP integer overflow

I haven't been able to test the code out, but maybe it'll help you on your way at least.

Community
  • 1
  • 1
badandyomega
  • 252
  • 1
  • 4
  • Judging by the values OP is getting (as per their comment), this is certainly it. – Niet the Dark Absol May 11 '16 at 03:32
  • i want to get in live host the same result as localhost . can you help me please with that. and i didn't understand what you mean by the NOTE. i did try what you wrote above but i still get two different results – ler May 11 '16 at 04:10