Use as little code as possible when writing PHP

I’ve commented on this many times in the past across a number of forums and blogs. If you use as little PHP as possible to get something done, it is usually far easier to keep track of, both for yourself and for future readers of your code. Here’s a typical example, taken from the osCommerce forum;

Does anyone know how to show credit card numbers seperated by dashes like on admin/orders.php thanks in advance for the help

What this guy wants is a credit card number to be written like this: 4111-1111-1111-1111 instead of 4111111111111111 – quite why he wants it is beside the point as he should not be storing the info anyway in my opinion, but anyway, that doesn’t really matter as the point of this article is not about the legalities of storing CC numbers, but more about writing code nicely…

No-one replied to his thread, so he came up with a workable solution himself;

[php]$cc_p1=substr($order->info[‘cc_number’],0,4);
$cc_p2=substr($order->info[‘cc_number’],4,4);
$cc_p3=substr($order->info[‘cc_number’],8,4);
$cc_p4=substr($order->info[‘cc_number’],12,4);
echo $cc_p1 . ‘-‘ . $cc_p2 . ‘-‘ . $cc_p3 . ‘-‘ . $cc_p4;[/php]

Here he is splitting the $order->info[‘cc_number’] into 4’s and adding a – between each. Easy enough. But look at the ugly code!

My solution is like this:

[php]echo rtrim(chunk_split($order->info[‘cc_number’], 4 , “-“), “-“);[/php]

Here I am using chunk_split to split the $order->info[‘cc_number’] into 4’s and adding a – at the same time after (not between) each block of 4. The only problem is that this will add a – onto the end of the number – to combat this I am using rtrim to take off the end – : so I go from this 411111111111111 to this: 4111-1111-1111-1111- to this: 4111-1111-1111-1111

Easy as 123? Well maybe not if you are not that skilled in PHP 😉

Of course, I am not saying that my way is the right way – there are probably many ways in which the same effect can be achieved. What you have to agree with is that my line of code is much sexier than the first block of code posted and much easier to read!

Have fun with PHP and osCommerce! You can click the links in the PHP snippets above to learn more about rtrim, chunk_split etc

4 Replies to “Use as little code as possible when writing PHP”

  1. Ooh, can I play? I choose:

    echo implode(‘-‘, str_split($order->info[‘cc_number’], 4));

    which I find easier to read (we are splitting the string every four characters and then joining it back together with – between the pieces). It also only requires the separator to be specified once (consider what happens if you change the separator for the chunk_split but not the rtrim).

    I find the chunk_split behavior of adding a character at the end which then needs to be removed counter-intuitive. The substr version is actually easier for me to read, albeit verbose. The chunk_split version might be faster though.

    Using minimal code isn’t just a PHP thing. It’s true of pretty much any coding language. The more code, the more likely that there is a bug (potentially a typo) and the harder it is to debug or change behavior.

    Note that in this particular case, the substr code is not robust in the face of credit cards of more than sixteen characters. All it would take would be one provider with a non-standard credit card length over sixteen characters and it will silently drop part of the credit card info. Certainly fixable: remove the ,4 after the 12 or

    $cc_output = ”;
    $cc_temp = (string)$order->info[‘cc_number’];
    while ( strlen($cc_temp) > 4 ) {
    $cc_output .= substr($cc_temp, 0, 4) . ‘-‘;
    $cc_temp = substr($cc_temp, 4);
    }
    echo $cc_output . $cc_temp;

    would probably do it but is even more code.

    None of these solutions would really work well for a thirteen character credit card number. Note that the thirteen character split is normally 4 3 3 3. You’d pretty much have to handle that case specially to produce the changing chunk length. E.g. something like

    if ( strlen($order->info[‘cc_number’]) == 13 ) {
    // since we want 4 3 3 3, split the number into
    // a first digit and a twelve digit string
    $first_digit = substr($order->info[‘cc_number’], 0, 1);
    $rest_of_number = substr($order->info[‘cc_number’], 1);

    // then split the twelve into four groups of three
    // then join the groups with – between them
    // then put the first digit back in front
    // and we have four digits in the first group
    // and three in each of the other groups
    echo $first_digit . implode(‘-‘, str_split($rest_of_number, 3));
    } else {
    echo implode(‘-‘, str_split($order->info[‘cc_number’], 4));
    }

    I used temporary variables with the substr to make the code more self commenting. The only real advantage of using implode and str_split in the 13 case is that it allows us to specify the separator and piece length (3) only once. With substr, we’d have to rewrite it multiple times. Because the piece length is three, we also could use number_format for the 13 case, as so:

    echo $first_digit . number_format($rest_of_number, 0, ”, ‘-‘);

    but I don’t really like that as it relies on the thousands separator being used between groups of three digits. It also converts the number to a float, which is not necessary here. Not robust in the face of letters in the credit card “number”.

  2. How about:

    preg_replace(‘/([0-9]{4})([0-9]{3,4})([0-9]{3,4})([0-9]{3,})/’, ‘$1-$2-$3-$4’, $order->info[‘cc_number’]);

    That will match a 16-digit number to 4-4-4-4, a 13 digit number to 4-3-3-3, and will fill the spaces up including a 19 digit number as 4-4-4-7

    And it’s only 1 PHP function!

  3. Excellent – love it when a blog post of mine leads to more (and better) solutions. Now we have 4 solutions doing the same thing. And that means more choidce, more ideas and more learning for all of us. Sweet.

    Cheers, Guys

Leave a Reply

Your email address will not be published. Required fields are marked *