Skip to content

Instantly share code, notes, and snippets.

@damonjones
Created November 20, 2013 17:10
Show Gist options
  • Save damonjones/7566978 to your computer and use it in GitHub Desktop.
Save damonjones/7566978 to your computer and use it in GitHub Desktop.
UPC/EAN Validator
<?php
namespace Insig\UtilBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* Metadata for the UpcEanValidator.
*
* @Annotation
*/
class UpcEan extends Constraint
{
public $message = '{{ value }} is not a valid UPC/EAN.';
}
<?php
namespace Insig\UtilBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates a UPC/EAN using a variation of the Luhn Algorithm
*/
class UpcEanValidator extends ConstraintValidator
{
/**
* Validates a UPC/EAN number with a variation of the Luhn algorithm.
*
* @param mixed $value
* @param Constraint $constraint
*/
public function validate($value, Constraint $constraint)
{
// Must be a string
if (!is_string($value)) {
throw new UnexpectedTypeException($value, 'string');
}
// Must consist of 12 (UPC) or 13 (EAN) digits
if (!preg_match('/^\d{12,13}$/', $value)) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
}
$lastDigitIndex = strlen($value) - 1;
$accumulator = 0;
$checkDigit = (int) $value[$lastDigitIndex];
// reverse the actual digits (excluding the check digit)
$str = strrev(substr($value, 0, $lastDigitIndex));
/**
* Moving from right to left
* Even digits are just added
* Odd digits are multiplied by three
*/
$accumulator = 0;
for ($i = 0; $i < $lastDigitIndex; $i++) {
$accumulator += $i % 2 ? (int) $value[$i] : (int) $value[$i] * 3;
}
$checksum = (10 - ($accumulator % 10)) % 10;
if ($checksum !== $checkDigit) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
}
}
}
@ademlabs
Copy link

There is an error in this code, for example the EAN 3263852666610 will not be validated. The correct code should use $str (reversed EAN without check digit) to calculate the accumulator:

for ($i = 0; $i < $lastDigitIndex; $i++) {
    $accumulator += $i % 2 ? (int) $str[$i] : (int) $str[$i] * 3;
} 

Replace lines 45 to 47 inf file UpcEanValidator.php with above code.

@fulgurio
Copy link

need also a "return;" after the line 29, without you get 2 error messages

        if (!preg_match('/^\d{12,13}$/', $value)) {
            $this->context->addViolation($constraint->message, array('{{ value }}' => $value));
            return;
        }

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