Notes on codes, projects and everything

Weekend Code Quiz – Easy Template Parser

A friend of mine recently posted a screenshot containing a code snippet for a fairly straight forward problem. So after reading the solution I suddenly had the itch to propose another solution that I initially thought would be better (SPOILER: Turns out it isn’t). Then mysteriously I stuck myself to my seat and started coding an alternative solution to it instead of playing Diablo 3 just now.

The problem is easy, it is a very simplified templating script. Let’s assume there’s no edge cases (because there are tons of different cases one would need to care about while writing a parser), just plainly turn this statement

I have a [##1##] and [##2##], and [##three##]

into


I have <input type="text" name="input1"> and <input type="text" name="input2">, and <input type="text" name="inputthree">

The posted code snippet involves a lot of exploding, and I saw a possibility of using sscanf for this purpose (the first input he gave me was without the [##three##] tag). However, before I start with sscanf I decided to give regex a try. As the poster also wishes to return the matched value (‘1’ in [##1##]), so the solution to this is


function lazy_regex($input) {
    $regex = '/(\[##([^#]+)##\])/';

    return preg_match_all($regex, $input, $result) !== FALSE ?
        array(
            $result[2],
            preg_replace($regex, '<input type="text" name="input$2">', $input))
        : FALSE;
}

Short, simple and just nice.

However nothing is perfect IRL, using regex as solution is often the last thing one would want to do. Hence I continued with the previously mentioned sscanf part.


function pretty_sscanf($input) {
    $result = array_map(
        function($token) {
            $scan_result = sscanf($token, '[##%[^#]##]%s');

            return array(
                $scan_result[0],
                empty($scan_result[0]) ?
                    $token
                    : vsprintf('<input type="text" name="input%s">%s', $scan_result));
        },
        explode(' ', $input));

    return array_reduce(
        $result,
        function($current, $next) {
            return array(
                empty($next[0]) ? $current[0] : array_merge($current[0], array($next[0])),
                trim(sprintf('%s %s', $current[1], $next[1])));
        },
        array(array(), NULL));
}

Obviously this piece of code requires PHP 5.3 and up. However this friend of mine unfortunately still stuck at 5.0, which is supposedly in EOL state. So a friendly reminder, for the love of humanity, please upgrade to at least PHP 5.4 and up (even better if upgraded to 5.6.0 which is just released from the oven).

Everything should work fine and dandy, however there is a problem with this solution, it does not work for string as follows

I have a [##1##][##2##]

I am sure there are more.

This particular case can be solved, but this involves writing more code where I simply don’t have the motivation to do so. As this is not going to hit to production so I am gonna leave this as it is. Another problem with this supposedly “simple and easy” templating system is that there are numerous edge cases that one may have not thought of. As PHP community is growing in a good way these days, I am sure there are great folks publishing libraries for this problem at places like Packagist.

Guess I should go grab some food to eat, and continue hurting Diablo (vice-versa, if you insist).

leave your comment

name is required

email is required

have a blog?

This blog uses scripts to assist and automate comment moderation, and the author of this blog post does not hold responsibility in the content of posted comments. Please note that activities such as flaming, ungrounded accusations as well as spamming will not be entertained.

Click to change color scheme