Validating a French SIRET number with the Luhn algorithm

Published on 2020-09-24 • Modified on 2020-09-24

In this snippet, we will see how to check if a French SIRET number is valid with the Luhn algorithm. Check out the Wikipedia page for more explanations. Here you have the main code that returns is the number is valid or not. Now, you can inject this service in a custom validator to make things clean. Note that even a number a considered valid, it doesn't mean that's there is an actual company associated to it.


<?php

declare(strict_types=1);

namespace App\Tools;

class Siret
{
    public const SIRET_LENGTH = 14;

    /**
     * Be careful, it doesn't handle exceptions like "MONACOCONFO001".
     *
     * @see https://en.wikipedia.org/wiki/SIRET_code
     */
    public function isValid(?string $siret): bool
    {
        $siret = trim((string) $siret);
        if (!is_numeric($siret) || \strlen($siret) !== self::SIRET_LENGTH) {
            return false;
        }

        $sum = 0;
        for ($i = 0; $i < self::SIRET_LENGTH; ++$i) {
            if ($i % 2 === 0) {
                $tmp = ((int) $siret[$i]) * 2;
                $tmp = $tmp > 9 ? $tmp - 9 : $tmp;
            } else {
                $tmp = $siret[$i];
            }
            $sum += $tmp;
        }

        return !($sum % 10 !== 0);
    }
}

 ≪ this.showUnitTest ? this.trans.hide_unit_test : this.trans.show_unit_test ≫  More on Stackoverflow   Read the doc  More on the web  Random snippet

  Work with me!

<?php

declare(strict_types=1);

namespace App\Tests\Integration\Controller\Snippets;

use App\Tools\Siret;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
 * @see Siret
 */
final class Snippet114Test extends KernelTestCase
{
    private Siret $siret;

    protected function setUp(): void
    {
        $this->siret = self::getContainer()->get(Siret::class);
    }

    /**
     * @return iterable<int, array{0: string, 1: bool}>
     */
    public function siretProvider(): iterable
    {
        // NOK
        yield ['', false];
        yield ['1234567890123', false];
        yield ['1234567890123A', false];
        yield ['MONACOCONFO001', false];
        yield ['21218711783329', false]; // generated with random_int
        yield ['92958655678109', false]; // generated with random_int

        // OK
        yield ['80365813700036', true];
    }

    /**
     * @dataProvider siretProvider
     *
     * @see Siret::isValid
     */
    public function testIsValid(string $siret, bool $isValid): void
    {
        self::assertSame($this->siret->isValid($siret), $isValid);
    }
}