What is this bitwise OR doing in this weird array key construct? - php

Can someone explain to me what this means?? I have never seen this construct - taken from the Prestashop doc
foreach ( $languages as $language )
{
echo '<div id="test_' . $language['id_lang'|'id_lang'] .... // <-- What the??
// ...
}
$language contains the following keys:
Array
(
[id_lang] => 1
[name] => English (English)
// and others...
)
The result is that it takes the value of $language["id_lang"] - 1. But I don't understand the syntax and can't find any documentation about it.

This php -a session shows that it's totally meaningless:
php > $value = 'something'|'something';
php > echo $value;
something
php > $arr = array('abc' => 1, 'def' => 2);
php > echo $arr['abc'|'abc'];
1
php > echo $arr['def'|'def'];
2
Basically, if you "bitwise or" anything by itself, you get the original value. This property is called idempotence in mathematics. For further info, read:
http://en.wikipedia.org/wiki/Idempotence
http://en.wikipedia.org/wiki/Bitwise_operation#OR
Honestly, the original author of that code had no idea what they were doing.

What that does is use the bitwise operator on the ASCII values of the characters in the string "id_lang", although why they are doing this is beyond me, since the result is always going to be the same.
To elaborate a little bit, let's say (for convenience) that we're using ASCII, where each character is encoded as a single byte. Let's look at what happens when it does the comparison for the binary representation of the first character (i is 105, which in binary is 01101001):
"i": 01101001
OR "i": 01101001
___________
= 01101001
= "i"
0|0 is 0, 1|1 is 1, so inevitably all bits remain unchanged.

It's not doing anything, strangely enough.
var_dump('id_lang'|'id_lang');
#=> string(7) "id_lang"
http://ideone.com/zXdRMO
Even if it was doing something, using a bitwise operator on a string-based array key certainly feels like code smell to me.

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.

How come ('-' == 0) === true?

I was working here with some exploded string walks:
array_walk($data, function(&$value)
{
// Transform numerics into appropriate type numbers
if (is_numeric($value))
{
$value = substr_count($value, '.') == 1 ? (float) $value : (int) $value;
}
// Transform dashes into nulls
if ($value == '-')
{
$value = null;
}
});
to transform values into their appropriate types, and some special character handling.
Where I stumbled upon an interesting, huh, bug?
The Bug
I was amazed, that each entry, that had it's initial value as string(1) '0' ended up being a null.
At first, I thought that the problem relies in (float) and (int) typecasts, though, after debugging:
var_dump((float) '0', (int) '0');
I saw that's not the case, getting the expected result:
float(0)
int(0)
It took me a while, to attempt to debug the, what at the moment appeared to be an obvious, weak type check, but, once I did, I was shocked:
var_dump('-' == 0);
The above expression appears to be:
bool(true)
Now, while writing, I thought I should debug some more, so:
var_dump( '=' == 0 );
var_dump( 'What a lovely nonsense?' == 0 );
var_dump( 0 == 'DAFUQ?' ); // maybe it's because of order? It's PHP, world of miracles, you know...
And, every expression listed above is bool(true).
Okay, maybe that's because internally, mystically PHP casts the expression into a (bool)?
var_dump( (bool) '-' == 0 );
No:
bool(false)
So, so, so...
I made a test-case here: http://codepad.org/smiEvsDj
The problem exists in 5.2.5 (codepad), also in 5.4.3 (friend) and also in 5.4.17 (my actual environment).
What is the reason behind this feature / bug / what-the-F-actually-is-this?
You have stumbled upon one of the major complaints that people have about PHP as a language: The fact that the "==" operator is not transitive.
Any string "foo" == TRUE, because the PHP people wanted this to work:
if ($string) {
// do something if $string is set
}
Yet, converting a string to a number (which PHP always tries to do when you use "=="), "foo" == 0!
Of course, TRUE != 0. That is a major pain when dealing with PHP, it's not logical, but it's reality.
It's trying to parse numbers from your strings, not finding any digits and automatically reverting to 0. I think...
e.g. it sees 'What a lovely nonsense?' == 0, sees that you're comparing integers, and tries to convert What a lovely nonsense? to an integer. As there are no numerical digits it defaults to 0, and believes that LHS == RHS so returns true

Using Eval to perform if comparison of strings in PHP

I have some code that looks like this.
eval('$ruleTrue = '."{$value} {$operator} {$value2};");
I am pulling mostly numeric values from a database and comparing them with other numeric values. The operator comes from a database as well. Possible operators are <,>,==.
Well when comparing ints and floats this works perfectly. BUT when comparing strings it breaks. For instance..
WORKS:
5 > 4
$ruleTrue = true
Doesn't Work Right:
John-Adams == Alice
$ruleTrue = true <--- WHY? Because John is not == to Alice.
For some reason my $ruleTrue variable is being returned as true when comparing strings.
You're trying to evaluate this code:
$ruleTrue = John == Alice;
John and Alice aren't strings, they're undefined constants. You want to put quotes around them. But be careful, because if your users are able to edit those fields in your database, they could find a way of unquoting the strings and executing their own php code, which could be disastrous. Eval is very unsecure that way, and you probably shouldn't be using it.
The expression John-Adams == Alice parses somewhat like (John - Adams) == Alice. The left side is trying to subtract two strings (undefined constants are considered 'barewords', and are equal to a stringification of their names; John === 'John', for example), and in order to make sense of such an odd operation, PHP turns both strings into numbers. As integers, both strings have a value of 0, so the left side equates to 0.
An int.
Now, when PHP wants to compare with ==, it wants to coerce both sides into the same type. In this case, it's converting to int. Alice also converts to 0. Both sides being 0, they're "obviously" equal.
In order to prevent this from happening, you should probably put quotes around your values. You might also consider using the strict equals operator (===), unless you really want that type coercion magic.
Alternatively, if you have a known set of operators, you can eliminate eval and make this safer and more robust all around, by making a comparison function that has sub-functions for the operators. Like so:
function compare($value1, $op, $value2) {
static $known_ops = array(
'==' => function($a, $b) { return $a == $b; },
'!=' => function($a, $b) { return $a != $b; },
...
# you can even make up your own operators. For example, Perl's 'eq':
'eq' => function($a, $b) { return "$a" === "$b"; }
...
);
$func = $known_ops[$op];
return $func($value1, $value2);
}
...
$ruleTrue = compare($value, $operator, $value2);
Now you don't have to worry about your values. You do have to worry about $operator, but that's only an issue if you let a user input it without you validating it. In which case, you may want to throw an exception or something if $op wasn't in $known_ops, cause if you leave PHP to handle it, you'll likely get a fatal error when it tries to call null.
Make sure, if the value are strings, they are surrounded with "quotes"

Weird PHP String Integer Comparison and Conversion

I was working on some data parsing code while I came across the following.
$line = "100 something is amazingly cool";
$key = 100;
var_dump($line == $key);
Well most of us would expect the dump to produce a false, but to my surprise the dump was a true!
I do understand that in PHP there is type conversion like that:
$x = 5 + "10 is a cool number"; // as documented on PHP manual
var_dump($x); // int(15) as documented.
But why does a comparison like how I mentioned in the first example converts my string to integer instead of converting the integer to string.
I do understand that you can do a === strict-comparison to my example, but I just want to know:
Is there any part of the PHP documentation mentioning on this behaviour?
Can anyone give an explanation why is happening in PHP?
How can programmers prevent such problem?
If I recal correcly PHP 'casts' the two variables to lowest possible type.
They call it type juggling.
try: var_dump("something" == 0);
for example, that'll give you true . . had that bite me once before.
More info: http://php.net/manual/en/language.operators.comparison.php
I know this is already answered and accepted, but I wanted to add something that may help others who find this via search.
I had this same problem when I was comparing a post array vs. keys in a PHP array where in my post array, I had an extra string value.
$_POST["bar"] = array("other");
$foo = array(array("name"=>"foobar"));
foreach($foo as $key=>$data){
$foo[$key]["bar"]="0";
foreach($_POST["bar"] as $bar){
if($bar==$key){
$foo[$key]["bar"]="1";
}
}
}
From this you would think that at the end $foo[0]["bar"] would be equal to "0" but what was happening is that when $key = int 0 was loosely compared against $bar = string "other" the result was true to fix this, I strictly compared, but then needed to convert the $key = int 0 into a $key = string "0" for when the POST array was defined as array("other","0"); The following worked:
$_POST["bar"] = array("other");
$foo = array(array("name"=>"foobar"));
foreach($foo as $key=>$data){
$foo[$key]["bar"]="0";
foreach($_POST["bar"] as $bar){
if($bar==="$key"){
$foo[$key]["bar"]="1";
}
}
}
The result was $foo[0]["bar"]="1" if "0" was in the POST bar array and $foo[0]["bar"]="0" if "0" was not in the POST bar array.
Remember that when comparing variables that your variables may not being compared as you think due to PHP's loose variable typing.

PHP: Faster to check if variable is integer or starts with #

I'm developing a module with some arrays in it.
Now my array contains:
$omearray = array (
'#title' = 'title',
0 = array ( 'another array', '2ndvalue', ),
);
foreach($omearray as $key => $value)
When I load the array to display I want to check whether the $key value is a 0, 1, 2, 3, etc. or a $key value that starts with a #.
Which would be better to use: checking if the value is_int() or a regex that checks whether the first character is a #?
EDIT: checking stringpos 1 is # vs is_int() since regex seems to be slow.
Since if ($key[0]=='#') is faster and is_int() is exhaustive, and || is a short circuit operator with left associativity (evaluates from left to right, see example 1 on http://php.net/manual/en/language.operators.logical.php) I would say:
if ($key[0]=='#' || is_int($val)) /* etc */
Because this way you only need to bother about using the # key naming self made convention with the keys you'll compare so much that you'd benefit from an optimization.
But unless you're making a huge number of evaluations I would stick to just if(is_int($val)), because it's clearer and less messy.
I would check it using if($key[0]=="#")
You can also check if the $value is an array (is_array($value)), in this case you dont need either regex,is_int, and # char.
PS: # character means (somewhere) "I'm a number/ID"
if (is_int($val) || $val{0} == '#') ...
I would go for the is_int(), because you are not dependent on the string. It can be anything and your code will still just take the integer indeices. Looking for # as the first character will prevent that flexibility. But if you are absolutely sure that the string's first character will always be a #, than the $Key[0] == '#' will be the fastest option.
You haven't given much insight into your actual data or reason for choosing this structure, but depending on that info, altering your structure so that you aren't inter-mixing integer indexes with hash keys may be an option:
$omearray = array(
'#title' => 'title',
'foo' => array(
'anotherarray',
'2ndvalue'
)
);
foreach ($omearray as $key => $value) {
if ($key == 'foo') {
// do something with $value, which is the 2nd array, numerically indexed
}
}
Apologies if this solution doesn't suit your needs.
You can check if string is integer
or if it starts with #
or both
or whatever.
It's all makes not a slightest difference.
It is not a part of your code that may affect any performance issue ever.

Categories