Skip to content

Instantly share code, notes, and snippets.

@troelskn
Created October 14, 2011 18:24
Show Gist options
  • Save troelskn/1287893 to your computer and use it in GitHub Desktop.
Save troelskn/1287893 to your computer and use it in GitHub Desktop.
Luhn's algorithm in php
<?php
function is_valid_luhn($number) {
settype($number, 'string');
$sumTable = array(
array(0,1,2,3,4,5,6,7,8,9),
array(0,2,4,6,8,1,3,5,7,9));
$sum = 0;
$flip = 0;
for ($i = strlen($number) - 1; $i >= 0; $i--) {
$sum += $sumTable[$flip++ & 0x1][$number[$i]];
}
return $sum % 10 === 0;
}
@liamzdenek
Copy link

This function works perfectly if your number is already in a 16-character string, is_valid_luhn("4111111111111111")

But the function will fail if the string contains any symbols or spaces, is_valid_luhn("4111 1111 1111 1111") = false

This can be easily fixed by placing a preg_replace at the beginning of the function to strip all non-digit characters.

$number = preg_replace("/[^0-9]/", "", $number);

Copy link

ghost commented Jun 29, 2015

PHP:

function isValid($num) {
    $num = preg_replace('/[^\d]/', '', $num);
    $sum = '';

    for ($i = strlen($num) - 1; $i >= 0; -- $i) {
        $sum .= $i & 1 ? $num[$i] : $num[$i] * 2;
    }

    return array_sum(str_split($sum)) % 10 === 0;
}

Javascript:

function isValid(number) {
    var num = number.replace(/[^\d]/, '');
    var str = '';

    for (var i = num.length - 1; i >= 0; -- i) {
        str += i & 1 ? num[i] : (parseInt(num[i]) * 2).toString();
    }

    var sum = str.split('').reduce(function(prev, current) {
        return prev + parseInt(current);
    }, 0);

    return sum % 10 === 0;
};

@locoduffs
Copy link

= 0; $i--) { $sum += $sumTable[$flip++ & 0x1][$number[$i]]; } return $sum % 10 === 0; }

@ugokoli
Copy link

ugokoli commented May 29, 2017

        //Using Luhn's algorithm to validate a credit card

        public static function isValidCardNumber($number){
        	settype($number, 'string');
        	$number = preg_replace("/[^0-9]/", "", $number);
        	$numberChecksum= '';
		$reversedNumberArray = str_split(strrev($number));
		foreach ($reversedNumberArray as $i => $d) {
		    $numberChecksum.= (($i % 2) !== 0) ? (string)((int)$d * 2) : $d;
		}
		$sum = array_sum(str_split($numberChecksum));
                return ($sum % 10) === 0;
        }

@thomsonimjd
Copy link

is there any possibility to get bpay biller address and info by biller code?

@tflori
Copy link

tflori commented Mar 22, 2018

@mikemirten first I thought thats really small and simple to understand but unfortunately it's wrong. have you tried this with a credit card number of odd length? visas can have 13 - 16 numbers and that fails.

my solution is now:

function validateLuhn(string $number): bool
{
    $sum = 0;
    $revNumber = strrev($number);
    $len = strlen($number);

    for ($i = 0; $i < $len; $i++) {
        $sum += $i & 1 ? ($revNumber[$i] > 4 ? $revNumber[$i] * 2 - 9 : $revNumber[$i] * 2) : $revNumber[$i];
    }

    return $sum % 10 === 0;
}

@mogzol: you are right: the string sum does not make sense... So I removed it.

edit fixed the calculation without string split and unused variables

@mogzol
Copy link

mogzol commented Sep 12, 2019

Alternative version of @tflori's code that doesn't rely on strrev, array_sum or str_split, which makes it more readable IMO, and slightly faster (though at this point it's basically just the original code without the sum table):

function validateLuhn(string $number): bool
{
    $sum = 0;
    $flag = 0;

    for ($i = strlen($number) - 1; $i >= 0; $i--) {
        $add = $flag++ & 1 ? $number[$i] * 2 : $number[$i];
        $sum += $add > 9 ? $add - 9 : $add;
    }

    return $sum % 10 === 0;
}

@mogzol
Copy link

mogzol commented Sep 13, 2019

@tflori Your updated code doesn't work just FYI. You actually did need the array_sum. This is because of the first step outlined here: https://en.wikipedia.org/wiki/Luhn_algorithm#Description

If the result of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then add the digits of the result (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9)

Your original code achieved this by using a string and summing all the numbers in the string. My code uses the alternate method mentioned:

alternatively, the same final result can be found by subtracting 9 from that result (e.g., 16: 16 − 9 = 7, 18: 18 − 9 = 9)

But your newly edited code does neither, and as such returns false for many valid numbers.

@tflori
Copy link

tflori commented Sep 14, 2019

@mogzol ah f**k.. thanks for the info 👍

@pseudolimonada
Copy link

pseudolimonada commented Jan 11, 2024

In case anyone tries using @mogzol solution while working with the Luhn algorithm applied to a base higher than 10, for example to validate an alphanumeric string where each character can go from 0-9 or A-Z, meaning its value ranges from 0-36, you need to be more careful performing the total sum.

In the solution, when we're doing the total sum we are essentially ignoring the fact that Luhn algorithm only wants you to subtract 9 to values that have been doubled. It works for base 10 only because values that have not been doubled can't possibly be greater than 9.

To correctly implement a general any-base Luhn algorithm, you don't have this luxury, and must therefore apply both the doubling of the value and subsequent subtraction only IF the condition is met, in this case ($flag++ & 1), which by the way is quite an unreadable way to flip between true and false.

Here's a general readable example, assuming some valid string $number in base $base

$flip = false;
    $sum = 0;
    for ($i = strlen($number) - 1; $i >= 0; $i--) {
        $value = intval($number[$i], $base);
        if($flip){
            $value = $value * 2;
            $value = $value > 9 ? $value - 9 : $value;
        }
        $sum += $value;
        $flip = !$flip;
    }
    return $sum % 10 === 0;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment