AndreCunha AndreCunha - 7 months ago 13
PHP Question

PHP max() and min() weird behavior with different types

I have been scratching my head for the past two hours over this behavior:

echo 'max(1, "a", "2" ) : '; var_dump( max( 1, 'a', '2' ) );
echo 'max(1, "2", "a" ) : '; var_dump( max( 1, '2', 'a' ) );
echo 'max( "a", "2", 1) : '; var_dump( max( 'a', '2', 1 ) );
echo 'max( "2", "a", 1) : '; var_dump( max( '2', 'a', 1 ) );
echo "\n";
echo 'min(1, "a", "2" ) : '; var_dump( min( 1, 'a', '2' ) );
echo 'min(1, "2", "a" ) : '; var_dump( min( 1, '2', 'a' ) );
echo 'min( "a", "2", 1) : '; var_dump( min( 'a', '2', 1 ) );
echo 'min( "2", "a", 1) : '; var_dump( min( '2', 'a', 1 ) );


Which prints out:

max(1, "a", "2" ) : string(1) "2"
max(1, "2", "a" ) : string(1) "a"
max( "a", "2", 1) : int(1)
max( "2", "a", 1) : int(1)

min(1, "a", "2" ) : string(1) "2"
min(1, "2", "a" ) : string(1) "a"
min( "a", "2", 1) : int(1)
min( "2", "a", 1) : int(1)


When I was expecting:

max(1, "a", "2" ) : string(1) "2"
max(1, "2", "a" ) : string(1) "2"
max( "a", "2", 1) : string(1) "2"
max( "2", "a", 1) : string(1) "2"

min(1, "a", "2" ) : string(1) "a"
min(1, "2", "a" ) : string(1) "a"
min( "a", "2", 1) : string(1) "a"
min( "2", "a", 1) : string(1) "a"


Note that the values given to
max()
and
min()
functions are always the same, but the order is different.

Also, according to the documentation, strings not started with numerical characters are evaluated as integer
0
when compared to integers, but multiple string values are compared alphanumerically. Source:



How could this output be explained?

Answer

The comparison in min() and max() is done sequentially as this can be seen in the source code:

min() source code:

//...
min = &args[0];

for (i = 1; i < argc; i++) {
    is_smaller_function(&result, &args[i], min);
    if (Z_TYPE(result) == IS_TRUE) {
        min = &args[i];
    }
}
//...

max() source code:

//...
max = &args[0];

for (i = 1; i < argc; i++) {
    is_smaller_or_equal_function(&result, &args[i], max);
    if (Z_TYPE(result) == IS_FALSE) {
        max = &args[i];
    }
}
//...

So you can see that the function goes through the array and checks if the next value is smaller/bigger than the previous smallest/biggest saved value.


To understand how you get these results you also have to know that the comparison of the values is done after PHP type juggling.

So let's look at two example to see how the functions work:

Example 1

max ( 1 , 'a' , '2' )
      │    │     │
      1 > 'a'    │
        │        │
        1    <  '2'
             │
             └ Result: '2'
  1. Comparing 1 and 'a', numerical context means 'a' gets converted to 0, 1 is greater than 0.
    → int 1 returned

  2. Comparing 1 and '2', numerical string means '2' gets converted to 2, 2 is greater than 1.
    → string '2' returned

  • Result: string '2'

Example 2

max ( 1 , '2' , 'a' )
      │    │     │
      1 < '2'    │
        │        │
       '2'   <  'a'
             │
             └ Result: 'a'
  1. Comparing 1 and '2', numerical string means '2' gets converted to 2, 2 is greater than 1.
    → string '2' returned

  2. Comparing '2' and 'a', both strings means string comparison, 'a' is greater than '2' just compared with the ASCII values.
    → string 'a' returned

  • Result: string 'a'

And the same rules apply for all of the other examples.