Skip to content

Instantly share code, notes, and snippets.

@fernandosavio
Created May 30, 2018 15:47
Show Gist options
  • Save fernandosavio/ffa417fa9f0e48879134735e5381a8d5 to your computer and use it in GitHub Desktop.
Save fernandosavio/ffa417fa9f0e48879134735e5381a8d5 to your computer and use it in GitHub Desktop.
Validadores de CPF e CNPJ mais legíveis e PHP 7.
<?php
class Utils
{
/**
* Tira qualquer caracter que não seja número da string
* @param string $str
* @return string
*/
public static function onlyNumbers(string $str) : string
{
return preg_replace('#\D+#', '', $str);
}
/**
* Garante que $cpf contenha apenas números de tenha a quantidade de dígitos correta
* @param string $cpf
* @return string
* @throws \InvalidArgumentException
*/
private static function sanitizeCPF(string $cpf) : string
{
$cpf = self::onlyNumbers($cpf);
if (strlen($cpf) !== 11)
throw new \InvalidArgumentException("CPF deve ter 9 números");
return $cpf;
}
/**
* Garante que $cnpj contenha apenas números de tenha a quantidade de dígitos correta
* @param string $cnpj
* @return string
* @throws \InvalidArgumentException
*/
private static function sanitizeCNPJ(string $cnpj) : string
{
$cnpj = self::onlyNumbers($cnpj);
if (strlen($cnpj) !== 14)
throw new \InvalidArgumentException("CNPJ deve ter 14 números");
return $cnpj;
}
/**
* Valida os dígitos verificadores com código mais moderno e legível dos que encontrei na internet.
* @see http://www.geradorcpf.com/algoritmo_do_cpf.htm
* @param string $cpf Uma string com o CPF (com máscara ou apenas números)
* @return bool verdadeiro se o CPF é válido
*/
public static function validaCPF(string $cpf) : bool
{
// prepara a string para trabalhar nela
try {
$cpf = self::sanitizeCPF($cpf);
} catch (\InvalidArgumentException $e) {
return false;
}
// Não pode ser números repetidos. Ex.: 000.000.000-00, 333.333.333-33, etc...
if (preg_match('#^(\d)\1{10}$#', $cpf)) {
return false;
}
/* quebra "11122233399" para:
$numeros = [1, 1, 1, 2, 2, 2, 3, 3, 3];
$verificadores = [9, 9]; */
list($numeros, $verificadores) = array_chunk(array_map('intval', str_split($cpf)), 9);
// Multiplica cada elemento com os números correspondentes e então soma tudo
$soma = array_sum(array_map(function (int $num, int $num_aux) {
return $num * $num_aux;
}, $numeros, range(10, 2)));
// Calcula o dígito verificador
$digito = $soma % 11;
$digito = $digito < 2 ? 0 : 11 - $digito;
// Se o primeiro dígito verificador não bateu, o CNPJ é inválido
if ($verificadores[0] != $digito) {
return false;
}
// Prepara as variáveis para a próxima iteração do algoritmo
$numeros[] = $digito;
// Soma o resultado das multiplicações
$soma = array_sum(array_map(function (int $num, int $num_aux) {
return $num * $num_aux;
}, $numeros, range(11, 2)));
// Calcula o segundo dígito verificador
$digito = $soma % 11;
$digito = $digito < 2 ? 0 : 11 - $digito;
// Se o segundo dígito confere então o CNPJ é válido
return $digito === $verificadores[1];
}
/**
* Valida os dígitos verificadores com código mais moderno e legível dos que encontrei na internet.
* @see http://www.geradorcnpj.com/algoritmo_do_cnpj.htm
* @param string $cnpj Uma string com o CPF (com máscara ou apenas números)
* @return bool verdadeiro se o CNPJ é valido
*/
public static function validaCNPJ(string $cnpj) : bool
{
// prepara a string para uso
try {
$cnpj = self::sanitizeCNPJ($cnpj);
} catch (\InvalidArgumentException $e) {
return false;
}
// Não pode ser números repetidos. Ex.: 00.000.000/0000-00, 333.333.333-33, etc...
if (preg_match('#^(\d)\1{13}$#', $cnpj)) {
return false;
}
/* quebra "11222333000199" para:
$numeros = [1, 1, 2, 2, 2, 3, 3, 3, 0, 0, 0, 1];
$verificadores = [9, 9]; */
list($numeros, $verificadores) = array_chunk(array_map('intval', str_split($cnpj)), 12);
// Valores que serão multiplicados pelos números
$aux = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
// Multiplica cada elemento de $aux com os números correspondentes e então soma tudo
$soma = array_sum(array_map(function (int $num, int $num_aux) {
return $num * $num_aux;
}, $numeros, $aux));
// Calcula o dígito verificador
$digito = $soma % 11;
$digito = $digito < 2 ? 0 : 11 - $digito;
// Se o primeiro dígito verificador não bateu, o CNPJ é inválido
if ($verificadores[0] != $digito) {
return false;
}
// Prepara as variáveis para a próxima iteração do algoritmo
array_unshift($aux, 6);
$numeros[] = $digito;
// Soma o resultado das multiplicações
$soma = array_sum(array_map(function (int $num, int $num_aux) {
return $num * $num_aux;
}, $numeros, $aux));
// Calcula o segundo dígito verificador
$digito = $soma % 11;
$digito = $digito < 2 ? 0 : 11 - $digito;
// Se o segundo dígito confere então o CNPJ é válido
return $digito === $verificadores[1];
}
/**
* Formata CPF para padrão "###.###.###-##"
* @param string $cpf
* @return string
*/
public static function formatCPF(string $cpf) : string
{
$cpf = self::sanitizeCPF($cpf);
$matches = [];
preg_match('~^(\d{3})(\d{3})(\d{3})(\d{2})$~', $cpf, $matches);
list( , $part1, $part2, $part3, $verificador) = $matches;
return "$part1.$part2.$part3-$verificador";
}
/**
* Formata CNPJ para padrão "##.###.###/####-##"
* @param string $cnpj
* @return string
*/
public static function formatCNPJ(string $cnpj) : string
{
// garante que a string contém apenas números
$cnpj = self::onlyNumbers($cnpj);
// valida a string
if (strlen($cnpj) !== 14) {
throw new \InvalidArgumentException("CNPJ deve ter 14 números");
}
$matches = [];
preg_match('~^(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})$~', $cnpj, $matches);
list( , $part1, $part2, $part3, $part4, $verificador) = $matches;
return "$part1.$part2.$part3/$part4-$verificador";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment