Strange behaviour of ++ operator in PHP 5.3 - php

Watch following code:
$a = 'Test';
echo ++$a;
This will output:
Tesu
Question is, why ?
I know "u" is after "t", but why it doesn't print "1" ???
PHP Documentation:
Also, the variable being incremented
or decremented will be converted to
the appropriate numeric data
type—thus, the following code will
return 1, because the string Test is
first converted to the integer number
0, and then incremented.

PHP follows Perl's convention when dealing with arithmetic operations on character variables and not C's. For example, in Perl 'Z'+1 turns into 'AA', while in C 'Z'+1 turns into '[' ( ord('Z') == 90, ord('[') == 91 ). Note that character variables can be incremented but not decremented and even so only plain ASCII characters (a-z and A-Z) are supported.
Source: http://php.net/operators.increment

In PHP you can increment strings (but you cannot "increase" strings using the addition operator, since the addition operator will cause a string to be cast to an int, you can only use the increment operator to "increase" strings!... see the last example):
So "a" + 1 is "b" after "z" comes "aa" and so on.
So after "Test" comes "Tesu"
You have to watch out for the above when making use of PHP's automatic type coercion.
Automatic type coercion:
<?php
$a="+10.5";
echo ++$a;
// Output: 11.5
// Automatic type coercion worked "intuitively"
?>
No automatic type coercion! (incrementing a string):
<?php
$a="$10.5";
echo ++$a;
// Output: $10.6
// $a was dealt with as a string!
?>
You have to do some extra work if you want to deal with the ASCII ordinals of letters.
If you want to convert letters to their ASCII ordinals use ord(), but this will only work on one letter at a time.
<?php
$a="Test";
foreach(str_split($a) as $value)
{
$a += ord($value); // string + number = number
// strings can only handle the increment operator
// not the addition operator (the addition operator
// will cast the string to an int).
}
echo ++$a;
?>
live example
The above makes use of the fact that strings can only be incremented in PHP. They cannot be increased using the addition operator. Using an addition operator on a string will cause it to be cast to an int, so:
Strings cannot be "increased" using the addition operator:
<?php
$a = 'Test';
$a = $a + 1;
echo $a;
// Output: 1
// Strings cannot be "added to", they can only be incremented using ++
// The above performs $a = ( (int) $a ) + 1;
?>
The above will try to cast "Test" to an (int) before adding 1. Casting "Test" to an (int) results in 0.
Note: You cannot decrement strings:
Note that character variables can be incremented but not decremented and even so only plain ASCII characters (a-z and A-Z) are supported.
The previous means that echo --$a; will actually print Test without changing the string at all.

The increment operator in PHP works against strings' ordinal values internally. The strings aren't cast to integers before incrementing.

Related

Convert array of values into a single float value in PHP?

I have an array with these values (when the array is printed with print_r();
Array:
[0] => 66
[1] => 233
[2] => 204
[3] => 205
The values in hex are:
Array:
[0] => 0x42
[1] => 0xE9
[2] => 0xCC
[3] => 0xCD
What I'm looking to do is to turn this 4 byte array into a float value. If I use implode(); to turn the array into a value, it just combines the string into 66233204205 instead of 0x42E9CCCD which are not similar. Thus I can't use floatval(). PHP is new to me, and so is using string values instead of the actual bits, like I can in C.
What I'm thinking is to some how implode() it with the hex values, instead of those integer numbers, and then use floatval().
Any ideas guys?
EDIT:
Just so it's a little clearer, I should be obtaining 116.900 as the result
You have to do a simple math operation to concatenate hex values of the array one after the other. The algorithm would be like this:
Assign the first hex value of the array to a resultant variable, $concat in this case.
Use a for loop to loop through the array from 2nd element till nth element
In each iteration of the loop left shift 8 times the existing hex value of the resultant variable and place the new hex value in the least significant 8 bits of the resultant variable.
// Suppose $array is your original array
$concat = $array[0];
$count = count($array);
for($i = 1; $i < $count; $i++){
$concat = ($concat << 8) + $array[$i];
}
// display concatenated hex value: 42e9cccd
var_dump(dechex($concat));
// Now do your operation on the concatenated hex value
Here's a demo, https://eval.in/844793
Revised Answer with ....
Performing math with hex strings used to be a feature supported in PHP. Now with PHP 7, a hex string only represents a string of characters and no longer is recognized as containing a numeric value. If you attempt to do math with it, the result is zero. Consider the following code:
<?php
$arr = [66, 233, 204, 205];
$res = array_reduce( $arr, function($c,$i) {
$c.=dechex( $i );
return $c;
});
$temp = "0x" . $res; // 0x42e9cccd
var_dump($temp + 0);
See demo
This code attempts to provide the hex string a mathematical context by adding zero to the value contained in $temp. This code works until PHP 7 because the powers that be determined that hexstrings created more problems than they were worth; see this RFC and the Manual:"Hexadecimal strings are no longer considered numeric".
Concatenation, being a string operation, creates the example's hex string whose direct usage proves unwise in a math operation. A notice will be emitted (in PHP 7.1), complaining as follows:
Notice: A non well formed numeric value encountered
You may suppress displaying this notice, but the resulting sum will be zero in PHP 7. When the code functions correctly in PHP 5.6, the result of 1122618573 seems wrong, certainly far too large to cast as a float and obtain the value that the OP seeks.
... A Bona Fide Work-Around
<?php
$arr = [66, 233, 204, 205];
$res = array_reduce( $arr, function($c,$i) {
$c.=dechex( $i );
return $c;
});
$temp = "0x" . $res;
$int = filter_var( $temp, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX );
if (false === $int) {
throw new Exception("Invalid integer!");
}
$arr = (unpack('f', pack('i', $int )));
$f = array_pop($arr);
printf("%.3f",$f);
See demo
PHP will recognize the numeric hex string that array_reduce() yields if you use filter_var() with the indicated parameters. In this fashion, you may obtain an integer evaluating as 1122618573. The key thing rather than the integer's value is its binary bit pattern. Borrowing from the official answer here, the code needs to pack $int into a binary string, which it subsequently will unpack as a float -- almost. That result will be returned as an array with just one element. After popping off and capturing that element's float value, printf() displays 116.900.
This appends the values of the array to eachother (in hexadecimal). PHP's dechex() function.
http://php.net/dechex
dechex — Decimal to hexadecimal
$b = [66,233,204,205];
$a = dechex($b[0]);
for($x = 1; $x < count($b); $x++) {
$a = $a . dechex($b[$x]);
}
echo $a; // $a = 42e9cccd
You didn't specify if your array represents an integer, if is the integer part of the floating point value, or is the entire number represented in IEEE 754 format.
Anyway, I would suggest you to take a look at the "pack" function.
$value = pack('i', your_value);
HERE you can find the documentation: basically you have to provide the type you want to obtain, along with your value(s), of course.
Also PHP is NOT a strongly typed language, so you don't have to distinguish integer from floats, in this case. You can treat integer like floats, and viceversa. But if you want to be 100% sure, just do something like this:
$value = floatval(pack('i', your_value));
This is, of course, machine dependent, but I don't know of any machine running PHP that doesn't use IEEE 754 floats.

PHP- Maintain leading 0 in number [duplicate]

I need to add numbers in php without changing the number format like below
$a = "001";
$b = "5";
$c = $a+$b;
Now the result comes like "6" but I need "006" if $a is "01" then the result should be "06".
Thanks
Technically speaking, the $a and $b in your example are strings - when you use the addition operator on them they converted to integers which can't retain leading zeroes. More details on string-to-number conversion are in the manual
Something like this would do it (assuming positive integer strings with leading zeros)
#figure out how long the result should be
$len=max(strlen($a), strlen($b));
#pad the sum to match that length
$c=str_pad($a+$b, $len, '0', STR_PAD_LEFT);
If you always know how long the string has to be, you could use sprintf, e.g.
$c=sprintf('%03d', $a+$b);
Here, % introduces a placeholder, 03 tells it we want zero padded to fill at least 3 digits, and d tells it we're formatting an integer.
Hope this would help you:
<?php
$a="001";
$b="5";
$l=max(strlen($a),strlen($b));
$c=str_pad($a+$b, $l,"0", STR_PAD_LEFT);
echo $c;
?>
For common case. Your code should looks like this.
$a = someFormat($original_a);
$b = someFormat2($original_b); // $b has different format.
$c = someFormat($a + $b);
Or, you need write formatRecognition function.
$a = getValueA();
$b = getValueB();
$c = someFormat(formatRecognition($a), $a + $b);

Concatenation-assignment in PHP

I am learning PHP and I just read about Assignment Operators and I saw this
$a .= 5 which means $a equals $a concatenated with 5. To test this I coded a simple script
<?php
$a = 12345;
$a .=6;
$b = 12345;
$b .=006;
$c = 12345;
$c .=678;
echo " a=$a and b=$b c=$c" ;
?>
the output was a=123456 and b=123456 c=12345678.My question is why the b isn't equal with 12345006? Is it because treats 6 == 006?
Because 006 is treated as the octal number 6, which is the converted to the string "6", and concatenated to "12345" (which is the number 12345 converted to a string).
Use $b .= "006", and the result will be 12345006.
Because numbers with leading zeroes are treated as octals. if the concatenation would've included the leading zeroes, then 006 would've been interpretted as a string, which makes no sense, because it hasn't got quotes around it.
If you want 006 to be treated as a string, write it as one: '006'. Leave it as is, and it'll be interpreted as an octal:
$b = $a = 123;
$a .= 8;
$b .= 010;//octal
echo $a, ' === ', $b, '!';//echoes 1238 === 1238!
Just as an asside: yes, those are comma's/. echo is a language construct to which you can push multiple values, separated by comma's. The upshot of using comma's is that the values aren't concatenated into a single string before being pushed to the output stream.
This means that it is (marginally faster). The downsides: there aren't any AFAIK.
In case you're interested in the internals of PHP, I've explain this a bit more detailed here...
Just try b as a string:
$b.= '006';
Then you will get output 12345006.

Why is hash_hmac making different hashes for these two large numbers?

Why are hmac values different ? $a is equal to $b. I'm not sure what kind of error is causing this or if my logic is correct. Hmac_hash should convert any input to string to byte or hex and do it's job, tell me if I'm wrong.
Below example code.
Thank you.
<?php
$a = 1234567890000*1234567890000;
$b = gmp_mul("1234567890000","1234567890000");
$b = gmp_strval($b);
echo $a."<br/>".$b."<br/>";
$c = 2525;
echo ($a==$b)."<br/>";
echo hash_hmac("SHA512",$a,$c);
echo "<br/>";
echo hash_hmac("SHA512",$b,$c);
?>
Result
1.5241578750191E+24
1524157875019052100000000
1
973967436d3562150d60769b70a9010db21f89b114b9897430663dd195115b7893b9a6bd0e141cee301d23e2229afd6d39546630cd38f6fe7842073749f9ce3d
8bd1e7c837efaf8d51ed2d40354a020ee3c9663aa6bd4425c02b21ed02343185070216d37e54478ee78332af8e6fdecd51445fcee8cb04d4e51e4cc31283f9a9
When you compare $a and $b, the string inside $b is converted into an integer. Because the value is larger than what PHP can represent as an integer, it gets silently converted into a float instead. $a is a float. The floats are equal, which is why the compare succeeds.
hash_hmac converts the float in $a to a string instead of converting the string in $b into an integer which then overflows into a float. This is why the hashes differ.
If you want the compare to fail so that the behavior between it and hash_hmac is consistent, you'll want to cast $a into a string first or will want to use === to avoid converting data types.

php reference problem

<?php
$a = array("ABC","DEF");
foreach($a as &$b){
$b++;
echo $b . "<br />";
}
?>
Can any body explain me why the above code snippet generate the following output?
output:
ABD
DEG
Thanks in advance
It is explained in php manual
PHP follows Perl's convention when dealing with arithmetic operations on character variables and not C's. For example, in Perl 'Z'+1 turns into 'AA', while in C 'Z'+1 turns into '[' ( ord('Z') == 90, ord('[') == 91 ). Note that character variables can be incremented but not decremented and even so only plain ASCII characters (a-z and A-Z) are supported.
And if you mean why $b has changed:
You pass reference to $b in foreach loop, so assignments, increases etc. are done on reference to $b not on value that $b represents.
If you use ++ on a string it is "counted up": the next char in the alphabet is used. Remove the $b++; and your output will be "ABC DEF"
Good explanations here
hint:
If you call $a[0][1], it would return "B".

Categories