Last active
December 9, 2025 15:40
-
-
Save viniciusvts/511b18e0192e09c8e27ee614c6f35ea2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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