Tuesday 26 May 2020

Spot The Bug: It's all about the breaks

Bug-hunting season is always on, and today we've got something that might not quite qualify as a bug since it appears to be working, unless you're super anal like I sometimes can be.
Give it up for Spot The Bug!

Still, it's a good lesson to learn, and will come in useful as a general rule of thumb. You'll see what I mean in a minute.

nl2br() is a PHP function that purportedly replaces new lines with HTML breaks. And we usually use it for cases where the raw string input is straight from the textbox and we want the text to display nicely on a web page. So let's say I had code like this.
<!DOCTYPE html>
<html>
<head>
    <title>nl2br test</title>
</head>
<body>
    <?php
    $str = "";

    if (isset($_POST["btnSubmit"]))
    {
        $str = $_POST["txtString"];
    }

    ?>

    <?php
        echo nl2br($str);
    ?>
    <hr />

    <h1>TEST FORM</h1>
    <form method="POST">
        <textarea name="txtString" cols="30" rows="10"><?php echo $str; ?></textarea>
        <button name="btnSubmit">Test</button>
    </form>
</body>

</html>


There's a form containing a text box and a submit button. And let's say I input this text and click the button.


With the nl2br() function applied, it looks like this.


Now my intention here was to write a function that worked just like nl2br(), just for a start, with the intention to add on to it later. Don't ask me why. Seems like a silly thing to do in hindsight, but there you go.
<!DOCTYPE html>
<html>
    <head>
        <title>nl2br test</title>
    </head>
    <body>
        <?php
        $str = "";

        if (isset($_POST["btnSubmit"]))
        {
            $str = $_POST["txtString"];
        }

        function replaceBreaks($str)
        {
            return str_replace("\r\n", "<br />", $str);
        }
        ?>

        <?php
            echo nl2br($str);
        ?>
        <hr />

        <h1>TEST FORM</h1>
        <form method="POST">
            <textarea name="txtString" cols="30" rows="10"><?php echo $str; ?></textarea>
            <button name="btnSubmit">Test</button>
        </form>
    </body>
</html>


Not only that, I wanted to print both the output from nl2br() and my function, and do a direct comparison using a conditional. Kind of like a very rudimentary unit test.
<!DOCTYPE html>
<html>
    <head>
        <title>nl2br test</title>
    </head>
    <body>
        <?php
        $str = "";

        if (isset($_POST["btnSubmit"]))
        {
            $str = $_POST["txtString"];
        }

        function replaceBreaks($str)
        {
            return str_replace("\r\n", "<br />", $str);
        }
        ?>

        <h1>RAW STRING</h1>
        <?php
            echo $str;
        ?>
        <hr />

        <h1>NL2BR</h1>
        <?php
            echo nl2br($str);
        ?>
        <hr />

        <h1>NEW FUNCTION</h1>
        <?php
            echo replaceBreaks($str);
        ?>
        <hr />

        <h1>NEW FUNCTION = NL2BR?</h1>
        <?php
            echo (nl2br($str) == replaceBreaks($str) ? "PASS" : "FAIL");
        ?>
        <hr />

        <h1>TEST FORM</h1>
        <form method="POST">
            <textarea name="txtString" cols="30" rows="10"><?php echo $str; ?></textarea>
            <button name="btnSubmit">Test</button>
        </form>
    </body>
</html>


And whoops! It said "FAIL". But why? The output looked identical.


What went wrong

And yes, it did appear to be identical. Appear being the operative word. When I viewed the source, this is what I saw.

The br tags were inserted, but the first set of output still had line breaks!


Why it went wrong

That was when I started taking a closer look at the nl2br() function, right at the source of truth. Most people are under the impression that nl2br() replaces carriage returns and line breaks with br tags.

But nope. This is what nl2br() actually does.
Returns string with <br /> or <br> inserted before all newlines (\r\n, \n\r, \n and \r).


That meant that instead of this...
Best regards<br />John Smith<br />CEO


...I was getting this!
Best regards<br />\r\nJohn Smith<br />\r\nCEO


How I fixed it

This was what I should have been doing for my function.
    function replaceBreaks($str)
    {
        return str_replace("\r\n", "<br />\r\n", $str);
    }


There it was... that beautiful PASS.


Moral of the story

There are a couple takeaways from this.

Just because text output looks correct on a web page, it doesn't necessarily mean the output is correct. It just means that the output is displayed correctly, or in this case, acceptably.

Also, I might have mentioned this before, but it bears repeating. When involving any built-in function, always be very clear what it actually does. I may have been the idiot who thought nl2br() only replaced carriage returns and new lines with HTML breaks, but I wasn't the only one.

Them's the breaks, kiddo!
T___T

No comments:

Post a Comment