Weird $key => $value comparison with PHP - php

I'm facing a weird issue with PHP. This simple example:
<?php
$array = array(
'zero',
'one',
'id' => 'two'
);
foreach ($array as $key => $value) {
if ($key == "id") {
echo "Key: ". $key .", value: ". $value ."\n";
}
}
?>
should (imho) output this:
Key: id, value: two
But it outputs
Key: 0, value: zero
Key: id, value: two
How is this possible: 0 == "id"?

When $key is 0 and gets compared to the string "id", the string ("id") will be converted to an integer. Since "id" can't be turned into a valid integer that conversion will yield 0, and the if statement becomes true.
Since you don't want the implicit conversion to happen between two types who aren't compatible use the more strict === version of ==. === will see if the variables are of the same type and has the same exact value.
if ($key === "id") {
...
}
Documentation PHP: Comparison Operators
Examples
var_dump (0 == (int)"id");
var_dump ((string)0 == "id");
var_dump (0 === "id");
var_dump (1.0 === 1);
output
bool(true)
bool(false)
bool(false)
bool(false) # be careful!

You are being bitten by a process called type juggling.
Try the following:
var_dump(0 == "id");
It will output bool(true).
PHP is performing integer comparison, and when it attempts to convert the string "id" to an integer, the result is 0. PHP will happily parse leading digits of a string and stop at the first non-numeric value, yielding integer 123 for strings like "123xyz". Because there are no leading digits in the string "id", it is parsed as integer 0.
The solution is to use ===, which compares the value and type of two variables, without attempting to juggle the types of the operands.

This:
$key == 'id'
...will make PHP do integer comparison, since the lvalue is an integer.
If you're wondering why this:
if ($key) { ... }
...wouldn't give the same result, well it's because the lvalue here (while omitted) is boolean, equivalent to:
if (true == $key) { ... }
Therefore, PHP will attempt boolean comparison. You can use the === operator to force a type check.
You can refer to the Type Comparison Tables and the Comparison Operators Table.

If you set the logic expression to take into account vartype
if (key === "id")
if will work. Just like #refp says.

Related

Is this a PHP Bug? Run foreach over mixed array with an if

I was implementing the piwik api and I found unexpected behavior on my local copy of piwik. (The latest piwik version does not contain this piece of code anymore.)
Here is the bug:
<?php
$arrtest = array('label' => array(1,2,3), 0 => 'zero');
foreach($arrtest as $key => $value) {
if($key != 'label') {
var_dump($value);
}
}
?>
The given code should print string(4) 'zero' after skipping the 'label' key. But it does not print anything. if I replace the inner code with:
if($key === 'label') continue;
var_dump($value);
Then it prints: string(4) "zero"
Can anyone explain this?
Use strict comparison, always:
$key !== 'label'
With your original code $key != 'label', when 0 is compared to 'label', 'label' is actually coerced into a int, and because label does not start with a number, it is automatically coerced to 0, the default value of an int. You're now comparing 0 != 0, which of course is false.
Compare:
0 == 'label'; // true
0 === 'label'; // false
This is PHPs "unusual" type coercion rules in effect. In the loop instance you're interested in, $key is 0. Thus the comparison is if (0 != 'label'), comparing an integer to a string. In this instance, it will coerce the string to an integer using its inbuilt rules. This converts label to 0. So, 0 != 0 is the test, which fails.
As you've noticed, use type strict comparisons (which don't perform the type coercion), to avoid this.

foreach, unexpected result for element, which key is 0

I have this code
$arr = array(
"0"=>"http://site.com/somepage/param1/param2/0",
"1"=>"http://site.com/somepage/param1/param2/1",
"thispage" => "http://site.com/somepage/param1/param2/2",
"3"=> "http://site.com/somepage/param1/param2/3"
);
foreach ($arr as $k=>$v) {
if ($k == "thispage") {
echo $k." ";
}
else {
echo ''.$k.' ';
}
}
Its surprise, for first element "0"=>"http://site.com/somepage/param1/param2/0", not created link, (for other elements works fine)
If replace first element key 0 on something other, for example 4, now links created. What is wrong ?
This is happening because 0 == "thispage" and the first key is 0. To find out more about this, take a look at the PHP manual page about Type Juggling.
Use === ("is identical to") instead of == ("is equal to"), because 0 is equal to "thispage", but not identical.
This is what happens with ==:
$key takes the integer value of 0
PHP tries to compare 0 == "thispage"
in order to make the comparison, it needs to cast "thispage" to integer
the resulting comparison is 0 == 0, which is true
If you use ===:
$key takes the integer value of 0
PHP tries to compare 0 === "thispage"
since 0 is of a different type (integer) than "thispage" (string), the result is false
This is What you are doing wrong.
if ($k === "thispage") {
echo .$k." ";
}
Do the:
if ($k === "thispage")
You have to use identical comparison operator === as equal comparison operator won't help here, because
If you compare a number with a string or the comparison involves
numerical strings, then each string is converted to a number and the
comparison performed numerically.
thispage converted to number will return 0, so your if statement will match if you use equal comparison operator ==. When you do identical comparison === if type does not match it returns false.
You can read about comparison operators here.
Try this:
if ($k === "thispage") {
echo $k." ";
}
http://us.php.net/manual/en/language.types.array.php:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").
So in your case Stings "1", "2" and "3" are treated as integers.
To fix this use the === operator that check for type along with value.
The reason for the result you see is the comparison operator you use. == is too imprecise sometimes and can result in wierd things like this. Using the === will compare the values for exactness and will prevent the issue you have.
so:
foreach ($arr as $k=>$v) {
// this is the important thing
if ($k === "thispage") {
echo $k." ";
}
else {
echo ''.$k.' ';
}
}

Why does 1234 == '1234 test' evaluate to true? [duplicate]

This question already exists:
Closed 10 years ago.
Possible Duplicate:
php == vs === operator
An easy answer for someone I'm sure. Can someone explain why this expression evaluates to true?
(1234 == '1234 test')
Because you are using the == (similarity) operator and PHP is coercing the string to an int.
To resolve it use the === (equality) operator, which checks not only if the value is the same, but also if the data type is the same, so "123" string and 123 int won't be considered equal.
In PHP (and JavaScript -- which has slightly different behavior), the comparison operator == works differently than it does in strongly-typed languages like C or Java. The === operator has the behavior that you most likely expect. Below is a breakdown of the two comparison operators as they apply to PHP.
==
This operator is officially known as the "equality" operator, though that doesn't really fit the normal definition of the word "equality". It does what is known as a type-juggling comparison. If the types of both operands don't match (in your example, 1234 was an integer and 1234 test was a string), PHP will implicitly cast the operands to each others' types and test the equality of the newly-typed values as shown below:
<?php
var_dump( (int) 'hi' ); // int(0)
var_dump( (string) 0 ); //string("0")
var_dump( 'hi' == 0 ); // bool(true)
var_dump( (int) '1hi' ); // int(1)
var_dump( 1 == '1hi' ); // bool(true)
It has a counterpart (type-juggling) inequality operator, !=.
===
The === operator, known as the "identical" operator, performs a strict check of the value and type of both operands and does not perform any implicit casts. Therefore, "0" does not === 0 and "1234 test"does not === 1234.
<?php
var_dump( '1234 test' === 1234 ); // bool(false)
It has a counterpart (strict) inequality operator, !==.
Quirks
Note that the === operator has behavior on objects that is considered strange by some. Say we have class A and variables $a and $b as defined below:
<?php
class A {
public $property = 'default value';
}
$a = new A();
$b = new A();
You might expect var_dump($a === $b); to output bool(true). It will actually return false. When used upon objects, the operator actually checks if both operands are references to the same object. The == operator, in this instance, works by checking the properties of the objects, so $a == $b.
PHP Manual Links
Comparison operators
Type juggling
When casting a string to an integer, any numeric characters up to the first non-numeric character becomes the number. Thus '1234 test' becomes 1234 because space is not a numeric character.
Thus 1234 == '1234 test'
If you want to force a string comparison, you should cast to string:
''.(1234) == '1234 test' // implicit
(string) 1234 == '1234 test' // explicit
strval(1234) == '1234 test' // procedural
You are loosely comparing two different types of data (an integer and a string). PHP has a very detailed chart of how comparisons work in their system when using the loose comparison binary operator (==):
http://php.net/manual/en/types.comparisons.php
If you want to ensure that the types are also in sync, that is that they are both integers or both strings, use the strong type comparison operator (===).
Note that, when using this operator, this will also return false:
1234 === '1234'
If you are unsure of your types when comparing, you can couple the strong-type comparison with PHP typecasting:
$a = 1234;
$b = '1234';
if ($a === $b) { } // Will not fire, as it is false
if ((int)$a === (int)$b) { } // Will fire, as it is true
The double equals will tell php to parse an int from the string. The string will evaluate to the integer 1234. Use triple equals '===' to get exact comparison.
If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically
var_dump(0 == "a"); // 0 == 0 -> true

Foreach loop issues in php

Here is some code I have: (p just echos plus adds a newline)
foreach ($vanSteps as $k => $reqInfo)
{
p($k);
if ('van' == $k) { p('The key is the van, continue'); continue; }//continue if we reached the part of the array where van is key
//do stuff
}
and I'm getting this output:
0
The key is the van, continue
1
2
3
van
The key is the van, continue
Why does the if statement return true when the key is 0? This foreach loop handles logic that applies when the key == 0 (and any other key except if the key is 'van') and this messes up the logic because it's return true when key is 0.
Any help?
Thank you.
Use === for this comparison. When PHP compares string and integer it first casts string to integer value and then does comparison.
See Comparison Operators in manual.
In PHP 'van' == 0 is true. This is because when using == to compare a string and a number, the string is converted to a number (as described in the second link below); this makes the comparison internally become 0 == 0 which is of course true.
The suggested alternative for your needs, would be to use a strict equality comparison using ===.
See Comparison Operators and String conversion to numbers
In PHP, when you compare 2 types, it has to convert them to the same type. In your case, you compare string with int.
Internally this gets converted to
if((int)'van'==0)....
and then
if((int)'van'==1)....
(int)'any possible string' will be 0:) So you either have to manually convert the both values to the same type, or use === as a comparison operator, instead of the loose =.
An exception from this rule(as pointed out in the comments) would be if the string start with a number, or can be interpreted as a number in any way(1, 0002, -1 etc). In this case, the string would be interpreted as a number, diregarding the end of the non-numeric end-of-string
Take a look at http://php.net/manual/en/types.comparisons.php for more details.
This works fine:
$array = array(0=>"a",1=>"b","van"=>"booya!");
function p($v){ echo "{$v}<br />"; }
foreach ($array as $k => $reqInfo)
{
p($k);
if ('van' === $k) { p('The key is the van, continue'); continue; }//continue if we reached the part of the array where van is key
//do stuff
}
Output:
0
1
van
The key is the van, continue
Note the ===.
Read the Comparison with Various Types table
When one of the operand is number, the other operand is converted to number too. Since 'van' is non-numeric sting, it's converted to 0. You should use === operator in the case, which also checks the variable type
That's becuase 'van' == 0 (true).
Instead, you should use 'van' === 0 (false).
In short, use === instead of ==.
Its interpreting the 'van' as a boolean value (false) which 0 is equal to.
To check for exact matches in type and value in PHP you must use === instead of ==

Why the value being equalled to string in php

It is in this way:
$arr_val = array(0,1,'0','1');
foreach ($arr_val as $key){
echo ($key == "TEST")?"EQUALLED":"NOT EQUALLED"."<br>";
}
0 == "TEST" prints "EQUALLED"
1 == "TEST" prints "NOT EQUALLED"
'0' =="TEST" prints "NOT EQUALLED"
'1' =="TEST" prints "NOT EQUALLED"
When I say it prints the value "SELECTED". But why the above first case prints equalled. Any ideas on this please? How would this be equal to. We know the fix to do comparision with
(===) operator. But I am trying to know the reason why (0=="TEST") is true.
When you provide PHP's == operator with a mixture of numeric and string operands, PHP will attempt to convert the string to the matching numeric type, part of a process it calls "type juggling". In this case, the string "TEST" converts to integer 0 so your test is equivalent to 0 == 0 which is true.
PHP provides the === operator, for testing if the value and type of both operands are equal. So while 0 == "TEST" will evaulate to true, 0 === "TEST" will not, and neither will 0 === "0" or 0 === 0.0.
Note that when PHP converts a string to a number, it attempts to parse the string for a valid number. See intval for more information on how it does this. Had you'd written 0 == "1TEST", the expression would have evaulated to 0 == 1, or false.
In your second example, 1 == "TEST", the string "TEST" is again converted to an integer resulting in 1 == 0, which is false.
Your last two examples use string comparisons. There is no conversion involved, and the results are self-explanatory.
PHP provides a comprehensive breakdown of how variables of different type compare for equality.
Because 0 is an integer, behind the scenes, this is the comparison that happens:
0 == intval( "TEST" )

Categories