7

I am curious why this is happening in PHP:

'78' == ' 78' // true
'78' == '78 ' // false

I know that it's much better to use strcmp or the least ===. I also know that when you compare numerical strings with == they are casted to numbers if possible. I also can accept that the leading space is ignored, so (int)' 78' is 78, and the answer is true in the first case, but I'm really confused why it's false in the second.

I thought that '78' is casted to 78 and '78 ' is casted to 78, too, so they are the same and the answer is true, but obviously, that's not the case.

Any help will be appreciated! Thank you very much in advance! :)

7
  • 3
    Here php.net/manual/fr/language.operators.comparison.php see the answer from arnaud at arnapou dot net. It's quite great Commented Aug 31, 2015 at 12:08
  • Thank you very much @Hearner, but I already read it before posting the question here. :) I was just wondering why ' 78' is considered "numerical string" and the space is ignored, but in '78 ' it is taken into consideration. Commented Aug 31, 2015 at 12:12
  • do a var_dump(); and/or print_r(); on both and see what shows up. That might explain it in its own right. Commented Aug 31, 2015 at 12:21
  • I have already faced this issue and I red this article. This doesn't give any answer but it confirmed the problem. I can't provide any answer though but I hope someone will. I wanna satisfy my curiosity Commented Aug 31, 2015 at 12:21
  • 1
    @Fred-ii- var_dump can't help here because PHP is converting it internally before the comparison. I asked some internals people who hang out in chat and they said it's not possible to see that from this end. Commented Aug 31, 2015 at 12:51

2 Answers 2

8

It all seems to go back to this is_numeric_string_ex C function.

To start at the implementation of ==:

ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) {
    ...
    switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
        ...
        case TYPE_PAIR(IS_STRING, IS_STRING):
            ...
            ZVAL_LONG(result, zendi_smart_strcmp(op1, op2));

If both operands are a string, it ends up calling zendi_smart_strcmp...

ZEND_API zend_long ZEND_FASTCALL zendi_smart_strcmp(zval *s1, zval *s2) {
    ...
    if ((ret1 = is_numeric_string_ex(Z_STRVAL_P(s1), Z_STRLEN_P(s1), &lval1, &dval1, 0, &oflow1)) &&
        (ret2 = is_numeric_string_ex(Z_STRVAL_P(s2), Z_STRLEN_P(s2), &lval2, &dval2, 0, &oflow2))) ...

Which calls is_numeric_string_ex...

/* Skip any whitespace
 * This is much faster than the isspace() function */
while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') {
    str++;
    length--;
}
ptr = str;

Which has explicit code to skip whitespace at the beginning, but not at the end.

Sign up to request clarification or add additional context in comments.

5 Comments

This definitely deserves a good-answer badge. (Maybe submit it as manual page edit ? And add like a little gray "note:" box)
Thank you very much! This is just what I was looking for!
But if you try to compare '78' == ' 78' and '78' == '78 ' like 78 == ' 78' and 78 == '78 ' the return will be true in both cases. Why? Both continue being string | integer.
@Thiago That can be answered both from the manual and the source code: it's always using numeric comparison if any operand is a number. But if both are strings, then it depends on is_numeric_string_ex whether it will be treated as number or not, and this function returns something different for '78 ' and ' 78'.
When you compare string with integer, the string is casted to integer. You can try var_dump on (int)' 78' and (int)'78 ' - they both return the integer 78, which is why the return will be true in both cases.
-1

The space on the end of '78 ' causes PHP to treat the variable as a string. you can use trim() to strip the spaces.

2 Comments

Thanks! I know about trim() and I will never use the above code, I was just searching for explanation and logic why it behaves this way. For me ' 78' and '78 ' are equally "stringish". Even more, if you cast both of them to integers, the answer will be 78 in both cases.
While you've nailed the problem and how to solve it, this doesn't explain the WHY part like the OP was asking.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.