Php, in_array, 0 value - php

I was trying to understand the in_array behavior at the next scenario:
$arr = array(2 => 'Bye', 52, 77, 3 => 'Hey');
var_dump(in_array(0, $arr));
The returned value of the in_array() is boolean true. As you can see there is no value equal to 0, so if can some one please help me understand why does the function return true?

This is a known issue, per the comments in the documentation. Consider the following examples:
in_array(0, array(42)); // FALSE
in_array(0, array('42')); // FALSE
in_array(0, array('Foo')); // TRUE
To avoid this, provide the third paramter, true, placing the comparison in strict mode which will not only compare values, but types as well:
var_dump(in_array(0, $arr, true));
Other work-arounds exist that don't necessitate every check being placed in strict-mode:
in_array($value, $my_array, empty($value) && $value !== '0');
But Why?
The reason behind all of this is likely string-to-number conversions. If we attempt to get a number from "Bye", we are given 0, which is the value we're asking to look-up.
echo intval("Bye"); // 0
To confirm this, we can use array_search to find the key that is associated with the matching value:
$arr = array(2 => 'Bye', 52, 77, 3 => 'Hey');
echo array_search(0, $arr);
In this, the returned key is 2, meaning 0 is being found in the conversion of Bye to an integer.

Try adding a third parameter true (strict mode) to your in_array call.

This is a result of the loose comparison and type juggling.
Loose comparison means that PHP is using == not === when comparing elements. == does not compare that the two variable types are equal, only their value, while === will ensure that they match in type and value (e.g. compare 0 == FALSE and 0 === FALSE).
So, basically, your in_array function is checking:
0 == 'Bye'
0 == 'Hey'
0 == 77
Note that the 52 will get lost due to the way you created your array.
So, note that if you do:
print (0 == 'Bye');
You will get 1. Apparently, PHP is type juggling the 'Bye' to 0, which is the same thing that will happen when you cast a string to an int, e.g. (int) 'string' will equal 0. Specific reference from the String conversion to numbers doc (Emphasis added):
The value is given by the initial portion of the string. If the string
starts with valid numeric data, this will be the value used.
Otherwise, the value will be 0 (zero). Valid numeric data is an
optional sign, followed by one or more digits (optionally containing a
decimal point), followed by an optional exponent. The exponent is an
'e' or 'E' followed by one or more digits.
Apparently, the integer type takes precedence over the string type (i.e. it could just as easily be doing the comparison by casting the int 0 to a string, which would then return False). This is specified in the comparison operators doc:
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.
Thanks for an interesting question that led me to do some research and learn something new!

in_array is supposed to be used on indexed arrays ([0], [1], [2] etc), not with a dictionary as you have defined (key-value store).
If you want to check if your array $arr includes '0', try using the PHP function array_key_exists instead - http://php.net/manual/en/function.array-key-exists.php.
var_dump(array_key_exists(0, $arr));

Type compare (third parameter) needs more system resources and more time.
Simply do that:
$x=0;
$result=in_array($x.'',array('ABC','BAC','12c','54'));
var_dump($result);

Related

Why in PHP SWITCH with case 0 behaves such stupid? [duplicate]

I have the following piece of code:
$item['price'] = 0;
/* Code to get item information goes in here */
if($item['price'] == 'e') {
$item['price'] = -1;
}
It is intended to initialize the item price to 0 and then get information about it. If the price is informed as 'e' it means an exchange instead of a sell, which is stored in a database as a negative number.
There is also the possibility to leave the price as 0, either because the item is a bonus or because the price will be set in a later moment.
But, whenever the price is not set, which leaves it with the initial value of 0, the if loop indicated above evaluates as true and the price is set to -1. That is, it considers 0 as equal to 'e'.
How can this be explained?
When the price is provided as 0 (after initialization), the behavior is erratic: sometimes the if evaluates as true, sometimes it evaluates as false.*
You are doing == which sorts out the types for you.
0 is an int, so in this case it is going to cast 'e' to an int. Which is not parsable as one and will become 0. A string '0e' would become 0 and would match!
Use ===
From PHP.net:
Comparisons between strings and numbers using == and other non-strict
comparison operators currently work by casting the string to a number,
and subsequently performing a comparison on integers or floats. This
results in many surprising comparison results, the most notable of
which is that 0 == "foobar" returns true.
However this behavior was changed in PHP 8.0:
When comparing to a numeric string, PHP 8 uses a number comparison.
Otherwise, it converts the number to a string and uses a string
comparison.
PHP 7
0 == 'foobar' // true
0 == '' // true
4 == '4e' // true (4e is cast as a number and becomes 4)
PHP 8 converts numbers to strings before making comparisons
0 == 'foobar' // false
0 == '' // false
4 == '4e' // false ('4e' is considered non-numeric therefore 4 is cast as a string and becomes '4')
This is a major change therefore it was implemented in a new major PHP version. This change breaks backward compatibility in scripts that depend on the old behavior.
This is due to how PHP does the comparison operation that the == comparison operator denotes:
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. […] The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.
As the first operand is a number (0) and the second is a string ('e'), the string is also converted to a number (see also table Comparison with Various Types). The manual page on the string data type defined how the string to number conversion is done:
When a string is evaluated in a numeric context, the resulting value and type are determined as follows.
If the string does not contain any of the characters '.', 'e', or 'E' and the numeric value fits into integer type limits (as defined by PHP_INT_MAX), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
In this case the string is 'e' and thus it will be evaluated as a float:
The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e' or 'E' followed by one or more digits.
As 'e' does not start with a valid numeric data, it evaluates to float 0.
"ABC" == 0
evaluates true because first "ABC" is converted to integer and becomes 0 then it is compared to 0.
This is an odd behaviour of the PHP language: normally one would expect 0 to be promoted to string "0" and then compared to "ABC" with a result false.
Perhaps that's what happen in other languages like JavaScript where the weak comparison "ABC" == 0 evaluates false.
Doing a strict comparison solves the problem:
"ABC" === 0
evaluates false.
But what if I do need to compare numbers as strings with numbers?
"123" === 123
evaluates false because the left and right term are of different type.
What is actually needed is a weak comparison without the pitfalls of PHP type juggling.
The solution is to explicit promote the terms to string and then do a comparison (strict or weak doesn't matter anymore).
(string)"123" === (string)123
is
true
while
(string)"123" === (string)0
is
false
Applied to the original code:
$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
$item['price'] = -1;
}
The == operator will try to match values even if they are of different types. For instance:
'0' == 0 will be true
If you need type comparison as well, use the === operator:
'0' === 0 will be false
Your problem is the double equal operator, which will typecast the right member to the type of the left. Use strict if you prefer.
if($item['price'] == 'e') {
$item['price'] = -1;
}
Let's go back to your code (copied above). In this case, in most cases, $item['price'] is an integer (except when it is equal to e, obviously). As such, by laws of PHP, PHP will typecast "e" to integer, which yields int(0). (Don't believe me? <?php $i="e"; echo (int)$i; ?>).
To easily get away from this, use the triple equal (exact comparison) operator, which will check the type and will not implicitly typecast.
P.S: a PHP fun fact: a == b does not imply that b == a. Take your example and reverse it: if ("e" == $item['price']) will never actually be fulfilled provided that $item['price'] is always an integer.
There's a rather handy method in PHP for validating a mix of "0", "false", "off" as == false and "1", "on", "true" as == true which is often overlooked. It's particularly useful for parsing GET/POST arguments:
filter_var( $item['price'], FILTER_VALIDATE_BOOLEAN );
It's not wholy relevant to this use-case but given the similarity and fact this is the result search tends to find when asking the question of validating (string)"0" as false I thought it would help others.
http://www.php.net/manual/en/filter.filters.validate.php
You should use === instead of ==, because the ordinary operator does not compare the types. Instead it will attempt to typecast the items.
Meanwhile the === takes in consideration type of items.
=== means "equals",
== means "eeeeh .. kinda looks like"
Basically, always use the === operator, to guarantee type safety.
I think it is best to show by examples I did, while running into the same weird behavior. See my test case and hopefully it will help you understand the behavior better:
// Normal comparison using the == Operator
echo (0 == "0"); // true
echo (0 == "a"); // true
echo (0 == "safta!"); // true
echo (1000 == "bla"); // false. It appears that PHP has a weird behavior only with the number / string 0 / "0" according to the past 3 examples.
echo (23 == "23"); // true. So as we said, PHP has a problem (not a problem but weird behavior) only when the number / string 0 (or "0") is present
echo (23 == "24"); // false. values aren't equal (unlike last example). The type is less relevant with the == operator as we can see.
// Now using the === and !== Operators
echo ("0" === 0); // false, since === requires both value and type to be the same. Here, type is different (int vs string)
echo ("0" !== 0); // true because they aren't the same in terms of === comparison (type is different and that's why it's true)
echo ("bla" === "blaa"); // false because the values are not the same. The type is the same, but === checks for both equal type and equal value.
//Now using casting and === Operator:
echo ((string)123 === "123"); // true. The casting of the int 123 to string changed it to "123" and now both variables have same value and are of same type
echo ((int)"123" === 123); // true. The casting of the string 123 to int, changed it to int, and now both variables are of same value and type (which is exactly what the === operator is looking for)
// Now using casting and == Operator. Basically, as we've seen above, the == care less for the
// type of var, but more to the value. So the casting is less relevant here, because even
// without casting, like we saw earlier, we can still compare string to int with the == operator
// and if their value is same, we'll get true. Either way, we will show that:
echo ((string)123 == "123"); // true. The casting of the int 123 to string changed it to "123" and now both vars have same value and are of same type
echo ((int)"123" == 123); // true. The casting of the string 123 to int, changed it to int, and now both vars are of same value and type (which is exactly what the === operator is looking for)

PHP Bug in if-statement? [duplicate]

I have the following piece of code:
$item['price'] = 0;
/* Code to get item information goes in here */
if($item['price'] == 'e') {
$item['price'] = -1;
}
It is intended to initialize the item price to 0 and then get information about it. If the price is informed as 'e' it means an exchange instead of a sell, which is stored in a database as a negative number.
There is also the possibility to leave the price as 0, either because the item is a bonus or because the price will be set in a later moment.
But, whenever the price is not set, which leaves it with the initial value of 0, the if loop indicated above evaluates as true and the price is set to -1. That is, it considers 0 as equal to 'e'.
How can this be explained?
When the price is provided as 0 (after initialization), the behavior is erratic: sometimes the if evaluates as true, sometimes it evaluates as false.*
You are doing == which sorts out the types for you.
0 is an int, so in this case it is going to cast 'e' to an int. Which is not parsable as one and will become 0. A string '0e' would become 0 and would match!
Use ===
From PHP.net:
Comparisons between strings and numbers using == and other non-strict
comparison operators currently work by casting the string to a number,
and subsequently performing a comparison on integers or floats. This
results in many surprising comparison results, the most notable of
which is that 0 == "foobar" returns true.
However this behavior was changed in PHP 8.0:
When comparing to a numeric string, PHP 8 uses a number comparison.
Otherwise, it converts the number to a string and uses a string
comparison.
PHP 7
0 == 'foobar' // true
0 == '' // true
4 == '4e' // true (4e is cast as a number and becomes 4)
PHP 8 converts numbers to strings before making comparisons
0 == 'foobar' // false
0 == '' // false
4 == '4e' // false ('4e' is considered non-numeric therefore 4 is cast as a string and becomes '4')
This is a major change therefore it was implemented in a new major PHP version. This change breaks backward compatibility in scripts that depend on the old behavior.
This is due to how PHP does the comparison operation that the == comparison operator denotes:
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. […] The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.
As the first operand is a number (0) and the second is a string ('e'), the string is also converted to a number (see also table Comparison with Various Types). The manual page on the string data type defined how the string to number conversion is done:
When a string is evaluated in a numeric context, the resulting value and type are determined as follows.
If the string does not contain any of the characters '.', 'e', or 'E' and the numeric value fits into integer type limits (as defined by PHP_INT_MAX), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
In this case the string is 'e' and thus it will be evaluated as a float:
The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e' or 'E' followed by one or more digits.
As 'e' does not start with a valid numeric data, it evaluates to float 0.
"ABC" == 0
evaluates true because first "ABC" is converted to integer and becomes 0 then it is compared to 0.
This is an odd behaviour of the PHP language: normally one would expect 0 to be promoted to string "0" and then compared to "ABC" with a result false.
Perhaps that's what happen in other languages like JavaScript where the weak comparison "ABC" == 0 evaluates false.
Doing a strict comparison solves the problem:
"ABC" === 0
evaluates false.
But what if I do need to compare numbers as strings with numbers?
"123" === 123
evaluates false because the left and right term are of different type.
What is actually needed is a weak comparison without the pitfalls of PHP type juggling.
The solution is to explicit promote the terms to string and then do a comparison (strict or weak doesn't matter anymore).
(string)"123" === (string)123
is
true
while
(string)"123" === (string)0
is
false
Applied to the original code:
$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
$item['price'] = -1;
}
The == operator will try to match values even if they are of different types. For instance:
'0' == 0 will be true
If you need type comparison as well, use the === operator:
'0' === 0 will be false
Your problem is the double equal operator, which will typecast the right member to the type of the left. Use strict if you prefer.
if($item['price'] == 'e') {
$item['price'] = -1;
}
Let's go back to your code (copied above). In this case, in most cases, $item['price'] is an integer (except when it is equal to e, obviously). As such, by laws of PHP, PHP will typecast "e" to integer, which yields int(0). (Don't believe me? <?php $i="e"; echo (int)$i; ?>).
To easily get away from this, use the triple equal (exact comparison) operator, which will check the type and will not implicitly typecast.
P.S: a PHP fun fact: a == b does not imply that b == a. Take your example and reverse it: if ("e" == $item['price']) will never actually be fulfilled provided that $item['price'] is always an integer.
There's a rather handy method in PHP for validating a mix of "0", "false", "off" as == false and "1", "on", "true" as == true which is often overlooked. It's particularly useful for parsing GET/POST arguments:
filter_var( $item['price'], FILTER_VALIDATE_BOOLEAN );
It's not wholy relevant to this use-case but given the similarity and fact this is the result search tends to find when asking the question of validating (string)"0" as false I thought it would help others.
http://www.php.net/manual/en/filter.filters.validate.php
You should use === instead of ==, because the ordinary operator does not compare the types. Instead it will attempt to typecast the items.
Meanwhile the === takes in consideration type of items.
=== means "equals",
== means "eeeeh .. kinda looks like"
Basically, always use the === operator, to guarantee type safety.
I think it is best to show by examples I did, while running into the same weird behavior. See my test case and hopefully it will help you understand the behavior better:
// Normal comparison using the == Operator
echo (0 == "0"); // true
echo (0 == "a"); // true
echo (0 == "safta!"); // true
echo (1000 == "bla"); // false. It appears that PHP has a weird behavior only with the number / string 0 / "0" according to the past 3 examples.
echo (23 == "23"); // true. So as we said, PHP has a problem (not a problem but weird behavior) only when the number / string 0 (or "0") is present
echo (23 == "24"); // false. values aren't equal (unlike last example). The type is less relevant with the == operator as we can see.
// Now using the === and !== Operators
echo ("0" === 0); // false, since === requires both value and type to be the same. Here, type is different (int vs string)
echo ("0" !== 0); // true because they aren't the same in terms of === comparison (type is different and that's why it's true)
echo ("bla" === "blaa"); // false because the values are not the same. The type is the same, but === checks for both equal type and equal value.
//Now using casting and === Operator:
echo ((string)123 === "123"); // true. The casting of the int 123 to string changed it to "123" and now both variables have same value and are of same type
echo ((int)"123" === 123); // true. The casting of the string 123 to int, changed it to int, and now both variables are of same value and type (which is exactly what the === operator is looking for)
// Now using casting and == Operator. Basically, as we've seen above, the == care less for the
// type of var, but more to the value. So the casting is less relevant here, because even
// without casting, like we saw earlier, we can still compare string to int with the == operator
// and if their value is same, we'll get true. Either way, we will show that:
echo ((string)123 == "123"); // true. The casting of the int 123 to string changed it to "123" and now both vars have same value and are of same type
echo ((int)"123" == 123); // true. The casting of the string 123 to int, changed it to int, and now both vars are of same value and type (which is exactly what the === operator is looking for)

PHP anomaly or bug? [duplicate]

I have the following piece of code:
$item['price'] = 0;
/* Code to get item information goes in here */
if($item['price'] == 'e') {
$item['price'] = -1;
}
It is intended to initialize the item price to 0 and then get information about it. If the price is informed as 'e' it means an exchange instead of a sell, which is stored in a database as a negative number.
There is also the possibility to leave the price as 0, either because the item is a bonus or because the price will be set in a later moment.
But, whenever the price is not set, which leaves it with the initial value of 0, the if loop indicated above evaluates as true and the price is set to -1. That is, it considers 0 as equal to 'e'.
How can this be explained?
When the price is provided as 0 (after initialization), the behavior is erratic: sometimes the if evaluates as true, sometimes it evaluates as false.*
You are doing == which sorts out the types for you.
0 is an int, so in this case it is going to cast 'e' to an int. Which is not parsable as one and will become 0. A string '0e' would become 0 and would match!
Use ===
From PHP.net:
Comparisons between strings and numbers using == and other non-strict
comparison operators currently work by casting the string to a number,
and subsequently performing a comparison on integers or floats. This
results in many surprising comparison results, the most notable of
which is that 0 == "foobar" returns true.
However this behavior was changed in PHP 8.0:
When comparing to a numeric string, PHP 8 uses a number comparison.
Otherwise, it converts the number to a string and uses a string
comparison.
PHP 7
0 == 'foobar' // true
0 == '' // true
4 == '4e' // true (4e is cast as a number and becomes 4)
PHP 8 converts numbers to strings before making comparisons
0 == 'foobar' // false
0 == '' // false
4 == '4e' // false ('4e' is considered non-numeric therefore 4 is cast as a string and becomes '4')
This is a major change therefore it was implemented in a new major PHP version. This change breaks backward compatibility in scripts that depend on the old behavior.
This is due to how PHP does the comparison operation that the == comparison operator denotes:
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. […] The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.
As the first operand is a number (0) and the second is a string ('e'), the string is also converted to a number (see also table Comparison with Various Types). The manual page on the string data type defined how the string to number conversion is done:
When a string is evaluated in a numeric context, the resulting value and type are determined as follows.
If the string does not contain any of the characters '.', 'e', or 'E' and the numeric value fits into integer type limits (as defined by PHP_INT_MAX), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
In this case the string is 'e' and thus it will be evaluated as a float:
The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e' or 'E' followed by one or more digits.
As 'e' does not start with a valid numeric data, it evaluates to float 0.
"ABC" == 0
evaluates true because first "ABC" is converted to integer and becomes 0 then it is compared to 0.
This is an odd behaviour of the PHP language: normally one would expect 0 to be promoted to string "0" and then compared to "ABC" with a result false.
Perhaps that's what happen in other languages like JavaScript where the weak comparison "ABC" == 0 evaluates false.
Doing a strict comparison solves the problem:
"ABC" === 0
evaluates false.
But what if I do need to compare numbers as strings with numbers?
"123" === 123
evaluates false because the left and right term are of different type.
What is actually needed is a weak comparison without the pitfalls of PHP type juggling.
The solution is to explicit promote the terms to string and then do a comparison (strict or weak doesn't matter anymore).
(string)"123" === (string)123
is
true
while
(string)"123" === (string)0
is
false
Applied to the original code:
$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
$item['price'] = -1;
}
The == operator will try to match values even if they are of different types. For instance:
'0' == 0 will be true
If you need type comparison as well, use the === operator:
'0' === 0 will be false
Your problem is the double equal operator, which will typecast the right member to the type of the left. Use strict if you prefer.
if($item['price'] == 'e') {
$item['price'] = -1;
}
Let's go back to your code (copied above). In this case, in most cases, $item['price'] is an integer (except when it is equal to e, obviously). As such, by laws of PHP, PHP will typecast "e" to integer, which yields int(0). (Don't believe me? <?php $i="e"; echo (int)$i; ?>).
To easily get away from this, use the triple equal (exact comparison) operator, which will check the type and will not implicitly typecast.
P.S: a PHP fun fact: a == b does not imply that b == a. Take your example and reverse it: if ("e" == $item['price']) will never actually be fulfilled provided that $item['price'] is always an integer.
There's a rather handy method in PHP for validating a mix of "0", "false", "off" as == false and "1", "on", "true" as == true which is often overlooked. It's particularly useful for parsing GET/POST arguments:
filter_var( $item['price'], FILTER_VALIDATE_BOOLEAN );
It's not wholy relevant to this use-case but given the similarity and fact this is the result search tends to find when asking the question of validating (string)"0" as false I thought it would help others.
http://www.php.net/manual/en/filter.filters.validate.php
You should use === instead of ==, because the ordinary operator does not compare the types. Instead it will attempt to typecast the items.
Meanwhile the === takes in consideration type of items.
=== means "equals",
== means "eeeeh .. kinda looks like"
Basically, always use the === operator, to guarantee type safety.
I think it is best to show by examples I did, while running into the same weird behavior. See my test case and hopefully it will help you understand the behavior better:
// Normal comparison using the == Operator
echo (0 == "0"); // true
echo (0 == "a"); // true
echo (0 == "safta!"); // true
echo (1000 == "bla"); // false. It appears that PHP has a weird behavior only with the number / string 0 / "0" according to the past 3 examples.
echo (23 == "23"); // true. So as we said, PHP has a problem (not a problem but weird behavior) only when the number / string 0 (or "0") is present
echo (23 == "24"); // false. values aren't equal (unlike last example). The type is less relevant with the == operator as we can see.
// Now using the === and !== Operators
echo ("0" === 0); // false, since === requires both value and type to be the same. Here, type is different (int vs string)
echo ("0" !== 0); // true because they aren't the same in terms of === comparison (type is different and that's why it's true)
echo ("bla" === "blaa"); // false because the values are not the same. The type is the same, but === checks for both equal type and equal value.
//Now using casting and === Operator:
echo ((string)123 === "123"); // true. The casting of the int 123 to string changed it to "123" and now both variables have same value and are of same type
echo ((int)"123" === 123); // true. The casting of the string 123 to int, changed it to int, and now both variables are of same value and type (which is exactly what the === operator is looking for)
// Now using casting and == Operator. Basically, as we've seen above, the == care less for the
// type of var, but more to the value. So the casting is less relevant here, because even
// without casting, like we saw earlier, we can still compare string to int with the == operator
// and if their value is same, we'll get true. Either way, we will show that:
echo ((string)123 == "123"); // true. The casting of the int 123 to string changed it to "123" and now both vars have same value and are of same type
echo ((int)"123" == 123); // true. The casting of the string 123 to int, changed it to int, and now both vars are of same value and type (which is exactly what the === operator is looking for)

array_search can't find element which is clearly there

Why does the following code work:
$test = array(0=>'test1','field0'=>'test2',1=>'test3','field1'=>'test4');
echo array_search('test4',$test);
But the following doesn't:
$test = array(0=>0,'field0'=>'test2',1=>'test3','field1'=>'test4');
echo array_search('test4',$test);
If you had a mixed array from say mysql_fetch_array($result,MYSQL_BOTH) and took the keys which you needed to search you wouldn't be able too - it would never progress further than 0.
Try to do array_search('test4',$test, TRUE);. The 3rd parameter tells it to use === instead of == when comparing.
Since your array holds both strings and numbers, when it compares to the 0 (the 1st element), it converts 'test4' to a number (it stops at the 1st non-numeric character) and it happens to match.
What I mean is: 'test4' == 0 => 0 == 0 => true.
When you pass TRUE as the 3rd parameter, it uses ===, and 'test4' === 0 is automatically false since the types don't match, there is no conversion.
The solution = force the 0 value to be a string:
$test = array(0=>0,'field0'=>'test2',1=>'test3','field1'=>'test4');
foreach ($test as $k=>$v){ $test[$k] = (string) $v; }
echo array_search('test4',$test);
You can't search a number for a string and expect a good result.
My guess is it sees the value as a number, so it converts your string to a number (which it can't) so that string gets converted to a 0. So the value of 0 equals the search string, which also equals 0, and there's your result.
If the value is 1, it won't match as the search string gets converted to a 0 (as you can't convert a string to a number) so it wouldn't match in the following.
$test = array(0=>1,'field0'=>'test2',1=>'test3','field1'=>'test4');
You'll only get your exact case scenario when that value in the array is 0.

Why does PHP consider 0 to be equal to a string?

I have the following piece of code:
$item['price'] = 0;
/* Code to get item information goes in here */
if($item['price'] == 'e') {
$item['price'] = -1;
}
It is intended to initialize the item price to 0 and then get information about it. If the price is informed as 'e' it means an exchange instead of a sell, which is stored in a database as a negative number.
There is also the possibility to leave the price as 0, either because the item is a bonus or because the price will be set in a later moment.
But, whenever the price is not set, which leaves it with the initial value of 0, the if loop indicated above evaluates as true and the price is set to -1. That is, it considers 0 as equal to 'e'.
How can this be explained?
When the price is provided as 0 (after initialization), the behavior is erratic: sometimes the if evaluates as true, sometimes it evaluates as false.*
You are doing == which sorts out the types for you.
0 is an int, so in this case it is going to cast 'e' to an int. Which is not parsable as one and will become 0. A string '0e' would become 0 and would match!
Use ===
From PHP.net:
Comparisons between strings and numbers using == and other non-strict
comparison operators currently work by casting the string to a number,
and subsequently performing a comparison on integers or floats. This
results in many surprising comparison results, the most notable of
which is that 0 == "foobar" returns true.
However this behavior was changed in PHP 8.0:
When comparing to a numeric string, PHP 8 uses a number comparison.
Otherwise, it converts the number to a string and uses a string
comparison.
PHP 7
0 == 'foobar' // true
0 == '' // true
4 == '4e' // true (4e is cast as a number and becomes 4)
PHP 8 converts numbers to strings before making comparisons
0 == 'foobar' // false
0 == '' // false
4 == '4e' // false ('4e' is considered non-numeric therefore 4 is cast as a string and becomes '4')
This is a major change therefore it was implemented in a new major PHP version. This change breaks backward compatibility in scripts that depend on the old behavior.
This is due to how PHP does the comparison operation that the == comparison operator denotes:
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. […] The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.
As the first operand is a number (0) and the second is a string ('e'), the string is also converted to a number (see also table Comparison with Various Types). The manual page on the string data type defined how the string to number conversion is done:
When a string is evaluated in a numeric context, the resulting value and type are determined as follows.
If the string does not contain any of the characters '.', 'e', or 'E' and the numeric value fits into integer type limits (as defined by PHP_INT_MAX), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
In this case the string is 'e' and thus it will be evaluated as a float:
The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e' or 'E' followed by one or more digits.
As 'e' does not start with a valid numeric data, it evaluates to float 0.
"ABC" == 0
evaluates true because first "ABC" is converted to integer and becomes 0 then it is compared to 0.
This is an odd behaviour of the PHP language: normally one would expect 0 to be promoted to string "0" and then compared to "ABC" with a result false.
Perhaps that's what happen in other languages like JavaScript where the weak comparison "ABC" == 0 evaluates false.
Doing a strict comparison solves the problem:
"ABC" === 0
evaluates false.
But what if I do need to compare numbers as strings with numbers?
"123" === 123
evaluates false because the left and right term are of different type.
What is actually needed is a weak comparison without the pitfalls of PHP type juggling.
The solution is to explicit promote the terms to string and then do a comparison (strict or weak doesn't matter anymore).
(string)"123" === (string)123
is
true
while
(string)"123" === (string)0
is
false
Applied to the original code:
$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
$item['price'] = -1;
}
The == operator will try to match values even if they are of different types. For instance:
'0' == 0 will be true
If you need type comparison as well, use the === operator:
'0' === 0 will be false
Your problem is the double equal operator, which will typecast the right member to the type of the left. Use strict if you prefer.
if($item['price'] == 'e') {
$item['price'] = -1;
}
Let's go back to your code (copied above). In this case, in most cases, $item['price'] is an integer (except when it is equal to e, obviously). As such, by laws of PHP, PHP will typecast "e" to integer, which yields int(0). (Don't believe me? <?php $i="e"; echo (int)$i; ?>).
To easily get away from this, use the triple equal (exact comparison) operator, which will check the type and will not implicitly typecast.
P.S: a PHP fun fact: a == b does not imply that b == a. Take your example and reverse it: if ("e" == $item['price']) will never actually be fulfilled provided that $item['price'] is always an integer.
There's a rather handy method in PHP for validating a mix of "0", "false", "off" as == false and "1", "on", "true" as == true which is often overlooked. It's particularly useful for parsing GET/POST arguments:
filter_var( $item['price'], FILTER_VALIDATE_BOOLEAN );
It's not wholy relevant to this use-case but given the similarity and fact this is the result search tends to find when asking the question of validating (string)"0" as false I thought it would help others.
http://www.php.net/manual/en/filter.filters.validate.php
You should use === instead of ==, because the ordinary operator does not compare the types. Instead it will attempt to typecast the items.
Meanwhile the === takes in consideration type of items.
=== means "equals",
== means "eeeeh .. kinda looks like"
Basically, always use the === operator, to guarantee type safety.
I think it is best to show by examples I did, while running into the same weird behavior. See my test case and hopefully it will help you understand the behavior better:
// Normal comparison using the == Operator
echo (0 == "0"); // true
echo (0 == "a"); // true
echo (0 == "safta!"); // true
echo (1000 == "bla"); // false. It appears that PHP has a weird behavior only with the number / string 0 / "0" according to the past 3 examples.
echo (23 == "23"); // true. So as we said, PHP has a problem (not a problem but weird behavior) only when the number / string 0 (or "0") is present
echo (23 == "24"); // false. values aren't equal (unlike last example). The type is less relevant with the == operator as we can see.
// Now using the === and !== Operators
echo ("0" === 0); // false, since === requires both value and type to be the same. Here, type is different (int vs string)
echo ("0" !== 0); // true because they aren't the same in terms of === comparison (type is different and that's why it's true)
echo ("bla" === "blaa"); // false because the values are not the same. The type is the same, but === checks for both equal type and equal value.
//Now using casting and === Operator:
echo ((string)123 === "123"); // true. The casting of the int 123 to string changed it to "123" and now both variables have same value and are of same type
echo ((int)"123" === 123); // true. The casting of the string 123 to int, changed it to int, and now both variables are of same value and type (which is exactly what the === operator is looking for)
// Now using casting and == Operator. Basically, as we've seen above, the == care less for the
// type of var, but more to the value. So the casting is less relevant here, because even
// without casting, like we saw earlier, we can still compare string to int with the == operator
// and if their value is same, we'll get true. Either way, we will show that:
echo ((string)123 == "123"); // true. The casting of the int 123 to string changed it to "123" and now both vars have same value and are of same type
echo ((int)"123" == 123); // true. The casting of the string 123 to int, changed it to int, and now both vars are of same value and type (which is exactly what the === operator is looking for)

Categories