Skip to main content

Very illogical php value comparisons



I stumbled upon a very strange bit of PHP code. Could someone explain why this is happening? ** BONUS POINTS ** if you can tell my why this is useful.







<?php

if(0=='a'){

print ord(0)." should NEVER equal ".ord('a')."<br>";

}

if(false==0){

print "false==0<br>";

}

if('a'==false){

print "a==false<br>";

}

?>







And the resulting output:







48 should NEVER equal 97

false==0





Source: Tips4all

Comments

  1. In PHP, 'a' is not the ASCII character a, but the string a. In a numeric context, it is equal to 0. For instance intval('a') results in a value of 0.

    This is useful because PHP is primarily used for processing text, and one might want to try the test (123 == '123'), which is true. And given that a number in single (or double) quotation marks is treated as the number, it doesn't make sense for a string with no numeric value to be treated as anything other than 0.

    Oh yeah, one more thing. 'a' in a boolean context is true, not false. I believe this makes some types of text processing more natural, but I honestly can't think of an example at this late hour.

    ReplyDelete
  2. Well, there's always the PHP type cheat sheet for that!

    ReplyDelete
  3. This is a basic principle of weakly/dynamically typed languages called type juggling. Types will be cast to other types in certain circumstances. When you compare a string to a number, the string will be cast into a number. When comparing anything to a boolean, that value will be cast to a boolean.

    There are rules for every type as to how it will be cast into another type or how it compares to other types. 'a' happens to be converted to 0 when cast to a number (the only logical choice, really). To avoid this type casting, test not with the equality operator ==, but with the identity operator ===.

    As James pointed out, this is useful since PHP deals a lot with strings that are really numbers. For example, HTML forms only submit strings, even if the value is a number. It also allows for some really terse code, like:

    $result = someOperation();
    if (!$result) {
    // $result may be null, false, 0, '' or array(),
    // all of which we're not interested in
    error();
    }


    It also means you have to be really careful about what to check for in which circumstances though, since a value might unexpectedly cast into something else. And admittedly, 'a' == 0 in itself is really a pitfall of type juggling rather than helpful. It's one of the situations where you have to be careful and test like if (is_numeric($var) && $var == 0).

    ReplyDelete
  4. ord() takes characters, so PHP turns 0 into '0'. And 0 is equal to false, even though it is not identical (===).

    ReplyDelete
  5. Check out the PHP type comparison tables from the manual. It's a really handy thing to have close at hand until you've internalised it and has been invaluable to my understanding of exactly what will evaluate to true and when.

    Others have already answered the core of the question, but I think it's important to state that in PHP, the only non-empty string that does not evaluate to "true" with the == operator is "0" as PHP treats any string containing only numbers as an integer or float.

    The rationale for this is that PHP is fairly loosely typed and tries to allow integers, strings, floats and boolean values to be interchangeable. A real-world and extremely common example of this is if you're using the mysql or PDO functions, strings are returned for everything, even if the underlying column is an integer.

    Consider the following sql:

    CREATE TABLE `test`.`pants` (
    `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    `some_other_int` INT NOT NULL
    ) ENGINE = InnoDB;

    INSERT INTO `test`.`pants` (`id`, `some_other_int`)
    VALUES ('1', '1'), ('2', '0');


    And the following code:

    <?php

    $c = mysql_connect('127.0.0.1', 'user', 'password');
    mysql_select_db('test', $c);
    $r = mysql_query('SELECT * FROM pants', $c);

    while ($row = mysql_fetch_assoc($r)) {
    var_dump($row);
    foreach($row as $k=>$v) {
    if (!is_string($v))
    echo "field {$v} was not a string!\n";
    }
    }


    The "field x was not a string!" message is never printed, even though every column in the database is an integer. Suppose you want to actually use the second row in that table.

    <?php
    $id = 2;
    $r = mysql_query(sprintf('SELECT * FROM pants WHERE id=%s', mysql_real_esacpe_string($id)), $c);
    $row = mysql_fetch_assoc($r);

    // this is the important bit
    if (0 == $row['some_other_int']) {
    echo "It was zero!";
    }


    If the string "0" was not treated as the integer 0 for the comparison, the above code would never print "It was zero!". The programmer would be required to take responsibility for juggling the type of the value which comes out of the database. This is not desirable for a loosely typed language.

    Strict equality including type is tested using the "Is really, truly, honest to god equal to" operator, which is represented by the symbol "===".

    ReplyDelete
  6. I don't see how ('a'==0) is helpful


    $var = '123abc';

    if (123 == $var)
    {
    echo 'Whoda thunk it?';
    }


    It comes down to PHP's implicit conversion rules.

    I'm failing at thinking of a practical example, but that's the basic reason why you're seeing that behavior.

    Expansion:

    In your example, 'a' is converted to 0 (zero), for the comparison. Imagine that for the purpose of the comparison, it's equivalent to '0a'. (That's the numeral zero, not the letter 'o.')

    Further expansion:

    I thought there was a good example use case for this in the manual, but I failed to find it. What I did come across should help shed some light on this "illogical" situation.


    PHP is first and foremost a Web
    language, not a general-purpose
    scripting language. Since the Web is
    not typed and everything is a string,
    I had to do things slightly
    differently early on to make PHP do
    what people expected. Specifically,
    "123"==123 needs to be true in order
    to not have to type cast every single
    numeric user input.


    http://bugs.php.net/bug.php?id=48012

    That doesn't exactly answer the question, but it points in the general direction.

    ReplyDelete
  7. PHP is a loosely typed language, and allows you to compare values of different types without throwing errors, which makes it very easy to use but as you have found can cause some weird but logical outputs.

    Your first example:

    if(0=='a'){
    print ord(0)." should NEVER equal ".ord('a')."<br>";
    }


    When two different types of values are compared, one value is first turned into the same type as another via a cast and then compared. In the example of Int and String the string is converted into Int. When PHP turns a letter into a string it takes all the first numeric characters and then chops of the rest: i.e '123123afraa' becomes 123123, '9a9' becomes 9. If the string does not start with numerals it is given the value of 0.

    Therefor your example is really: 0===(string)'a' which is really 0===0 since 'a' does not start with a numeric. I think you were expecting PHP to return the value of 'a' in ASCII which it does not! This is really useful to sanitise strings, php rarely needs to deal with ascii values it is too high level for that. (Its for making websites!)

    When a string is compared to a boolean a value of '' or '0' are false, all other values are true. This is useful so you can check if a value is 'empty':

    url http://domain.com/?foo=

    if ($_GET['foo']))
    {
    // do something
    }

    When an integer is compared to a boolean the values of 0 is false other values are true, this is pretty standard.

    So, all in all you need to understand what happens when different types of variables are compared with the == operator. Also it is probably wise to realise that == is almost never what you want and using === (which will not typecast your values) is ALOT safer.

    ReplyDelete
  8. The code seems to emanate from a unit test for the purpose of catching failures, hence the seemingly weird comparisons. In the same light, it may be preparatory to the main unit test to confirm that the == operator is working properly - as it should.

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex