SitePoint Sponsor

User Tag List

Results 1 to 11 of 11
  1. #1
    SitePoint Enthusiast
    Join Date
    May 2006
    Posts
    87
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    Smile Generate Modulus 10 / Luhn Check Digit in PHP help?

    Happy new year from Palo Alto! Please, I have been working for an inordinately long time trying to create some code for something that should be pretty basic. I have written some nice, simple PHP code that generates a random 11 digit alphanumeric codes, called $elevendigits, like this one:

    FFM00976YH5

    What I now need to do is write some code that generates a check digit according to the Modulus 10 - - sometimes called Luhn - - standard. Let's call it it $checkdigit. It will be the twelfth digit.

    I would have thought this an off-the-shelf function but, can't seem to get anything to work. It would need to:

    1. Convert letters to numbers according to their ordinal place, starting with A=10, such that M=22, for example;
    2. Starting with the right-most digit, multiply every other digit by 2;
    3. Convert any two-digit products from the preceding step into 2 one-digit numbers;
    4. Add all the digits together; and then
    5. Subtract the sum from the next highest number ending in zero. This is the check digit.

    Please, can anyone help me with the code necessary to accomplish this? I would have thought it easy, but am failing. Thank you sincerely!

  2. #2
    SitePoint Wizard TheRedDevil's Avatar
    Join Date
    Sep 2004
    Location
    Norway
    Posts
    1,196
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    It is a pretty simple method to write in PHP, as long as you understand the principle for how the Luhn Algorithm work, which it seems you do.

    The difference with your project is that you also need to consider letters, and convert them as you hit any.

    I am also not sure why you don't reuse the method you created to make these numbers for the validation as well? You would just need to change it from setting the last digit, to validating it instead.

    Give it another try and I'm sure your able to figure it out, remember modulus is your friend. If you run into problems, post your code here and we can take a look and help you out.

  3. #3
    From space with love silver trophy
    SpacePhoenix's Avatar
    Join Date
    May 2007
    Location
    Poole, UK
    Posts
    5,028
    Mentioned
    103 Post(s)
    Tagged
    0 Thread(s)
    http://www.mrexcel.com/forum/excel-q...ml#post1184172

    Found that via a google search, you could perhaps use that as a "blueprint" with each "step" being a seperate little section of code and wrap it up in a function.
    Community Team Advisor
    Forum Guidelines: Posting FAQ Signatures FAQ Self Promotion FAQ
    Help the Mods: What's Fluff? Report Fluff/Spam to a Moderator

  4. #4
    SitePoint Enthusiast
    Join Date
    May 2006
    Posts
    87
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thank you, The Red Devil and Space Phoenix, for your encouragement. Here's the code that I have so far:

    PHP Code:
    <?
    //generate a random string of 11 alphanumeric characters without vowels
    function random_string()
    {
        
    $character_set_array = array();
        
    $character_set_array[] = array('count' => 11'characters' => 'BCDFGHJKLMNPQRSTVWXYZ0123456789');
        
    $temp_array = array();
        foreach (
    $character_set_array as $character_set) {
            for (
    $i 0$i $character_set['count']; $i++) {
                
    $temp_array[] = $character_set['characters'][rand(0strlen($character_set['characters']) - 1)];
            }
        }
        
    shuffle($temp_array);
        return 
    implode(''$temp_array);
    }

    $randomdigits=random_string();

    //change all letters to numbers based on ordinal position and starting with B as eleven
    $randomdigits str_replace('B','11'$randomdigits);
    $randomdigits str_replace('C','12'$randomdigits);
    $randomdigits str_replace('D','13'$randomdigits);
    $randomdigits str_replace('F','15'$randomdigits);
    $randomdigits str_replace('G','16'$randomdigits);
    $randomdigits str_replace('H','17'$randomdigits);
    $randomdigits str_replace('J','19'$randomdigits);
    $randomdigits str_replace('K','20'$randomdigits);
    $randomdigits str_replace('L','21'$randomdigits);
    $randomdigits str_replace('M','22'$randomdigits);
    $randomdigits str_replace('N','23'$randomdigits);
    $randomdigits str_replace('P','25'$randomdigits);
    $randomdigits str_replace('Q','26'$randomdigits);
    $randomdigits str_replace('R','27'$randomdigits);
    $randomdigits str_replace('S','28'$randomdigits);
    $randomdigits str_replace('T','29'$randomdigits);
    $randomdigits str_replace('V','31'$randomdigits);
    $randomdigits str_replace('W','32'$randomdigits);
    $randomdigits str_replace('X','33'$randomdigits);
    $randomdigits str_replace('Y','34'$randomdigits);
    $randomdigits str_replace('Z','35'$randomdigits);
    ?>
    So now, we wind up with $randomdigits as only numberals, which is great. But alas, that's all I've been able to accomplish so far. I still need to multiply every other digit of $randomdigits by 2 starting with the right-most digit; convert any two-digit products from the preceding step into 2 one-digit numbers; add all the digits together; and finally
    subtract the sum from the next highest number ending in zero to yield the check digit.

    I am diligent, but stuck. Any help would be greatly appreciated! Thank you very much.
    Last edited by SpacePhoenix; Jan 3, 2013 at 00:34. Reason: placed php tags around php code

  5. #5
    SitePoint Wizard TheRedDevil's Avatar
    Join Date
    Sep 2004
    Location
    Norway
    Posts
    1,196
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    Hmm, ok. Yea, I can see why you might be a little stuck.

    You are looking on this from perhaps the wrong perspective, and perhaps a little too procedural If you have the time, I would recommend you to read up on OOP, it would greatly improve your development speed.

    I have wrote a function for you that should work for both generating the validation number, as well as validating a complete number as well.

    To use the different functions, you would change to the correct return method. One will give you the correct validation number that is missing, the other will tell you if the number is valid (bool).
    When fetching the correct validation number it is important that you append a "x" (or any other number or character) to the end of the string. I.e. in your first post you mentioned "FFM00976YH5", when passing this number along you would pass it as "FFM00976YH5x", and then just replacing the x afterwards with the correct validation number.

    PHP Code:
    function luhnAlgorithm($checkNumber) {
        
    $characters array_flip(range('B''Z'));
        
        
    $length strlen($checkNumber) - 1;
        
    $total_sum 0;
        
    $cur_num 0;
        
        for (
    $num=($length-1);$num >= 0;--$num) {
            if (!
    ctype_digit((string) $checkNumber[$num])) {
                
    $sum $characters[$checkNumber[$num]] + 11;
                }
            else {
                
    $sum $checkNumber[$num];
                }        
                
            if (
    $cur_num++ % == 0) {
                
    $sum *= 2;
                }

            if (
    $sum 9) {
                
    $sum substr($sum01) + substr($sum11);
                }
                
            
    $total_sum += $sum;
            }

        return (
    10 - ($total_sum 10));//Use this to return the missing validation number, note has to be passed with x in the end
        //return (($total_sum + $checkNumber[$length]) % 10 == 0); //Use this to validate the 12 digit number
        

    EDIT:
    Forgot to mention that I have not tested the code, but it should work. In the event you have any problems, please let us know what is happening and we can help sort it out.

  6. #6
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    72 Post(s)
    Tagged
    0 Thread(s)
    So, procedurally (Trying to wrap my head around the algorithm in 30 seconds)
    For a given string
    PHP Code:
    $string "FFM00976YH5"
    1: str_replace letters with numbers . This gives you a pure-digit string of variable length.
    PHP Code:
    $string str_replace(range('B','Z'),range('11','35'),$string
    2: Iterate to every other digit, starting from the right and walking back to the start.
    PHP Code:
    for ($x count($string)-1$x >= 0$x -= 2
    3: Double the value and Sum (via type juggling)
    PHP Code:
    $sum += array_sum(str_split(($string[$x] * 2))); 
    4: Apply the math X*9 Mod 10;
    PHP Code:
    $cd = ($sum 9) % 10
    $cd is now my Check Digit.

    Did i miss anything?
    Never grow up. The instant you do, you lose all ability to imagine great things, for fear of reality crashing in.

  7. #7
    SitePoint Wizard TheRedDevil's Avatar
    Join Date
    Sep 2004
    Location
    Norway
    Posts
    1,196
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by StarLion View Post
    Did i miss anything?
    Point one would be wrong if you are looking for a true implementation of the algorithm, since you would treat B as 11 and not as 1 and 1 later on.

    In your example the string "FFM00976YH5" would be treated as "1515220097634175" so the string goes from being 11 + Validation number to 16 + Validation number, and the length would vary depending on the number of characters in the string/number.

    Point two is also not correct as you just illiterate over ever second number, while you also need to add every odd number to the sum as well (without doubling it).

    Point three is correct, only thing is that you need the addition for the odd numbers which should not be doubled.


    Point four, I am not sure why you are multiplying with 9 here?


    On a side note if the validation is only for internal use, any way its implemented will work. The problem would be if you want it to work off other already established systems.

  8. #8
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    72 Post(s)
    Tagged
    0 Thread(s)
    Or did i misinterpret your desire to convert the numbers (whole numbers instead of individual digits)? That would change the code to something along the lines of...

    For a given string
    PHP Code:
    $string "FFM00976YH5"
    1: Iterate to every other character, starting from the right and walking back to the start.
    PHP Code:
    for ($x count($string)-1$x >= 0$x -= 2) { 
    2: str_replace letters with numbers .
    PHP Code:
    $char str_replace(range('B','Z'),range('11','35'),$string[$x]) 
    3: Double the value and Sum (via type juggling)
    PHP Code:
    $sum += array_sum(str_split(($char 2)));

    4: Apply the math X*9 Mod 10;
    PHP Code:
    $cd = ($sum 9) % 10
    $cd is now my Check Digit.

    (step 2 and 3 could be combined, but for clarity i left them seperate)
    Never grow up. The instant you do, you lose all ability to imagine great things, for fear of reality crashing in.

  9. #9
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    72 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TheRedDevil View Post
    Point one would be wrong if you are looking for a true implementation of the algorithm, since you would treat B as 11 and not as 1 and 1 later on.
    Altered in my second post (made while you were posting!)

    In your example the string "FFM00976YH5" would be treated as "1515220097634175" so the string goes from being 11 + Validation number to 16 + Validation number, and the length would vary depending on the number of characters in the string/number.
    hence "variable length".
    Point two is also not correct as you just illiterate over ever second number, while you also need to add every odd number to the sum as well (without doubling it).
    Ah... yes, that will necessitate a *little* extra code... below.
    Point four, I am not sure why you are multiplying with 9 here?
    According to wikipedia, there are two methods of obtaining the value;
    subtracting the modulo value from 10, or multiplying by 9 and taking the modulo of the result. Either way it's 2 mathematical operations.
    On a side note if the validation is only for internal use, any way its implemented will work. The problem would be if you want it to work off other already established systems.
    Entirely agree.

    Code modified:
    PHP Code:
    $string "FFM00976YH5";  
    $string strrev($string); //Turn the string around so that $x has meaning.
    for ($x 0$x strlen($string); $x++) { 
     
    $char str_replace(range('B','Z'),range('11','35'),$string[$x]);  
     
    $sum += array_sum(str_split(($char pow(2,(($x+1) % 2)))));  
    }
     
    $cd = ($sum 9) % 10
    Never grow up. The instant you do, you lose all ability to imagine great things, for fear of reality crashing in.

  10. #10
    SitePoint Enthusiast
    Join Date
    May 2006
    Posts
    87
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    I'm happy to report that I have a working cluster of code, thanks to the generous contributions of StarLion and The Red Devil. Part of the trouble I was having came from not recognizing that my host was running only PHP 4, which caused the str_split call to return undefined. But with encouragement from StarLion and The Red Devil, I plugged through and created a workaround using preg_split. Everything now works fine.

    I am extremely grateful to StarLion and The Red Devil, without whom I would not have figured this out. Thank you!

  11. #11
    From space with love silver trophy
    SpacePhoenix's Avatar
    Join Date
    May 2007
    Location
    Poole, UK
    Posts
    5,028
    Mentioned
    103 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kjm7267 View Post
    I'm happy to report that I have a working cluster of code, thanks to the generous contributions of StarLion and The Red Devil. Part of the trouble I was having came from not recognizing that my host was running only PHP 4, which caused the str_split call to return undefined. But with encouragement from StarLion and The Red Devil, I plugged through and created a workaround using preg_split. Everything now works fine.

    I am extremely grateful to StarLion and The Red Devil, without whom I would not have figured this out. Thank you!
    Ask you host if they have the option of running php5 as php4 is no longer supported. If they don't give some thought about moving hosts to one that does.

    Support for PHP 4 has been discontinued since 2007-12-31. Please consider upgrading to PHP 5.
    from: http://php.net/releases/index.php

    The current stable release listed is 5.4.10
    Community Team Advisor
    Forum Guidelines: Posting FAQ Signatures FAQ Self Promotion FAQ
    Help the Mods: What's Fluff? Report Fluff/Spam to a Moderator


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •