Skip to content

Instantly share code, notes, and snippets.

@viniciusvts
Last active December 9, 2025 15:40
Show Gist options
  • Select an option

  • Save viniciusvts/511b18e0192e09c8e27ee614c6f35ea2 to your computer and use it in GitHub Desktop.

Select an option

Save viniciusvts/511b18e0192e09c8e27ee614c6f35ea2 to your computer and use it in GitHub Desktop.
<?php
/**
* Helper para validação e cálculo de CNPJ Alfanumérico.
* baseado na especificação oficial da Receita Federal do Brasil.
* @see https://servicos.receitafederal.gov.br/servico/cnpj-alfa/validar
* @author Vinicius de Santana <[email protected]>
*/
class CNPJAlfanumerico {
private const TAMANHO_CNPJ_SEM_DV = 12;
private const REGEX_CNPJ_SEM_DV = '/^([A-Z\d]){12}$/';
private const REGEX_CNPJ = '/^([A-Z\d]){12}(\d){2}$/';
private const REGEX_CARACTERES_MASCARA = '/[.\/-]/';
private const REGEX_CARACTERES_NAO_PERMITIDOS = '/[^A-Z\d.\/-]/i';
private const CNPJ_ZERADO = "00000000000000";
// "0".charCodeAt(0) no JS é igual a 48
private const VALOR_BASE = 48;
private static array $pesosDV = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
public static function isValid(string $cnpj): bool
{
// Verifica caracteres não permitidos
if (!preg_match(self::REGEX_CARACTERES_NAO_PERMITIDOS, $cnpj)) {
$cnpjSemMascara = self::removeMascaraCNPJ($cnpj);
// Verifica o formato e se não é tudo zerado
if (preg_match(self::REGEX_CNPJ, $cnpjSemMascara) && $cnpjSemMascara !== self::CNPJ_ZERADO) {
// substr no PHP funciona igual substring no JS (start, length opcional)
$dvInformado = substr($cnpjSemMascara, self::TAMANHO_CNPJ_SEM_DV);
try {
$dvCalculado = self::calculaDV(substr($cnpjSemMascara, 0, self::TAMANHO_CNPJ_SEM_DV));
return $dvInformado === $dvCalculado;
} catch (\Exception $e) {
return false;
}
}
}
return false;
}
public static function calculaDV(string $cnpj): string
{
if (!preg_match(self::REGEX_CARACTERES_NAO_PERMITIDOS, $cnpj)) {
$cnpjSemMascara = self::removeMascaraCNPJ($cnpj);
if (preg_match(self::REGEX_CNPJ_SEM_DV, $cnpjSemMascara) &&
$cnpjSemMascara !== substr(self::CNPJ_ZERADO, 0, self::TAMANHO_CNPJ_SEM_DV)) {
$somatorioDV1 = 0;
$somatorioDV2 = 0;
for ($i = 0; $i < self::TAMANHO_CNPJ_SEM_DV; $i++) {
// ord() no PHP é equivalente ao charCodeAt() do JS
// Acessamos o caractere da string como um array: $string[$i]
$asciiDigito = ord($cnpjSemMascara[$i]) - self::VALOR_BASE;
$somatorioDV1 += $asciiDigito * self::$pesosDV[$i + 1];
$somatorioDV2 += $asciiDigito * self::$pesosDV[$i];
}
$dv1 = ($somatorioDV1 % 11) < 2 ? 0 : 11 - ($somatorioDV1 % 11);
$somatorioDV2 += $dv1 * self::$pesosDV[self::TAMANHO_CNPJ_SEM_DV];
$dv2 = ($somatorioDV2 % 11) < 2 ? 0 : 11 - ($somatorioDV2 % 11);
return "{$dv1}{$dv2}";
}
}
throw new \Exception("Não é possível calcular o DV pois o CNPJ fornecido é inválido");
}
public static function removeMascaraCNPJ(string $cnpj): string
{
// preg_replace é a versão do replace com Regex no PHP
return preg_replace(self::REGEX_CARACTERES_MASCARA, "", $cnpj);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment