A função
Uma só função valida o CNPJ numérico e o alfanumérico de 2026. O segredo é que cada caractere da base entra no módulo 11 como ord($c) - 48: assim '0'..'9' viram 0..9 (retrocompatível) e 'A'..'Z' viram 17..42.
function isValidCnpj(string $cnpj): bool {
$cnpj = strtoupper(preg_replace('/[.\/-]/', '', $cnpj));
if (!preg_match('/^[A-Z0-9]{12}\d{2}$/', $cnpj)) return false;
if (preg_match('/^(.)\1{13}$/', $cnpj)) return false; // repetidos
$w1 = [5,4,3,2,9,8,7,6,5,4,3,2];
$w2 = [6,5,4,3,2,9,8,7,6,5,4,3,2];
$dv = function (string $base, array $w): int {
$s = 0;
foreach ($w as $i => $p) $s += (ord($base[$i]) - 48) * $p;
$r = $s % 11; return $r < 2 ? 0 : 11 - $r;
};
$d1 = $dv(substr($cnpj, 0, 12), $w1);
$d2 = $dv(substr($cnpj, 0, 12) . $d1, $w2);
return $d1 === (int)$cnpj[12] && $d2 === (int)$cnpj[13];
}
var_dump(isValidCnpj('11.222.333/0001-81')); // true (numérico)
var_dump(isValidCnpj('12ABC34501DE35')); // true (alfanumérico, exemplo SERPRO)
var_dump(isValidCnpj('11.222.333/0001-00')); // falseO mesmo código valida os dois formatos: o numérico é um caso particular do alfanumérico. A base tem 12 posições (letras A–Z ou dígitos 0–9) e os 2 verificadores são sempre numéricos — por isso a regex termina em \d{2}.
Por que ord − 48
No módulo 11 cada posição vira um número antes de multiplicar pelo peso. Para tratar letra e dígito de forma uniforme, converte-se o caractere pelo seu código ASCII menos 48: '0' vira 0, '9' vira 9 e 'A' vira 17 (segue até 'Z' = 42). É exatamente o que ord($base[$i]) - 48 faz na soma. Os pesos vêm em duas listas ($w1 para o 1º dígito, $w2 para o 2º) e o resto da divisão por 11 define o verificador — $r < 2 ? 0 : 11 - $r. É o módulo 11 de sempre, só com a entrada normalizada.
A base oficial do SERPRO 12ABC34501DE produz DV 35: no 1º dígito a soma dá 459 (resto 8 → 11 - 8 = 3) e no 2º a soma dá 424 (resto 6 → 11 - 6 = 5). Detalhe do cálculo em calcular o dígito verificador alfanumérico.
Cuidados
- Regex só valida formato. Um padrão como
/^[A-Z0-9]{12}\d{2}$/confirma que são 12 posições alfanuméricas + 2 dígitos, mas não confere o verificador — quem faz isso é a função acima. Veja regex de CNPJ. - Sequências repetidas (
00000000000000) passariam no módulo 11, por isso a função as descarta antes com/^(.)\1{13}$/. - Só maiúsculas. O alfanumérico não aceita minúsculas — por isso a função faz
strtoupper()e remove./-antes de validar. - No banco, use texto. Colunas numéricas (
BIGINT) quebram com letras — guarde emCHAR(14)/VARCHAR. - Válido ≠ existe. A função confirma a consistência matemática, não se o CNPJ foi emitido a uma empresa real: CNPJ válido vs. CNPJ real.
- Alfanumérico entra em jul/2026 (IN RFB nº 2.229/2024), só para novos registros. O código já está pronto para os dois formatos.