I have two PHP variables that can either be empty (i.e. value="") or contain a name in the format Last, First with a comma and a space between the last and first name (e.g. Mouse, Mickey).
I would like to make a simple check here and say if a variable is not empty AND is equal to another then check a checkbox but this doesnt work.
Can someone here show me what I am doing wrong (in the below example the checkbox should be checked) ?
My problem is that the checkbox always gets checked, even if the variables don't match.
Example:
$poc1 = "Mouse, Mickey"; // hard-coded for testing
$poc2 = "Mouse, Mickey"; // hard-coded for testing
<input type="checkbox" id="check2" name="Copy_POC" <?php if(($poc2 != "") && (strcmp($poc2,$poc1))) { echo "checked"; } ?> />
Many thanks for any help with this, Tim.
You need to look at the function signature for strcmp, and its return values:
int strcmp ( string $str1 , string $str2 )
So the function returns an int, but what kind of int? According to the manual:
Returns < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal.
In other words: if both strings are equal, strcmp returns 0, which evaluates to false. What you should've written therefore is:
strcmp($str1, $str2) !== 0
This will evaluate to true if the 2 strings do not match. Of course, you only want to see the ckeckbox checked when the two strings don't match:
if ($str1 != '' && strcmp($str1, $str2) === 0)
{
//checked
}
That ought to do it. Of course, this still relies on your calling functions to check these strings being equal. That doesn't really add up, though, and it might be a lot easier to just write:
if ($str1 && $str1 === $str2)
//an empty string is falsy + type & value check on 2 strings using === operator
Note
As you may already know, PHP is built on C, and therefore has a lot of C-like str* functions. Whenever you see a function like strcmp and strstr, check its return value. Like the C string.h functions, it often returns either a pointer (part of the string where substring is found, like strstr), or an integer (index/offset in string)...
<?php if($poc2 && $poc2 === $poc1) echo "checked" ?>
Related
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 is thinking that null is 0 when the character in $position doesn't exist.
$statusentery = $_POST[status];
$position = strpos($statusentery,"<");
if ($position == 0){
echo "Sorry, for security purposes we do not
allow characters such as <";
exit;
}
For example, if $statusentery was equal to "Howdy there", it would return "Sorry, for security purposes we do not allow characters such as <". (unexpected)
If $statusentery was equal to "Howdy there <" it would return blank (expected).
How to make it work so that when I enter "Howdy there", it didn't do the if loop, but when I enter "< howdy there>", it did the if loop?
Because 0 can means "false" in PHP, $postition will be false when it doesn't exist. So you'll need to use the following:
if ($position === 0){
Using the triple equal sign in PHP verifies that the compared values have the same data type. A lot more information can be found here: http://php.net/manual/en/language.operators.comparison.php
From: http://php.net/manual/es/function.strpos.php
WARNING
As strpos may return either FALSE (substring absent) or 0 (substring
at start of string), strict versus loose equivalency operators must be
used very carefully.
To know that a substring is absent, you must use:
=== FALSE
To know that a substring is present (in any position including 0), you
can use either of:
!== FALSE (recommended)
-1 (note: or greater than any negative number)
To know that a substring is at the start of the string, you must use:
=== 0
To know that a substring is in any position other than the start, you
can use any of:
0 (recommended) != 0 (note: but not !== 0 which also equates to FALSE) != FALSE (disrecommended as highly confusing)
Also note that you cannot compare a value of "" to the returned value
of strpos. With a loose equivalence operator (== or !=) it will return
results which don't distinguish between the substring's presence
versus position. With a strict equivalence operator (=== or !==) it
will always return false.
So the code must be:
$statusentery = $_POST[status];
$position = strpos($statusentery,"<");
if ($position === 0){
echo "Sorry, for security purposes we do not allow characters such as <";
exit;
}
This is a tricky function. As stated in the strpos manual:
Warning This function may return Boolean false, but may also return a
non-Boolean value which evaluates to false. Please read the section on
Booleans for more information. Use the === operator for testing the
return value of this function.
I use this comparison, which works well without even a notice level warning:
if ($position > -1)
Returns boolean false when the string is not found, which is evaluable as a number, thus comparing whether the value is greater than -1 actually works well. In case any match is found, even at position 0 the above comparison will be passed.
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)
I had an if statement similar to the following in my code and it took me forever to figure out what the problem was.
$a = 0;
if($a == 'something')
{
//this was being output when I didn't want it to be
}
Using
$a = '0';
fixed it, but I don't really know what's going on here.
One's a string, one's an integer. PHP will translate between the two as needed, unless you're using the 'strict' operators:
(0 == '0') // true
(0 === '0') // false (types don't match).
In your case, you'r comparing an integer 0 to a string 'something'. PHP will convert the string 'something' to an integer. If there's no digits in there at all, it'll conver to an integer 0, which makes your comparison true.
Just a guess, but I assume it's trying to cast the string to an integer.
intval('something') I expect will return 0.
You were comparing a numeric value ($a = 0;) to a string. In this case the string is casted to number, and PHP cast strings to 0 if there is no number in the beginning, so is true.
In the other case however you campared two strings, which are different, so it is false.
It seems that PHP's === operator is case sensitive. So is there a reason to use strcmp()?
Is it safe to do something like the following?
if ($password === $password2) { ... }
The reason to use it is because strcmp
returns < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal.
=== only returns true or false, it doesn't tell you which is the "greater" string.
You should never use == for string comparison. === is OK.
$something = 0;
echo ('password123' == $something) ? 'true' : 'false';
Just run the above code and you'll see why.
$something = 0;
echo ('password123' === $something) ? 'true' : 'false';
Now, that's a little better.
Don't use == in PHP. It will not do what you expect. Even if you are comparing strings to strings, PHP will implicitly cast them to floats and do a numerical comparison if they appear numerical.
For example '1e3' == '1000' returns true. You should use === instead.
Well...according to this PHP bug report, you can even get 0wned.
<?php
$pass = isset($_GET['pass']) ? $_GET['pass'] : '';
// Query /?pass[]= will authorize user
//strcmp and strcasecmp both are prone to this hack
if ( strcasecmp( $pass, '123456' ) == 0 ){
echo 'You successfully logged in.';
}
?>
It gives you a warning, but still bypass the comparison.
You should be doing === as #postfuturist suggested.
Always remember, when comparing strings, you should use the === operator (strict comparison) and not == operator (loose comparison).
Summing up all answers:
== is a bad idea for string comparisons.
It will give you "surprising" results in many cases. Don't trust it.
=== is fine, and will give you the best performance.
strcmp() should be used if you need to determine which string is "greater", typically for sorting operations.
Using == might be dangerous.
Note, that it would cast the variable to another data type if the two differs.
Examples:
echo (1 == '1') ? 'true' : 'false';
echo (1 == true) ? 'true' : 'false';
As you can see, these two are from different types, but the result is true, which might not be what your code will expect.
Using ===, however, is recommended as test shows that it's a bit faster than strcmp() and its case-insensitive alternative strcasecmp().
Quick googling yells this speed comparison: http://snipplr.com/view/758/
strcmp() and === are both case sensitive, but === is much faster.
Sample code: Speed Test: strcmp vs ===
strcmp will return different values based on the environment it is running in (Linux/Windows)!
The reason is the that it has a bug as the bug report says - Bug #53999strcmp() doesn't always return -1, 0, or 1
You can use strcmp() if you wish to order/compare strings lexicographically. If you just wish to check for equality then == is just fine.
Also, the function can help in sorting. To be more clear about sorting. strcmp() returns less than 0 if string1 sorts before string2, greater than 0 if string2 sorts before string1 or 0 if they are the same. For example
$first_string = "aabo";
$second_string = "aaao";
echo $n = strcmp($first_string, $second_string);
The function will return greater than zero, as aaao is sorting before aabo.
if ($password === $password2) { ... } is not a safe thing to do when comparing passwords or password hashes where one of the inputs is user controlled.
In that case it creates a timing oracle allowing an attacker to derive the actual password hash from execution time differences.
Use if (hash_equals($password, $password2)) { ... } instead, because hash_equals performs "timing attack safe string comparison".
In PHP, instead of using alphabetical sorting, use the ASCII value of the character to make the comparison.
Lowercase letters have a higher ASCII value than capitals. It's better to use the identity operator === to make this sort of comparison. strcmp() is a function to perform binary safe string comparisons. It takes two strings as arguments and returns < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal. There is also a case-insensitive version named strcasecmp() that first converts strings to lowercase and then compares them.