Type-Juggling and (Strict) Greater/Lesser-Than Comparisons in PHP

Is there a 'greater and lesser than' comparison operator?

No. You'd want to use an "AND" operator, &&:

if ($variable > 0 && $variable < 5) {
// bla.. bla.. bla..
}

php greater than or equal too AND of same type

You can try with gettype() php function.

if( gettype($var1) == gettype($var2) && $var2 < $var1 )

Good luck

PHP type juggling with strpos() conditions and ||, in spite of strict ===?

The problem is not down to a PHP bug. Here is the issue:

strpos($thispage, 'bg177b_s') || strpos($thispage, 'bg177b_t')

You are doing a strpos on both strings, and then doing an OR on the result. That forces the result to be boolean, which loses the difference between zero and false.

Put another way, if one of these functions returns zero, it means that one of the strings was found at the 0th position. This gets logical-ored with false (since presumably both won't occur at the same time) giving you an incorrect false result.

Your third code example is the correct one:

strpos($thispage, 'bg177b_s') === false &&
strpos($thispage, 'bg177b_t') === false

I would be inclined to use a helper function, which simplifies the call:

function findOneString($haystack, array $needles)
{
$found = false;
foreach($needles as $needle)
{
if (strpos($haystack, $needle) !== false)
{
$found = true;
break;
}
}

return $found;
}

You can call this with:

findOneString($thispage, array('bg177b_s', 'bg177b_t', ));

Underlying philosophy behind php type comparisons

For casting directly to a boolean this is how it works.

  • All string with a length > 0 are true
  • All non 0 numbers are true
  • All non-empty arrays are true
  • All objects are true

Then these rules for comparing variables of the same type:

  1. Objects are equivalent if their properties are equal
  2. Arrays are equivalent if their keys and elements are equal
  3. Strings are equivalent if they would produce the same output
  4. Numbers are equivalent if they are mathematically equivalent
  5. Booleans are equivalent if they have the same value.

For variable of different types the type that is higher on the above list is cast to the one that is lower then the comparison is made.

=== and !== operators don't cast prior to comparing but you should note objects are only === if they are the same instance.

The really odd one is arrays, they are === if they have the same keys and values defined in the same order.

$a = array("a"=>1, "b"=>2);
$b = array("b"=>2, "a"=>1);

$a == $b; // true
$a === $b; // false

and empty() is equivalent to !(bool)$var

EXCEPTIONS

  • Casting an array to a string will trigger a notice and unhelpfully cast as the text Array
  • Casting an object without a __toString method to a string will get you a fatal error.
  • Objects will not implicitly cast to an array, so any time you compare an object to an array it will yield a false (UPDATE confirmed that this is true even if object implemtents the ArrayAccess interface)

Why does PHP 8 treat 42 == 42 as true?

Short answer:

In PHP 8 and in PHP 7, 42 == " 42" is true, and in both PHP 7 and PHP 8, "42" == " 42" is true.

Long answer:

I think you have a misconception of how the equality operator == works in PHP. Both in PHP 8 and in earlier versions of PHP, the equality operator treats strings that look numeric differently from other strings:

<?php
// the == operator does type juggling and also handles numerical strings differently:
var_export("42" == "42"); // true
var_export(42 == "42"); // true
var_export("42" == " 42"); // true
var_export("42" == "42 "); // false in PHP 7 and true in PHP 8
var_export("42" == "042"); // true
var_export("10" == "1e1"); // true
var_export("abc" == " abc"); // false
var_export("42" == "abc 42"); // false

echo "\n";

// the === operator does not do type juggling and does not handle numerical strings differently:
var_export("42" === "42"); // true
var_export(42 === "42"); // false
var_export("42" === " 42"); // false
var_export("42" === "42 "); // false
var_export("42" === "042"); // false
var_export("10" === "1e1"); // false
var_export("abc" === " abc"); // false
var_export("42" === "abc 42"); // false

echo "\n";

Even in PHP 8, the == operator both does type juggling and it handles numeric strings differently. That principle hasn't changed. What has changed in PHP 8 is:

  • The equality operator == behaves differently in PHP 8 than in PHP 7 for comparisons between numbers and non-numeric strings
  • What strings are considered numeric strings is different in PHP 8

Regarding the latter point, read this:

Numeric string handling has been altered [in PHP 8] to be more intuitive and less error-prone. Trailing whitespace is now allowed in numeric strings for consistency with how leading whitespace is treated. This mostly affects:

  • The is_numeric() function
  • String-to-string comparisons
  • Type declarations
  • Increment and decrement operations

Do relational operators like less than, greater than, gte, lte use type conversion in PHP?

No, they aren't strict.

They can compare most types against each other, and cast as needed. For instance there's support for string comparisons:

 $true = "a" < "b";

Internally that's zendi_smart_strcmp().

If you want to do strict comparisons, you can just check the types yourself, or cast as needed.

Identifying type juggling issues before PHP upgrade to PHP8

I was able to detect this type of issues using https://github.com/phan/phan. Here is the sample issue that was caught.

{
"type": "issue",
"type_id": 10030,
"check_name": "PhanTypeMismatchDimAssignment",
"description": "TypeError PhanTypeMismatchDimAssignment When appending to a value of type '', found an array access index of type 'hobbies', but expected the index to be of type int",
"severity": 5,
"location": {
"path": "PQR/models/XYZModel.php",
"lines": {
"begin": 171,
"end": 171
}
}}

Thank you for your support guys.



Related Topics



Leave a reply



Submit