PHP: validação de Dados Nacionais (BR)
Introdução
Ao longo dos últimos anos, a presença da internet se faz cada vez mais importante em nossas vidas, seja para assistir uma aula, realizar alguma matrícula de curso, pedir uma pizza no delivery ou até mesmo comprar um Playstation 5, não é mesmo?
E durante a navegação podemos esbarrar com algum formulário que precise coletar informações como nome, endereço e número de documentos de identificação. Por isso, quem trabalha na área de programação deve garantir que os dados coletados estejam formatados corretamente e livres de códigos maliciosos.
Neste contexto, surgiram diversas técnicas com a intenção de formatar e validar dados. Uma delas é chamada de expressões regulares, ou o termo abreviado em inglês RegEx, que possui a finalidade de analisar a cadeia de caracteres e identificar padrões para extrair alguma informação ou validar algum dado.
Neste artigo, com a ajuda do PHP, vamos aprender como realizar validações no padrão brasileiro por meio de Expressões Regulares (RegEx), cálculo de dígitos verificadores e algumas facilidades que o PHP proporciona. Vamos validar os campos de:
- CPF;
- CNPJ;
- CEP;
- Telefone;
- e datas.
Além disso:
- Vamos trabalhar em prol de uma demanda de uma empresa fictícia, a Adopet;
- Vamos compreender de forma rudimentar como integrar o front-end e o back-end.
Vamos nessa?
Demanda da Adopet
Antes de darmos início ao desenvolvimento, preciso que saiba que vamos trabalhar juntos em uma demanda vinda da Adopet, uma empresa fictícia que possui o propósito de estimular a adoção de cães e gatos.
A Adopet precisa reunir pessoas que estejam interessadas em adotar gatos, por isso, eles requisitaram uma página web de cadastro para que as pessoas possam demonstrar o seu interesse.
Dessa forma, uma equipe preparou o seguinte front-end, mostrado na tela abaixo:
Seguindo a demanda da Adopet e do front-end ilustrado acima, nosso trabalho é realizar as seguintes validações no back-end com o PHP:
- Validar o padrão de formatação da Data de nascimento;
- Validar o padrão de formatação do CEP;
- Validar o padrão de formatação do Telefone;
- Validar o padrão de formatação do CPF;
- Validar os dígitos verificadores do CPF;
- Validar o padrão de formatação do CNPJ;
- Validar os dígitos verificadores do CNPJ.
Além disso, a empresa Adopet pediu que seja retornado um JSON com CPF ou CNPJ; ou seja, os dados serão de uma pessoa física ou pessoa jurídica.
O exemplo abaixo mostra o retorno em JSON de uma pessoa jurídica fictícia:
{
"nome": "Vinicius Louzada",
"email": "[email protected]",
"cnpj": "05555382000133",
"cep": "04101300",
"cidade": "São Paulo",
"estado": "SP",
"logradouro": "Rua Vergueiro",
"bairro": "Vila Mariana",
"numero": "3185",
"data-nascimento": "1999-12-20",
"telefone": "1141183319"
}
Caso contrário deverá ser exibido uma notificação na página de cadastro informando que os dados estão inválidos.
Na próxima seção vamos discutir se as validações no front-end são suficientes para a integridade dos dados de uma aplicação.
Afinal, as validações no front-end não são suficientes?
Em alguns formulários é possível notar a existência de validações de campos obrigatórios e máscaras de texto para CPF e CEP, conforme a animação abaixo do formulário da Adopet:
Embora haja uma certa validação e máscaras de texto no formulário da Adopet, não devemos confiar nos dados que virão do front-end, pois uma pessoa mal intencionada pode desativar o Javascript, remover o HTML ou enviar uma requisição de um outro lugar que não seja um navegador web, essas e outras ações poderiam comprometer a integridade do nosso sistema.
Por isso, as validações sempre devem existir no back-end, pois garantimos que as regras de negócios serão obedecidas.
Importante lembrar que as validações no front-end só servem para auxiliar o usuário durante a sua experiência na página web.
No próximo tópico, veremos mais detalhadamente o funcionamento de validações no back-end.
Como funcionam as validações no back-end?
As validações no back-end são realizadas através de uma linguagem de programação e uma lógica que atenda às regras de negócios definidas nos requisitos do projeto.
As validações geralmente seguem o fluxo mostrado abaixo:
- Entrada: Nesta etapa inicial o usuário insere seus dados no Formulário da Adopet e nosso back-end recebe essa informação formatada (pode ser sem formatação também, isso depende do time de desenvolvimento).
- Validação: Nesta segunda etapa o back-end recebe os dados (nome, e-mail, CEP, CPF, entre outros) e executa as validações para verificar se os dados que chegaram são válidos.
- Tratamento: Nesta terceira etapa é realizada a limpeza ou manipulação dos dados, de acordo com o padrão da empresa ou projeto. Ou seja, é comum que documentos pessoais sejam salvos no banco de dados sem formatação. Ex: CNPJ: 05555382000133.
- Banco de dados: Nesta última etapa são enviados, para o banco de dados, os registros validados, tratados e manipulados pelo nosso back-end.
Na próxima seção, vamos falar sobre um método de validação através de Expressões Regulares (RegEx). Bora?!
Validações através de RegEx
Como foi mencionado na introdução deste artigo, utilizaremos as Expressões Regulares (RegEx) para identificar padrões em uma cadeia de caracteres da esquerda para a direita, com o objetivo de realizar validações e substituições em uma string.
Vamos montar uma expressão regular para a validação de um telefone; para isso, vamos adicionar duas barras /
, pois toda RegEx começa com /
e finaliza com /
:
//
Tudo que estiver dentro das duas barras /
será o que desejamos capturar com esta expressão regular. E qual é a expressão ou padrão que desejamos capturar?
Segundo o padrão brasileiro, o formato de telefone segue o padrão de:
+55 + DDD + Número de telefone
Por exemplo:
+55 (00) 00000-0000
Para essa ocasião, vamos considerar o seguinte padrão que nos foi solicitado aos requisitos do projeto apresentado na seção da Demanda da Adopet:
(00) 00000-0000
Para indicar o início e o final da pesquisa da expressão regular, vamos adicionar ^
e $
, respectivamente:
/^$/
Agora devemos indicar que haverá um caractere obrigatório de abertura de parênteses:
(
, para isso adicionaremos um contra-barra \
e o caractere obrigatório (
:
/^\($/
Após a abertura dos parênteses, precisamos capturar 2 números do DDD. Para isso, a nossa expressão deverá saber que os números variam de 0 a 9, e podemos informar este intervalo entre colchetes []
da seguinte maneira:
/^\([0-9]$/
Dessa forma, podemos ter variações de 0 a 9, mas quantos números obedecem a esta captura? São 2 números, não é mesmo? Pois são do DDD, então devemos informar que serão 2 números que obedeceram ao intervalo, isso é informado entre chaves {}
, da seguinte forma:
/^\([0-9]{2}$/
Agora precisamos indicar que esperamos um caractere obrigatório de fechamento de parênteses, para isso adicionaremos um contra-barra \
e o caractere obrigatório )
/^\([0-9]{2}\)$/
Perfeito! Com essa expressão já conseguimos capturar a parte do DDD de uma string de Telefone:
(00) 00000-0000
Em seguida, é possível notar que há um espaço entre o DDD e os próximos 5 números. Então, precisamos apenas adicionar um caractere de “espaço”:
/^\([0-9]{2}\) $/
Com o caractere de espaço adicionado, precisamos informar que o próximo dígito é opcional, pois se o número do telefone for de um telefone móvel, haverá o prefixo 9; se for um telefone fixo, não haverá nenhum número, e serão apenas 4 números. Para informar que um número é opcional adicionamos o caractere de interrogação ?
depois do número:
/^\([0-9]{2}\) 9?$/
Adiante, precisamos informar que haverá 4 repetições de números que obedecem o intervalo de [0-9]:
/^\([0-9]{2}\) 9?[0-9]{4}$/
Perfeito! Com essa expressão já conseguimos capturar a parte do DDD + 5 próximos números de uma string de Telefone:
(00) 00000-0000
Agora, precisamos informar que haverá um caractere obrigatório de hífen -
; para isso adicionaremos um contra-barra \
e o caractere obrigatório -
:
/^\([0-9]{2}\) 9?[0-9]{4}\-$/
Para finalizar, informamos que haverá mais 4 repetições de números que obedecem o intervalo de [0-9]:
/^\([0-9]{2}\) 9?[0-9]{4}\-[0-9]{4}$/
Perfeito! Com essa expressão já conseguimos capturar uma string de Telefone completa! Confira:
(00) 00000-0000
Para confirmar que a RegEx irá atender nossos requisitos, podemos acessar o site do regex101 e testá-la.
Utilizaremos a expressão regular que montamos após a análise acima: /^\([0-9]{2}\) 9?[0-9]{4}\-[0-9]{4}$/
; além disso faremos o primeiro teste com um número de telefone fixo: (11) 4118-3319.
O resultado do teste é mostrado na figura abaixo:
Na imagem acima é possível notar que há 4 divisões na página do regex101:
- Regular Expression (Expressão Regular): Nesta divisão, adicionaremos nossa expressão regular. Ex:
/^\([0-9]{2}\) 9?[0-9]{4}\-[0-9]{4}$/
- Test String (Teste de String): Nesta divisão, adicionaremos um texto que desejamos testar. Ex: (11) 4118-3319
- Explanation (Explicação): Nesta divisão, o site nos informa uma breve explicação da RegEx adicionada na divisão Regular Expression
- Match Information (Correspondência de Informação): Nesta divisão, o site nos informa se a string analisada corresponde à expressão regular, e quantas correspondências foram encontradas
Vamos fazer um teste com o padrão de um telefone móvel: (11) 98451-0258
.
A figura abaixo mostra o resultado:
Bingo! Funcionou!
Para finalizar, faremos um teste com o número de telefone: 1198451-0258
. Nós sabemos que este padrão de telefone não é válido de acordo com as regras de negócio, mas precisamos saber se a nossa expressão regular irá identificar isto também.
A imagem a seguir mostra o resultado:
A mensagem retornada foi “Your regular expression does not match the subject string” que em português quer dizer “Sua expressão regular não corresponde à string sujeita”.
Em síntese, montamos uma RegEx que valida a formatação de um telefone móvel e fixo. Além disso, realizamos testes na página do regex101 para garantir que a expressão regular funciona.
Na próxima seção vamos conhecer os arquivos do projeto.
Vale salientar que vamos utilizar a linguagem de programação PHP para construirmos o back-end, então precisamos ter conhecimentos básicos da linguagem e de PHP na Web.
Preparando o ambiente
Para dar continuidade ao desenvolvimento da página de cadastro da Adopet, a equipe de front-end nos forneceu o link do projeto.
Vamos abrir o terminal ou prompt de comando na área de trabalho e digitar o seguinte comando para baixar o projeto para o nosso computador:
git clone --branch front-end https://github.com/vinelouzada/adopet
Vale lembrar que precisamos baixar e instalar o Git no computador, para que possa ser utilizado o comando acima.
Vamos abrir o projeto em alguma IDE ou Editor de Texto.
Ao abrir, visualizamos a seguinte estrutura de pastas:
Na sequência, a lista abaixo traz o detalhamento de cada pasta referente à figura acima:
- css: Nesta pasta estão armazenados os códigos de estilização da página (Cores, posicionamentos dos elementos e etc).
- img: Nesta pasta estão armazenadas as imagens que serão usadas na página.
- js: Nesta pasta estão armazenados os códigos Javascript que foram utilizados para realizar algumas manipulações no formulário e deixar a navegação mais agradável para a pessoa que estiver acessando nossa página.
- index.php: Embora o arquivo seja do tipo
.php
, há códigos HTML nele que são responsáveis por estruturar nossa página.
Vamos criar uma pasta na raiz do projeto com o nome de src
que é a abreviação de source
que traduzindo para o português significa fonte. Esta pasta guardará nossos códigos back-end com as regras de negócio.
Além disso, dentro de “src”, vamos criar uma pasta com o nome de “Model” que em português significa modelo, sendo que esta pasta guardará as classes referentes ao seu modelo de classes.
Dentro da pasta Model
, vamos criar uma classe que se chama Telefone.php
, conforme a imagem abaixo:
Na seção seguinte, vamos aprender como validar a formatação de um telefone com PHP e RegEx.
Como validar a formatação de um telefone?
De ponto de partida, na pasta Modelo que se encontra dentro da pasta src, vamos criar um arquivo Telefone.php com a estrutura básica de uma classe, como mostra o código abaixo:
<?php
class Telefone
{
private string $telefone;
public function __construct(string $telefone)
{
$this->telefone = $telefone;
}
public function recuperaTelefone(): string
{
return $this->telefone;
}
}
Neste código, seguindo o conceito de Programação Orientada a Objetos, temos:
private string $telefone
: Representa um atributo, ou seja, uma propriedade do objeto. Neste caso, armazenará o número do telefone.function __construct
: É responsável pela inicialização da classe. Quando um objeto for criado a partir dessa classe, esse método será chamado para fazer as operações que definimos nele. Além disso, essa classe exige que seja passado como parâmetro para o construtor uma string que é o telefone.recuperaTelefone()
: É responsável por recuperar o valor do atributo$telefone
.
Entretanto, da forma como esta classe está, não está validando nada não é mesmo? Então vamos lá!
Na classe Telefone.php
vamos criar um método privado que se chama validaFormatacao()
, de acordo com o código abaixo:
private function validaFormatacao(string $telefone):bool
{
return preg_match("/^\([0-9]{2}\) 9?[0-9]{4}\-[0-9]{4}$/", $telefone);
}
Este método espera receber uma string como parâmetro, e também espera que o retorno seja booleano: true
(verdadeiro) ou false
(falso).
Dentro do método validaFormatacao
, há uma função nativa do PHP que se chama preg_match. Esta função funciona de maneira semelhante ao regex101 visto na seção Validações através do RegEx. Serão passados dois parâmetros obrigatórios para a função preg_match:
- 1º - Expressão regular;
- 2º - String que será analisada.
Essa função irá retornar true se a string analisada corresponde à expressão regular, caso contrário retornará false.
Em seguida, no método __construct
, vamos mexer um pouco para que seja feita a validação antes do valor ser atribuído. O código ficará desta forma:
public function __construct(string $telefone)
{
$formatacaoValida = $this->validaFormatacao($telefone);
if ($formatacaoValida === false) {
header("Location: index.php?erro=Telefone");
die();
}
$this->telefone = $telefone;
}
Ou seja, o valor da variável $formatacaoValida
será true ou false, dependendo do retorno da chamada do método $this->validaFormatacao($telefone);
O valor da variável $formatacaoValida
será analisado na estrutura de decisão if
. Sendo assim, se o valor for false
quebraremos o fluxo da aplicação, e redirecionaremos o usuário de volta para a página de formulário, pois o valor que foi encontrado é inválido. Este redirecionamento é feito pela função header()
- convido você a ler um pouco mais sobre ela na documentação do PHP.
Caso o valor da variável $formatacaoValida
seja true
o fluxo da aplicação seguirá normal, e o valor do atributo telefone será definido pelo seguinte trecho: $this->telefone = $telefone;
Esse fluxo que descrevemos acima é um padrão de projeto que se chama early return (retorno antecipado), em resumo diz que : “Retorno antecipado é a maneira de escrever funções ou métodos para que o resultado positivo esperado seja retornado no final da função e o restante do código finalize a execução quando as condições não forem atendidas.” Confira mais neste artigo.
Show de bola! Estamos quase finalizando, precisamos ajustar apenas um detalhe: você lembra que dentre os requisitos do projeto a Adopet, a empresa gostaria que o JSON retornasse o número de telefone da seguinte maneira: 1141183319?
Ou seja, devemos remover a formatação. Para isso, vamos criar um método que se chama limpaFormatacao
, conforme é mostrado no código abaixo:
private function limpaFormatacao(string $telefone):string
{
return str_replace(["(",")","-"," "],"",$telefone);
}
Este método espera receber uma string como parâmetro e também espera que o retorno seja uma string.
Dentro do método limpaFormatacao
, há uma função nativa do PHP que se chama str_replace; esta função substitui todas as ocorrências da string de busca pela string de substituição.
A função str_replace espera receber três parâmetros obrigatórios:
- 1º - String de busca, que pode ser passado um array também com os caracteres que serão buscados;
- 2º - String de substituição;
- 3º - String que será analisada.
Em seguida, no método __construct
, vamos mexer um pouco para que seja feita a limpeza dos caracteres antes do valor ser atribuído. O código ficará da seguinte maneira:
public function __construct(string $telefone)
{
$formatacaoValida = $this->validaFormatacao($telefone);
if ($formatacaoValida === false) {
header("Location: index.php?erro=Telefone");
die();
}
$this->telefone = $this->limpaFormatacao($telefone);
}
Dessa forma, concluímos o desenvolvimento da classe Telefone.php
. Nós realizamos validações na formatação da string, e posteriormente a limpeza dessa string.
Vale ressaltar que esta validação de formatação realizada no telefone não garante que se tenha uma eficácia satisfatória de validação. Mas é uma das formas de se validar e aumentar a integridade dos dados que chegam até o back-end.
Em um projeto real, uma outra validação que seria interessante implementar é a validação por meio de código enviado via SMS. Este método confirma se um número existe e se está ativo, pois o usuário só poderá prosseguir se ele souber o código que foi enviado para o número de telefone.
Na próxima seção, vamos desenvolver a classe DataNascimento
seguindo uma estrutura similar apresentada na classe Telefone
.
Como validar datas?
Nesta seção iremos construir a classe DataNascimento
que representará um modelo no nosso negócio e realizará avaliações.
De ponto de partida, na pasta Modelo, em que se encontra dentro da pasta src, vamos criar um arquivo DataNascimento.php com a estrutura básica de uma classe, como mostra o código abaixo:
<?php
class DataNascimento
{
private string $data;
public function __construct(string $data)
{
$this->data = $data;
}
public function recuperaDataCompleta(): string
{
return $this->data;
}
}
Como na seção de validação de telefone explicamos com mais detalhes a estrutura acima, vamos diretamente para a validação:
Demanda: Retornando aos requisitos de aceitação mostrado no início do artigo. Nossa cliente Adopet espera que entregamos um JSON contendo a data de nascimento com a seguinte formatação: 1999-12-20. Esse é o padrão ano-mês-dia, que geralmente é usado em banco de dados.
Também sabemos que o padrão date de um input de formulário HTML é enviar uma requisição com o seguinte formato:
1999-12-20 (ano-mês-dia).
Entretanto, sabemos que uma pessoa mal intencionada pode enviar uma requisição no seguinte formato: 1999-20-12 (ano-mês-dia).
Isso é um problema, pois comprometeria a integridade do nosso sistema, pelo fato do mês 20 não existir em no nosso calendário.
Sendo assim, O que acha de optarmos por realizar uma validação no formato ano-mês-dia, e se a validação for concluída, verificaremos se os números da data correspondem a uma data válida? Parece bom para você?
Muito bom! Podemos deixar assim por hora, mas qualquer coisa se você tiver uma outra ideia fique à vontade para implementar.
Primeiro, vamos criar um método que se chama validaFormatacao
que irá validar o formato ano/mês/dia através de uma expressão regular, conforme o código abaixo:
private function validaFormatacao(string $data): bool
{
return preg_match("/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}$/", $data);
}
Como foi visto na seção anterior, a função preg_match
verifica se uma string corresponde à uma expressão regular; além disso, essa função retorna true, para sucesso, caso contrário retorna false.
Agora, precisamos criar um método que se chama validaNumeros
que irá validar os números da data, conforme mostra o código abaixo:
private function validaNumeros(string $data): bool
{
$dataSeparada = explode("-",$data);
$ano = $dataSeparada[0];
$mes = $dataSeparada[1];
$dia = $dataSeparada[2];
return checkdate($mes,$dia,$ano);
}
Em PHP, há uma função nativa que verifica se os números de uma data são válidos. O nome desta função é checkdate. Ela retorna true, para sucesso, caso contrário retorna false.
A função checkdate, espera 3 parâmetros: mês, dia e ano em variáveis separadas, para isso, usamos a função explode, que separa uma string em um array de strings, dessa forma, podemos ter cada parte da data em uma variável diferente.
Perfeito, já temos nossas duas validações prontas: validaFormatação()
e validaNumeros()
. Agora vamos adicioná-las na função __construct()
e verificar se uma dessas validações falhou. Se uma delas falhar, esta data está incompatível com as regras do nosso negócio. O código abaixo mostra essa implementação:
public function __construct(string $data)
{
$formatacaoValida = $this->validaFormatacao($data);
$numerosValidos = $this->validaNumeros($data);
if ($formatacaoValida === false OR $numerosValidos === false){
header("Location: index.php?erro=Data De Nascimento");
die();
}
$this->data = $data;
}
Por fim, o código completo da classe DataNascimento
ficou da seguinte maneira como é mostrado no código abaixo:
<?php
class DataNascimento
{
private string $data;
public function __construct(string $data)
{
$formatacaoValida = $this->validaFormatacao($data);
$numerosValidos = $this->validaNumeros($data);
if ($formatacaoValida === false OR $numerosValidos === false){
header("Location: index.php?erro=Data De Nascimento");
die();
}
$this->data = $data;
}
private function validaFormatacao(string $data): bool
{
return preg_match("/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}$/", $data);
}
private function validaNumeros(string $data): bool
{
$dataSeparada = explode("-",$data);
$ano = $dataSeparada[0];
$mes = $dataSeparada[1];
$dia = $dataSeparada[2];
return checkdate($mes,$dia,$ano);
}
public function recuperaDataCompleta(): string
{
return $this->data;
}
}
Vale ressaltar que esta validação de formatação realizada na Data de Nascimento não garante que se tenha uma eficácia satisfatória de validação. No entanto, é uma das formas de se validar e aumentar a integridade dos dados que chegam até o back-end.
Uma outra implementação - que fica de desafio para você - é verificar se esta data de nascimento não é no futuro, pois como pode uma pessoa inserir uma data de nascimento no futuro?
Um outro questionamento: data de 5 anos atrás, será que nosso sistema deveria permitir uma pessoa de 5 anos de idade passar pela validação? Fica este questionamento, pois isso depende de cada sistema. Fique à vontade para implementar essas e outras considerações que você achar necessárias.
Leia sobre DateTime, pode te ajudar no desenvolvimento.
Na próxima seção, vamos desenvolver a classe Endereco
seguindo uma estrutura similar apresentada na classe de Telefone
e DataNascimento
.
Como validar CEP?
Nesta seção iremos construir a classe Endereco
que representará um modelo no nosso negócio e realizará avaliações.
Para este artigo, vamos realizar as validações do CEP na classe de Endereco
, apenas para ficar mais fácil a demonstração, mas eu acredito que seria interessante isolar o CEP em uma classe Cep, e ter somente suas validações. Em seguida, poderíamos fazer uma composição de classes com a classe Endereco
.
De ponto de partida, na pasta Modelo em que se encontra dentro da pasta src, vamos criar um arquivo Endereco.php com a estrutura básica de uma classe, como mostra o código abaixo:
<?php
class Endereco
{
private string $cep;
private string $cidade;
private string $estado;
private string $logradouro;
private string $bairro;
private string $numero;
public function __construct(string $cep, string $cidade, string $estado, string $logradouro, string $bairro, string $numero)
{
$this->cep = $cep;
$this->cidade = $cidade;
$this->estado = $estado;
$this->logradouro = $logradouro;
$this->bairro = $bairro;
$this->numero = $numero;
}
public function recuperaCep(): string
{
return $this->cep;
}
public function recuperaCidade(): string
{
return $this->cidade;
}
public function recuperaEstado(): string
{
return $this->estado;
}
public function recuperaLogradouro(): string
{
return $this->logradouro;
}
public function recuperaBairro(): string
{
return $this->bairro;
}
public function recuperaNumero(): string
{
return $this->numero;
}
}
Como na seção de validação de telefone explicamos com mais detalhes a estrutura acima, vamos diretamente para a validação:
Demanda: Retornando aos requisitos de aceitação mostrados no início do artigo, nossa cliente Adopet espera que entregamos um JSON contendo o cep com a seguinte formatação: 04101300.
Sabemos que o padrão de CEP nacional é composto pela seguinte formatação:
00000-000
Ou seja, são oito dígitos, cinco de um lado e três de outro. Outra informação importante, é que o formulário do front-end irá enviar para o back-end o CEP da maneira do padrão nacional: 04101-300.
Sabendo desses detalhes, o que você acha de optarmos por realizar uma validação no formato 00000-000, e se a validação for concluída, realizamos a limpeza da string do CEP removendo o caractere de hífen “-”?
Beleza! Vamos implementar dessa forma por enquanto, depois você pode implementar de uma outra maneira.
Primeiro, vamos criar um método que se chama validaFormatacao
que irá validar o formato 00000-000 através de uma expressão regular, conforme o código abaixo:
private function validaFormatacao(string $cep): bool
{
return preg_match("/^[0-9]{5}\-[0-9]{3}$/", $cep);
}
Como foi visto nas seções anteriores, a função preg_match
verifica se uma string corresponde a uma expressão regular; além disso, esta função retorna true, para sucesso, caso contrário retorna false.
Agora, precisamos criar um método que se chama limpaFormatacao
que irá remover o caractere de hífen da string CEP, conforme mostra o código abaixo:
private function limpaFormatacao(string $cep):string
{
return str_replace("-","",$cep);
}
Utilizando a função str_replace podemos substituir todas as ocorrências da string de busca pela string de substituição. Serão passados três parâmetros obrigatórios para a função str_replace:
- 1º - String de busca, que pode ser passado um array também com os caracteres que serão buscados;
- 2º - String de substituição;
- 3º - String que será analisada.
Perfeito, já temos os métodos: validaFormatação()
e limpaFormatacao()
. Agora vamos adicioná-los na função __construct()
e verificar o retorno do método validaFormatação()
.
Se for false
significa que este CEP é incompatível com as regras do nosso negócio. Caso contrário, podemos prosseguir e limpar a formatação para que o valor seja concedido ao atributo $cep
.
O código abaixo mostra essa implementação:
public function __construct(string $cep, string $cidade, string $estado, string $logradouro, string $bairro, string $numero)
{
$formatacaoValida = $this->validaFormatacao($cep);
if ($formatacaoValida === false){
header("Location: index.php?erro=Endereco");
die();
}
$this->cep = $this->limpaFormatacao($cep);;
$this->cidade = $cidade;
$this->estado = $estado;
$this->logradouro = $logradouro;
$this->bairro = $bairro;
$this->numero = $numero;
}
Por fim, o código completo da classe Endereco
ficou da seguinte maneira como é mostrado no código abaixo:
<?php
class Endereco
{
private string $cep;
private string $cidade;
private string $estado;
private string $logradouro;
private string $bairro;
private string $numero;
public function __construct(string $cep, string $cidade, string $estado, string $logradouro, string $bairro, string $numero)
{
$formatacaoValida = $this->validaFormatacao($cep);
if ($formatacaoValida === false){
header("Location: index.php?erro=Endereco");
die();
}
$this->cep = $this->limpaFormatacao($cep);;
$this->cidade = $cidade;
$this->estado = $estado;
$this->logradouro = $logradouro;
$this->bairro = $bairro;
$this->numero = $numero;
}
private function validaFormatacao(string $cep): bool
{
return preg_match("/^[0-9]{5}\-[0-9]{3}$/", $cep);
}
private function limpaFormatacao(string $cep):string
{
return str_replace("-","",$cep);
}
public function recuperaCep(): string
{
return $this->cep;
}
public function recuperaCidade(): string
{
return $this->cidade;
}
public function recuperaEstado(): string
{
return $this->estado;
}
public function recuperaLogradouro(): string
{
return $this->logradouro;
}
public function recuperaBairro(): string
{
return $this->bairro;
}
public function recuperaNumero(): string
{
return $this->numero;
}
}
Vale ressaltar que esta validação de Endereço não garante que se tenha uma eficácia satisfatória de validação. No entanto, é uma das formas de se validar e aumentar a integridade dos dados que chegam até o back-end.
Na sequência, teríamos que validar os dados de: logradouro, número, bairro, cidade e estado; além disso, poderíamos utilizar alguma API para verificar se este CEP realmente existe, pois a validação feita nesta seção apenas verifica a formatação. Fica de desafio para você implementar essas e outras considerações que você achar necessárias.
Na próxima seção vamos desenvolver a classe Cpf
seguindo uma estrutura similar apresentada na classe de Telefone
, DataNascimento
e Endereco
.
Como validar um CPF
Nesta seção iremos construir a classe Cpf
que representará um modelo no nosso negócio e realizará avaliações.
De ponto de partida, na pasta Modelo que se encontra dentro da pasta src, vamos criar um arquivo Cpf.php com a estrutura básica de uma classe mostrada no código abaixo:
<?php
class Cpf
{
private string $cpf;
public function __construct(string $cpf)
{
$this->cpf = $cpf;
}
public function recuperaNumero(): string
{
return $this->cpf;
}
}
Como na seção de validação de telefone explicamos com mais detalhes a estrutura acima, vamos diretamente para a validação, ok? Confira abaixo:
Demanda: Retornando aos requisitos de aceitação mostrados no início do artigo, nossa cliente Adopet espera que entregamos um JSON contendo o cpf com a seguinte formatação: 12345678910.
Sabemos que o padrão de CPF nacional é composto pela seguinte formatação: 000.000.000-00, ou seja, são 11 dígitos decimais. Outra informação importante, é que o formulário do front-end possui uma máscara que irá enviar para o back-end o CPF na formatação do padrão nacional: 000.000.000-00
Sabendo desses detalhes, o que acha de nós optarmos por realizar uma validação no formato 000.000.000-00, e se a validação for concluída, realizamos a limpeza da string do CPF removendo os caracteres de hífen “-” e ponto “.”? Parece bom para você?
Legal! Podemos realizar assim por hora, mas qualquer coisa se você tiver uma outra ideia fique à vontade para implementar.
Primeiro vamos criar um método que se chama validaFormatacao
que irá validar o formato 000.000.000-00 através de uma expressão regular, conforme o código abaixo:
private function validaFormatacao(string $cnpj):bool
{
return preg_match("/^[0-9]{2}\.[0-9]{3}\.[0-9]{3}\/[0-9]{4}\-[0-9]{2}$/", $cnpj);
}
Como foi visto nas seções anteriores, a função preg_match
verifica se uma string corresponde a uma expressão regular; além disso essa função retorna true, para sucesso; ou caso contrário, retorna false.
Agora, precisamos criar um método que se chama limpaFormatacao
que irá remover o caractere de hífen “-” e ponto “.” da string CPF, conforme mostra o código abaixo:
private function limpaFormatacao(string $cpf): string
{
return str_replace(['.','-'],"",$cpf);
}
Lembrando que com a função str_replace podemos substituir todas as ocorrências da string de busca pela string de substituição.
Perfeito, já temos os métodos: validaFormatação()
e limpaFormatacao()
; agora vamos adicioná-las na função __construct()
e verificar o retorno do método validaFormatação()
. Se for false
significa que este CPF é incompatível com as regras do nosso negócio. Caso contrário, podemos prosseguir e limpar a formatação para que o valor seja concedido ao atributo $cep
.
O código abaixo mostra essa implementação:
public function __construct(string $cpf)
{
$formatacaoValida = $this->validaFormatacao($cpf);
if ($formatacaoValida === false){
header("Location: index.php?erro=CPF");
die();
}
$this->cpf = $this->limpaFormatacao($cpf);
}
Por enquanto o código da classe Cpf
está da seguinte maneira:
<?php
class Cpf
{
private string $cpf;
public function __construct(string $cpf)
{
$formatacaoValida = $this->validaFormatacao($cpf);
if ($formatacaoValida === false){
header("Location: index.php?erro=CPF");
die();
}
$this->cpf = $this->limpaFormatacao($cpf);
}
}
private function validaFormatacao(string $cpf): bool
{
return preg_match("/^[0-9]{3}\.[0-9]{3}\.[0-9]{3}\-[0-9]{2}$/", $cpf);
}
private function limpaFormatacao(string $cpf): string
{
return str_replace(['.','-'],"",$cpf);
}
public function recuperaNumero(): string
{
return $this->cpf;
}
Além de validar a formatação do CPF, a Adopet, nosso cliente, nos solicitou a validação dos dígitos verificadores. Veremos isso na sequência, vamos lá?
Como validar um CPF através dos dígitos verificadores?
Como sabemos, o CPF é uma sequência de 9 dígitos, seguidos por dois dígitos verificadores, como ilustra a figura abaixo:
- Dígitos únicos: são números definidos pela Receita Federal;
- Origem: região fiscal emissora do CPF;
- Dígitos Verificadores: primeiro dígito verificador e Segundo Dígito Verificador, em sequência.
De maneira geral, os dígitos verificadores validam se um número de CPF é consistente, ao possibilitar, por exemplo, a detecção de falhas por causa de erros de digitação.
Para verificar se um CPF é válido através dos dígitos verificadores é necessário seguir o passo a passo abaixo:
- 1º Calcular o primeiro dígito verificador;
- 2º Calcular o *segundo dígito verificador;
- 3º Comparar se os 2 dígitos verificadores encontrados são iguais aos dígitos verificadores do CPF analisado. Se forem iguais, então o CPF é válido.
Como calcular o primeiro dígito verificador?
O primeiro dígito verificador: É calculado através da multiplicação dos 9 primeiros dígitos do CPF e uma sequência decrescente de números de 10 até 2. O resultado de cada multiplicação é somado, conforme é mostrado na figura abaixo:
O resultado da soma das multiplicações será dividido por 11, com o propósito de obter o resto desta divisão. Como no exemplo mostrado na imagem acima, o resultado da soma das multiplicações encontrado foi 210, logo:
210%11 = 1
Para definir o primeiro dígito verificador:
- Se o resto da divisão for menor que 2, logo o primeiro dígito verificador é 0;
- Se o resto da divisão for maior ou igual a 2, logo o primeiro dígito verificador obtido através da subtração de
11 - resto da divisão
.
Para o exemplo feito acima, o resto da divisão é 1, logo podemos considerar que o primeiro dígito verificador está correto, pois corresponde ao CPF de exemplo: 123.456.789-09
Como calcular o segundo dígito verificador?
O segundo dígito verificador é calculado através dos 10 primeiros dígitos do CPF (1234567890) considerando o primeiro dígito verificador encontrado anteriormente (0).
Dessa forma, os 10 primeiros dígitos do CPF (1234567890) serão multiplicados por uma sequência decrescente de números de 11 até 2; o resultado de cada multiplicação é somado. Confira a figura abaixo:
O resultado da soma das multiplicações será dividido por 11, com o propósito de obter o resto desta divisão. Como no exemplo mostrado na imagem acima o resultado da soma das multiplicações encontrado foi 255, logo:
255%11 = 2
Para definir o segundo dígito verificador:
- Se o resto da divisão for menor que 2, logo o primeiro dígito verificador é 0;
- Se o resto da divisão for maior ou igual a 2, logo o primeiro dígito verificador obtido através da subtração de 11 - resto da divisão.
Para o exemplo feito acima, o resto da divisão é 2, logo:
11-2 = 9
Portanto, podemos considerar que o segundo dígito verificador está correto, pois corresponde ao CPF de exemplo: 123.456.789-09.
O primeiro dígito verificador e o segundo dígito verificador passam neste processo de validação. Logo podemos dizer que o CPF: 123.456.789-09 é um CPF consistente.
Agora sabendo toda essa lógica, precisamos implementar isso em código. Vamos nessa?
Implementando a solução para cálculo de dígitos verificadores do CPF
Primeiramente, vamos relembrar o passo a passo que foi descrito na seção anterior de como validar o CPF através dos dígitos verificadores:
- 1º Calcular o primeiro dígito verificador;
- 2º Calcular o segundo dígito verificador;
- 3º Comparar se os 2 dígitos verificadores encontrados são iguais aos dígitos verificadores do CPF analisado. Se forem iguais, então o CPF é válido.
Dessa forma, vamos adicionar duas constantes na classe Cpf
, que fazem referência aos pesos multiplicadores do CPF:
class Cpf
{
private string $cpf;
private const PESO_10 = 10;
private const PESO_11 = 11;
Em seguida, vamos criar um método que se chama validaDigitosVerificadores
. Ele será responsável por implementar o passo a passo descrito acima.
O código abaixo mostra essa implementação:
private function validaDigitosVericadores($cpf):bool
{
//Preparando o CPF com 9 dígitos
$cpfSemFormatacao = $this->limpaFormatacao($cpf);
$cpfCom9PrimeirosDigitos = substr($cpfSemFormatacao,0,9);
// 1º Passo:Calculando o primeiro dígito verificador
$primeiroDigitoVerificador = $this->calculaDigitoVerificador($cpfCom9PrimeirosDigitos, self::PESO_10);
// 2º Passo: Calculando o segundo dígito verificador
$cpfCom10PrimeirosDigitos = $cpfCom9PrimeirosDigitos . $primeiroDigitoVerificador;
$segundoDigitoVerificador = $this->calculaDigitoVerificador($cpfCom10PrimeirosDigitos, self::PESO_11);
/*3º Passo: Comprar se os 2 dígitos verificadores encontrados são iguais
aos dígitos verificadores do CPF analisado. Se forem iguais, então o CPF é válido.*/
$cpfAposValidacao = $cpfCom9PrimeirosDigitos . $primeiroDigitoVerificador . $segundoDigitoVerificador;
if ($cpfSemFormatacao != $cpfAposValidacao){
return false;
}
return true;
}
Podemos reparar que, no código acima, há uma chamada do método calculaDigitoVerificador
, que é responsável por implementar o passo a passo abaixo:
1º Passo: Realizar a multiplicação dos dígitos do CPF e de uma sequência de pesos associados a cada um deles. O resultado de cada multiplicação é somada;
2º Passo: O resultado da soma das multiplicações é dividido por 11, com o propósito de obter o resto da divisão;
3º Passo: Se o resto da divisão for menor que 2 logo o primeiro dígito verificador é 0; caso contrário, o primeiro dígito verificador é obtido através da subtração de 11 menos o resto da divisão.
Ao seguir este passo a passo acima podemos implementar o método calculaDigitoVerificador
, conforme o código abaixo:
private function calculaDigitoVerificador(string $numeroCpf, int $pesoMultiplicadores):string
{
//Prepara o cpf e transforma ele em Array para realizar as multiplicações
$cpfArray = str_split($numeroCpf);
$tamanhoCpf = count($cpfArray);
//1º Passo: Realizar a multiplicação dos dígitos do CPF e de uma sequência de pesos associados a cada um deles. O resultado de cada multiplicação é somado:
for ($i = 0; $i < $tamanhoCpf; $i++){
$resultadoMultiplicacao[$i] = $cpfArray[$i] * $pesoMultiplicadores;
$pesoMultiplicadores--;
}
$somaDoCpf = array_sum($resultadoMultiplicacao);
// 2º Passo: O resultado da soma das multiplicações é dividido por 11, com o propósito de obter o resto da divisão:
$restoDaDivisao = $somaDoCpf % 11;
//3º Passo: Se o resto da divisão for menor que 2, logo o primeiro dígito verificador é 0; caso contrário, o primeiro dígito verificador é obtido através da subtração de 11 menos o resto da divisão;
if ($restoDaDivisao < 2){
return 0;
}
$resultadoSubtracao = 11 - $restoDaDivisao;
return $resultadoSubtracao;
}
Com estes métodos implementados, devemos adicionar esta validação no nosso método __construct
da seguinte maneira:
public function __construct(string $cpf)
{
$formatacaoValida = $this->validaFormatacao($cpf);
$validacaoDigitosVerificadores = $this->validaDigitosVericadores($cpf);
if ($validacaoDigitosVerificadores === false OR $formatacaoValida === false){
header("Location: index.php?erro=CPF");
die();
}
$this->cpf = $this->limpaFormatacao($cpf);
}
Por fim, a classe Cpf ficou da seguinte maneira:
<?php
class Cpf
{
private string $cpf;
private const PESO_10 = 10;
private const PESO_11 = 11;
public function __construct(string $cpf)
{
$formatacaoValida = $this->validaFormatacao($cpf);
$validacaoDigitosVerificadores = $this->validaDigitosVericadores($cpf);
if ($validacaoDigitosVerificadores === false OR $formatacaoValida === false){
header("Location: index.php?erro=CPF");
die();
}
$this->cpf = $this->limpaFormatacao($cpf);
}
private function validaFormatacao(string $cpf): bool
{
return preg_match("/^[0-9]{3}\.[0-9]{3}\.[0-9]{3}\-[0-9]{2}$/", $cpf);
}
private function validaDigitosVericadores($cpf):bool
{
$cpfSemFormatacao = $this->limpaFormatacao($cpf);
$cpfCom9PrimeirosDigitos = substr($cpfSemFormatacao,0,9);
$primeiroDigitoVerificador = $this->calculaDigitoVerificador($cpfCom9PrimeirosDigitos, self::PESO_10);
$cpfCom10PrimeirosDigitos = $cpfCom9PrimeirosDigitos . $primeiroDigitoVerificador;
$segundoDigitoVerificador = $this->calculaDigitoVerificador($cpfCom10PrimeirosDigitos, self::PESO_11);
$cpfAposValidacao = $cpfCom9PrimeirosDigitos . $primeiroDigitoVerificador . $segundoDigitoVerificador;
if ($cpfSemFormatacao != $cpfAposValidacao){
return false;
}
return true;
}
private function limpaFormatacao(string $cpf): string
{
return str_replace(['.','-'],"",$cpf);
}
public function recuperaNumero(): string
{
return $this->cpf;
}
private function calculaDigitoVerificador(string $numeroCpf, int $pesoMultiplicadores):string
{
$cpfArray = str_split($numeroCpf);
$tamanhoCpf = count($cpfArray);
for ($i = 0; $i < $tamanhoCpf; $i++){
$resultadoMultiplicacao[$i] = $cpfArray[$i] * $pesoMultiplicadores;
$pesoMultiplicadores--;
}
$somaDoCpf = array_sum($resultadoMultiplicacao);
$restoDaDivisao = $somaDoCpf % 11;
if ($restoDaDivisao < 2){
return 0;
}
$resultadoSubtracao = 11 - $restoDaDivisao;
return $resultadoSubtracao;
}
}
Ficou um código um pouco denso, não é mesmo? Mas isso é normal, tendo em vista que há muitos passos a serem seguidos.
Vale ressaltar que esta validação apenas garante que o número de CPF não foi escrito de maneira incorreta. Além disso, o código acima considera como válido ocorrências de números iguais:
222.222.222-22
,777.777.777-77
.
Uma outra forma de validar e que aumentaria a segurança de uma aplicação é a busca desses dados através do banco de dados da Receita Federal. Fica o desafio para você implementar essas e outras considerações que você achar necessárias!
Na próxima seção, vamos desenvolver a classe Cnpj
seguindo uma estrutura similar apresentada na classe de Cpf
, vamos lá?
Como validar um CNPJ
Nesta seção iremos construir a classe Cnpj
que representará um modelo no nosso negócio e realizará avaliações.
De ponto de partida, na pasta Modelo que se encontra dentro da pasta src, vamos criar um arquivo Cnpj.php com a estrutura básica de uma classe mostrada no código abaixo:
<?php
class Cnpj
{
private string $cnpj;
public function __construct(string $cnpj)
{
$this->cnpj = $cnpj;
}
public function recuperaNumero(): string
{
return $this->cnpj;
}
}
Como na seção de validação de telefone explicamos com mais detalhes a estrutura acima, vamos diretamente para a validação:
Demanda: Ao retornar os requisitos de aceitação mostrados no início do artigo, nosso cliente Adopet espera que entreguemos um JSON contendo o cnpj com a seguinte formatação:
05555382000133.
Sabemos que o CNPJ nacional é segue o padrão 00.000.000/0000-00, ou seja, são 14 dígitos decimais. Outra informação importante é que o formulário do front-end possui uma máscara que irá enviar para o back-end o CNPJ na formatação do padrão nacional: 00.000.000/0000-00
Sabendo desses detalhes, o que acha de optarmos por realizar uma validação no formato 00.000.000/0000-00 por meio do RegEx, e se a validação for concluída, realizamos a limpeza da string do CNPJ removendo os caracteres de hífen “-”, ponto “.” e barra “/”? Parece bom para você?
Muito bem! Por enquanto vamos desenvolver dessa forma, caso achar interessante implementar de uma outra maneira fique à vontade.
Primeiro vamos criar um método que se chama validaFormatacao
que irá validar o formato *00.000.000/0000-00** através de uma expressão regular, conforme o código abaixo:
private function validaFormatacao(string $cnpj):bool
{
return preg_match("/^[0-9]{2}\.[0-9]{3}\.[0-9]{3}\/[0-9]{4}\-[0-9]{2}$/", $cnpj);
}
Como foi visto nas seções anteriores, a função preg_match
verifica se uma string corresponde a uma expressão regular. Além disso, esta função retorna true, para sucesso, caso contrário, retorna false.
Agora, precisamos criar um método que se chama limpaFormatacao
, que irá remover o caractere de hífen “-”, ponto “.” e barra “/” da string CNPJ, conforme mostra o código abaixo:
private function limpaFormatacao(string $cnpj): string
{
return str_replace(['.','-','/'],"",$cnpj);
}
Lembrando que, com a função str_replace, podemos substituir todas as ocorrências da string de busca pela string de substituição.
Perfeito, já temos os métodos: validaFormatação()
e limpaFormatacao()
. Agora vamos adicioná-los na função __construct()
e verificar o retorno do método validaFormatação()
.
Se for false
significa que este CNPJ é incompatível com as regras do nosso negócio, caso contrário, podemos prosseguir e limpar a formatação para que o valor seja concedido ao atributo $cnpj
.
O código abaixo mostra essa implementação:
public function __construct(string $cnpj)
{
$formatacaoValida = $this->validaFormatacao($cnpj);
if ($formatacaoValida === false){
header("Location: index.php?erro=CNPJ");
die();
}
$this->cnpj = $this->limpaFormatacao($cnpj);
}
Por enquanto, o código da classe Cnpj
está da seguinte maneira:
<?php
class Cnpj
{
private string $cnpj;
public function __construct(string $cnpj)
{
$formatacaoValida = $this->validaFormatacao($cnpj);
if ($formatacaoValida === false){
header("Location: index.php?erro=CNPJ");
die();
}
$this->cnpj = $this->limpaFormatacao($cnpj);
}
private function validaFormatacao(string $cnpj):bool
{
return preg_match("/^[0-9]{2}\.[0-9]{3}\.[0-9]{3}\/[0-9]{4}\-[0-9]{2}$/", $cnpj);
}
private function limpaFormatacao(string $cnpj): string
{
return str_replace(['.','-','/'],"",$cnpj);
}
public function recuperaNumero(): string
{
return $this->cnpj;
}
}
Além de validar a formatação do CNPJ, a Adopet nos solicitou a validação dos dígitos verificadores, então vamos lá!
Como validar um CNPJ através dos dígitos verificadores
Como sabemos, o CNPJ é uma sequência de 12 dígitos seguidos por 2 dígitos verificadores, como ilustra a figura abaixo:
- Dígitos únicos: São números definidos pela Receita Federal;
- Matriz ou filial: Geralmente, após a barra há uma sequência de 4 números. Ex: 0001 que representam a matriz. Já nas filiais o número 1 é substituído pela sequência numérica, por exemplo, 0002, 0003 e assim sucessivamente;
- Dígitos Verificadores: Primeiro dígito verificador e Segundo Dígito Verificador, em sequência.
De maneira geral, os dígitos verificadores validam se um número de CNPJ é consistente. Por exemplo: possibilita a detecção de falhas por causa de erros de digitação.
Para verificar se um CNPJ é válido através dos dígitos verificadores é necessário seguir o passo a passo abaixo:
- 1º Calcular o primeiro dígito verificador;
- 2º Calcular o segundo dígito verificador;
- 3º Comprar se os 2 dígitos verificadores encontrados são iguais aos dígitos verificadores do CNPJ analisado. Se forem iguais, então o CNPJ é válido.
Como calcular o primeiro dígito verificador?
O primeiro dígito verificador é calculado através da multiplicação dos 12 primeiros dígitos do CNPJ e uma sequência de 12 números: 5,4,3,2,9,8,7,6,5,4,3,2, sendo que o resultado de cada multiplicação é somado, conforme é mostrado na figura abaixo:
O resultado da soma das multiplicações será dividido por 11, com o propósito de obter o resto desta divisão. No exemplo mostrado na imagem acima, o resultado da soma das multiplicações encontrado foi 184, logo:
184%11 = 8
Para definir o primeiro dígito verificador:
- Se o resto da divisão for menor que 2, logo o primeiro dígito verificador é 0;
- Se o resto da divisão for maior ou igual a 2, logo o primeiro dígito verificador obtido através da subtração de 11 - resto da divisão.
Para o exemplo feito acima, o resto da divisão é 8, logo:
11-8 = 3
Portanto, podemos considerar que o primeiro dígito verificador está correto, pois corresponde ao CNPJ de exemplo: 05.555.382/0001-33.
Como calcular o segundo dígito verificador?
O segundo dígito verificador é calculado através dos 13 primeiros dígitos do CNPJ considerando o primeiro dígito verificador encontrado anteriormente multiplicados por uma sequência de 13 números: 6,5,4,3,2,9,8,7,6,5,4,3,2, sendo que o resultado de cada multiplicação é somado, conforme é mostrado na figura abaixo:
O resultado da soma das multiplicações será dividido por 11 com o propósito de obter o resto desta divisão. No exemplo mostrado na imagem acima, o resultado da soma das multiplicações encontrado foi 184, logo:
184%11 = 8
Para definir o segundo dígito verificador:
- Se o resto da divisão for menor que 2, logo o primeiro dígito verificador é 0;
- Se o resto da divisão for maior ou igual a 2, logo o primeiro dígito verificador obtido através da subtração de 11 - resto da divisão.
Para o exemplo feito acima, o resto da divisão é 8, logo:
11-8 = 3
Portanto, podemos considerar que o segundo dígito verificador está correto, pois corresponde ao CNPJ de exemplo: 05.555.382/0001-33
O primeiro dígito verificador e o segundo dígito verificador passam neste processo de validação. Posto isso, podemos dizer que o CNPJ: 05.555.382/0001-33 é um CNPJ consistente.
Agora, sabendo de toda essa lógica, precisamos implementar isso em código. Bora?!
Implementando a solução para cálculo de dígitos verificadores do CNPJ
Primeiramente, vamos relembrar o passo a passo que foi descrito na seção anterior de como validar o CNPJ através dos dígitos verificadores:
- 1º Calcular o primeiro dígito verificador;
- 2º Calcular o segundo dígito verificador;
- 3º Comparar se os 2 dígitos verificadores encontrados são iguais aos dígitos verificadores do CNPJ analisado. Se forem iguais, então o CNPJ é válido.
Dessa forma, vamos adicionar 2 constantes na classe Cnpj
; elas fazem referência aos pesos multiplicadores do CNPJ:
class Cnpj
{
private string $cnpj;
private const PESO_12 = [5,4,3,2,9,8,7,6,5,4,3,2];
private const PESO_13 = [6,5,4,3,2,9,8,7,6,5,4,3,2];
Em seguida, vamos criar um método que se chama validaDigitosVerificadores
. Ele será responsável por implementar o passo a passo descrito acima. O código abaixo mostra essa implementação:
private function validaDigitosVerificadores(string $cnpj):bool
{
//Preparando o CNPJ com 12 dígitos
$cnpjSemFormatacao = $this->limpaFormatacao($cnpj);
$cnpjCom12PrimeirosDigitos = substr($cnpjSemFormatacao,0,12);
// 1º Passo: Calculando o primeiro dígito verificador
$primeiroDigitoVerificador = $this->calculaDigitoVerificador($cnpjCom12PrimeirosDigitos, self::PESO_12);
// 2º Passo: Calculando o segundo dígito verificador
$cnpjCom13PrimeirosDigitos = $cnpjCom12PrimeirosDigitos . $primeiroDigitoVerificador;
$segundoDigitoVerificador = $this->calculaDigitoVerificador($cnpjCom13PrimeirosDigitos, self::PESO_13);
/*3º Passo: Comparar se os 2 dígitos verificadores encontrados são iguais aos dígitos
verificadores do CNPJ analisado. Se forem iguais, então o CNPJ é válido.*/
$cnpjAposValidacao = $cnpjCom12PrimeirosDigitos . $primeiroDigitoVerificador . $segundoDigitoVerificador;
if ($cnpjSemFormatacao != $cnpjAposValidacao){
return false;
}
return true;
}
Podemos reparar que no código acima há uma chamada do método calculaDigitoVerificador
. Ele é responsável por implementar o passo a passo abaixo:
- 1º Passo: Realizar a multiplicação dos dígitos do CNPJ e de uma sequência de pesos associados a cada um deles. O resultado de cada multiplicação é somado;
- 2º Passo: O resultado da soma das multiplicações é dividido por 11, com o propósito de obter o resto da divisão;
- 3º Passo: Se o resto da divisão for menor que 2, logo o primeiro dígito verificador é 0; caso contrário, o primeiro dígito verificador é obtido através da subtração de 11 - resto da divisão.
Seguindo este passo a passo acima podemos implementar o método calculaDigitoVerificador
, conforme o código abaixo:
private function calculaDigitoVerificador(string $numeroCnpj, array $pesoMultiplicadores):string
{
//Prepara o cnpj e transforma ele em Array para realizar as multiplicações
$cnpjArray = str_split($numeroCnpj);
$tamanhoCnpj = count($cnpjArray);
//1º Passo: Realizar a multiplicação dos dígitos do CNPJ e de uma sequência de pesos associados a cada um deles. O resultado de cada multiplicação é somado:
for ($i = 0; $i < $tamanhoCnpj; $i++){
$resultadoMultiplicacao[$i] = $cnpjArray[$i] * $pesoMultiplicadores[$i];
}
$somaDoCnpj = array_sum($resultadoMultiplicacao);
// 2º Passo: O resultado da soma das multiplicações é dividido por 11, com o propósito de obter o resto da divisão:
$restoDaDivisao = $somaDoCnpj % 11;
//3º Passo: Se o resto da divisão for menor que 2, logo o primeiro dígito verificador é 0; caso contrário, o primeiro dígito verificador é obtido através da subtração de 11 menos resto da divisão;
if ($restoDaDivisao < 2){
return 0;
}
$resultadoSubtracao = 11 - $restoDaDivisao;
return $resultadoSubtracao;
}
Com estes métodos implementados, devemos adicionar esta validação no nosso método __construct
da seguinte maneira:
public function __construct(string $cnpj)
{
$formatacaoValida = $this->validaFormatacao($cnpj);
$validacaoDigitosVerificadores = $this->validaDigitosVerificadores($cnpj);
if ($validacaoDigitosVerificadores === false OR $formatacaoValida === false){
header("Location: index.php?erro=CNPJ");
die();
}
$this->cnpj = $this->limpaFormatacao($cnpj);
}
Por fim, a classe Cnpj ficou da seguinte maneira:
<?php
class Cnpj
{
private string $cnpj;
private const PESO_12 = [5,4,3,2,9,8,7,6,5,4,3,2];
private const PESO_13 = [6,5,4,3,2,9,8,7,6,5,4,3,2];
public function __construct(string $cnpj)
{
$formatacaoValida = $this->validaFormatacao($cnpj);
$validacaoDigitosVerificadores = $this->validaDigitosVerificadores($cnpj);
if ($validacaoDigitosVerificadores === false OR $formatacaoValida === false){
header("Location: index.php?erro=CNPJ");
die();
}
$this->cnpj = $this->limpaFormatacao($cnpj);
}
private function validaFormatacao(string $cnpj):bool
{
return preg_match("/^[0-9]{2}\.[0-9]{3}\.[0-9]{3}\/[0-9]{4}\-[0-9]{2}$/", $cnpj);
}
private function limpaFormatacao(string $cnpj): string
{
return str_replace(['.','-','/'],"",$cnpj);
}
private function validaDigitosVerificadores(string $cnpj):bool
{
$cnpjSemFormatacao = $this->limpaFormatacao($cnpj);
$cnpjCom12PrimeirosDigitos = substr($cnpjSemFormatacao,0,12);
$primeiroDigitoVerificador = $this->calculaDigitoVerificador($cnpjCom12PrimeirosDigitos, self::PESO_12);
$cnpjCom13PrimeirosDigitos = $cnpjCom12PrimeirosDigitos . $primeiroDigitoVerificador;
$segundoDigitoVerificador = $this->calculaDigitoVerificador($cnpjCom13PrimeirosDigitos, self::PESO_13);
$cnpjAposValidacao = $cnpjCom12PrimeirosDigitos . $primeiroDigitoVerificador . $segundoDigitoVerificador;
if ($cnpjSemFormatacao != $cnpjAposValidacao){
return false;
}
return true;
}
private function calculaDigitoVerificador(string $numeroCnpj, array $pesoMultiplicadores):string
{
$cnpjArray = str_split($numeroCnpj);
$tamanhoCnpj = count($cnpjArray);
for ($i = 0; $i < $tamanhoCnpj; $i++){
$resultadoMultiplicacao[$i] = $cnpjArray[$i] * $pesoMultiplicadores[$i];
}
$somaDoCnpj = array_sum($resultadoMultiplicacao);
$restoDaDivisao = $somaDoCnpj % 11;
if ($restoDaDivisao < 2){
return 0;
}
$resultadoSubtracao = 11 - $restoDaDivisao;
return $resultadoSubtracao;
}
public function recuperaNumero(): string
{
return $this->cnpj;
}
}
Ficou um código bem extenso, mas isso é normal tendo em vista que há muitos passos a serem seguidos.
Vale ressaltar que esta validação apenas garante que o número de CNPJ não foi escrito de maneira incorreta. Além disso, o código acima considera como válido ocorrências de números iguais:
22.222.222/2222-22
,44.444.444/4444-44
.
Uma outra forma de validar que aumentaria a segurança de uma aplicação é a busca desses dados através do banco de dados da Receita Federal.
Na próxima seção, vamos agrupar tudo que construímos até agora em uma classe de Usuários
.
Pessoa física e Pessoa jurídica
Nesta seção iremos construir a classe Pessoa
, PessoaFisica
e PessoaJuridica
que representam modelos no nosso negócio.
Demanda: Ao retomar os requisitos de aceitação mostrado no início do artigo, nossa cliente Adopet espera que entregamos um JSON contendo cpf ou cnpj.
Dessa forma, sabemos que um CPF está ligado a uma pessoa física, e um CNPJ está ligado a uma pessoa jurídica. Ou seja, ambos são pessoas, porém há particularidades entre elas.
Em virtude disso, vamos nos aproveitar de um dos pilares da programação orientada a objetos — herança.
A herança é um mecanismo que permite que características comuns a diversas classes sejam fatoradas em uma “mãe”. A partir de uma “mãe”, outras classes “filhas” podem ser criadas com suas particularidades. Seguindo este raciocínio, vamos criar uma classe mãe: Pessoa
, e duas classes filhas: PessoaFísica
e PessoaJurídica
.
O diagrama abaixo nos auxilia a visualização da explicação dada acima:
Para prosseguir com nossa aplicação, vamos criar os arquivos Pessoa.php, PessoaFisica.php e PessoaJuridica.php dentro da pasta Model.
Seguindo a modelagem da imagem acima, a classe Pessoa
ficará da seguinte forma:
<?php
class Pessoa
{
protected string $nome;
protected string $email;
protected Endereco $endereco;
protected DataNascimento $data;
protected Telefone $telefone;
public function __construct(string $nome, string $email, Endereco $endereco, DataNascimento $data, Telefone $telefone)
{
$this->nome = $nome;
$this->email = $email;
$this->endereco = $endereco;
$this->data = $data;
$this->telefone = $telefone;
}
}
Veja que, no código acima, não há CPF e CNPJ, pois ambos são particularidades de Pessoa Física e Pessoa Jurídica, respectivamente. Além disso, os atributos estão definidos como protected
, isso significa que apenas a classe Pessoa
e as classes que irão herdar dela terão acesso a estes atributos. Vamos implementar as individualidades conforme no código abaixo:
PessoaFisica
<?php
class PessoaFisica extends Pessoa
{
private Cpf $cpf;
public function __construct(string $nome, string $email, Cpf $cpf, Endereco $endereco, DataNascimento $data, Telefone $telefone)
{
parent::__construct($nome, $email, $endereco, $data, $telefone);
$this->cpf = $cpf;
}
PessoaJuridica
<?php
class PessoaJuridica extends Pessoa
{
private Cnpj $cnpj;
public function __construct(string $nome, string $email, Cnpj $cnpj, Endereco $endereco, DataNascimento $data, Telefone $telefone)
{
parent::__construct($nome, $email, $endereco, $data, $telefone);
$this->cnpj = $cnpj;
}
Observe que tanto a classe PessoaFisica
e PessoaJuridica
possuem a expressão: extends Pessoa
. Isso significa que ambas as classes herdam atributos e métodos que já foram definidos na classe Pessoa
; em outras palavras aplicamos o conceito de herança.
É importante destacar mais um detalhe sobre a expressão abaixo que está definida no método _construct
das classes PessoaFisica
e PessoaJuridica
:
parent::__construct($nome, $email, $endereco, $data, $telefone);
Esta expressão informa que vamos utilizar o __construct
da classe mãe, ou seja, classe Pessoa
. Dessa forma, podemos instanciar um objeto diretamente de uma classe PessoaFisica
ou PessoaJuridica
.
Perfeito, concluímos mais uma etapa do nosso projeto, nesta seção falamos sobre herança nas classes: Pessoa
, PessoaFisica
e PessoaJuridica
.
Na próxima seção vamos implementar uma forma da classe PessoaFisica
e PessoaJuridica
retornarem os dados em formato JSON.
Retornando JSON
A demanda vinda da nossa cliente Adopet espera que entreguemos um JSON contendo os dados abaixo:
{
"nome": "Vinicius Louzada",
"email": "[email protected]",
"cpf": "12345678909",
"cep": "04101300",
"cidade": "São Paulo",
"estado": "SP",
"logradouro": "Rua Vergueiro",
"bairro": "Vila Mariana",
"numero": "3185",
"data-nascimento": "1999-12-20",
"telefone": "1141183319"
}
Vale lembrar que o trecho acima traz a descrição de um JSON de uma pessoa física, pois há um CPF; caso contrário, se fosse de uma pessoa jurídica haveria um CNPJ.
Com todas as classes criadas, precisamos retornar um JSON de uma classe PessoaFisica
e PessoaJuridica
, pois são elas que carregam todas as informações de uma Pessoa
. Em virtude disso, em PHP há uma interface que se chama JsonSerializable .
As classes que implementam a interface JsonSerializable
podem personalizar sua representação JSON através do método:
public jsonSerialize(): mixed
Vamos implementar a interface JsonSerializable
na classe PessoaFisica
da seguinte forma:
<?php
class PessoaFisica extends Pessoa implements JsonSerializable
{
private Cpf $cpf;
public function __construct(string $nome, string $email, Cpf $cpf, Endereco $endereco, DataNascimento $data, Telefone $telefone)
{
parent::__construct($nome, $email, $endereco, $data, $telefone);
$this->cpf = $cpf;
}
public function jsonSerialize(): mixed
{
return [
'nome' => $this->nome,
'email'=> $this->email,
'cpf' => $this->cpf->recuperaNumero(),
'cep' => $this->endereco->recuperaCep(),
'cidade' => $this->endereco->recuperaCidade(),
'estado' => $this->endereco->recuperaEstado(),
'logradouro' => $this->endereco->recuperaLogradouro(),
'bairro' => $this->endereco->recuperaBairro(),
'numero' => $this->endereco->recuperaNumero(),
'data-nascimento'=> $this->data->recuperaDataCompleta(),
'telefone' =>$this->telefone->recuperaTelefone(),
];
}
}
Em seguida, vamos implementar a interface JsonSerializable
na classe PessoaJuridica
da seguinte maneira:
<?php
class PessoaJuridica extends Pessoa implements JsonSerializable
{
private Cnpj $cnpj;
public function __construct(string $nome, string $email, Cnpj $cnpj, Endereco $endereco, DataNascimento $data, Telefone $telefone)
{
parent::__construct($nome, $email, $endereco, $data, $telefone);
$this->cnpj = $cnpj;
}
public function jsonSerialize(): mixed
{
return [
'nome' => $this->nome,
'email'=> $this->email,
'cnpj' => $this->cnpj->recuperaNumero(),
'cep' => $this->endereco->recuperaCep(),
'cidade' => $this->endereco->recuperaCidade(),
'estado' => $this->endereco->recuperaEstado(),
'logradouro' => $this->endereco->recuperaLogradouro(),
'bairro' => $this->endereco->recuperaBairro(),
'numero' => $this->endereco->recuperaNumero(),
'data-nascimento'=> $this->data->recuperaDataCompleta(),
'telefone' =>$this->telefone->recuperaTelefone(),
];
}
}
Perteito! Com isso programado, para exibir os dados em JSON precisamos apenas chamar o método jsonSerialize()
.
Para finalizar nossa aplicação, na próxima seção vamos criar um arquivo que irá receber os dados do formulário invocando cada uma das nossas classes.
Recebendo os dados do front-end
Nesta seção vamos criar um arquivo que será responsável por receber os dados do front-end e invocar cada uma das classes criadas.
Ao analisar o arquivo index.php
, localizado na raiz do projeto, é possível notar que o formulário HTML está enviando uma requisição HTTP através do método POST para um arquivo que se chama cadastro-form.php
.
Por isso, na raiz do projeto, vamos criar este arquivo com o código abaixo:
<?php
require_once __DIR__."/src/Model/Cpf.php";
require_once __DIR__ . "/src/Model/DataNascimento.php";
require_once __DIR__."/src/Model/Telefone.php";
require_once __DIR__."/src/Model/Endereco.php";
require_once __DIR__."/src/Model/Cnpj.php";
require_once __DIR__ . "/src/Model/Pessoa.php";
require_once __DIR__. "/src/Model/PessoaFisica.php";
require_once __DIR__. "/src/Model/PessoaJuridica.php";
$nomeForm = filter_input(INPUT_POST,"nome");
$emailForm = filter_input(INPUT_POST,"email");
$cpfForm = filter_input(INPUT_POST,"cpf");
$cnpjForm = filter_input(INPUT_POST,"cnpj");
$cepForm = filter_input(INPUT_POST,"cep");
$cidadeForm = filter_input(INPUT_POST,"cidade");
$logradouroForm = filter_input(INPUT_POST,'logradouro');
$bairroForm = filter_input(INPUT_POST,"bairro");
$estadoForm = filter_input(INPUT_POST,'estado');
$numeroForm = filter_input(INPUT_POST,'numero');
$dataForm = filter_input(INPUT_POST, "data");
$telefoneForm = filter_input(INPUT_POST,"telefone");
$endereco = new Endereco($cepForm,$cidadeForm,$estadoForm, $logradouroForm, $bairroForm, $numeroForm);
$data = new DataNascimento($dataForm);
$telefone = new Telefone($telefoneForm);
if ($cpfForm !== null){
$cpf = new Cpf($cpfForm);
$pessoa = new PessoaFisica($nomeForm,$emailForm,$cpf,$endereco,$data,$telefone);
}
if($cnpjForm !== null){
$cnpj = new Cnpj($cnpjForm);
$pessoa = new PessoaJuridica($nomeForm,$emailForm,$cnpj,$endereco,$data,$telefone);
}
echo json_encode($pessoa);
No código acima, utilizamos o require_once para incluir no arquivo atual todas as classes que criamos anteriormente.
Em seguida, recebemos os dados vindo do front-end através da função filter_input, onde o primeiro parâmetro significa a forma como estamos recebendo este dado. Para este caso, estamos recebendo através do método POST
; o segundo parâmetro faz referência a uma tag input
do formulário HTML.
Vale lembrar que no código acima não adicionamos no 3º parâmetro a função
filter_input
, pois isso ficará de tarefa para você. Podemos filtrar e validar se um dado que veio do formulário é um e-mail através doFILTER_VALIDATE_EMAIL
.
Confira mais sobre os filtros de validação e filtros de limpeza.
No código acima também foram realizadas as instâncias de Endereço
, DataNascimento
e Telefone
. Em seguida, criamos uma estrutura de decisão para verificar se do formulário HTML veio uma informação de CPF ou CNPJ, e dependendo de qual delas vier da requisição, será criado um objeto PessoaFisica
ou PessoaJuridica
.
Por fim, retornamos um JSON através da expressão abaixo:
echo json_encode($pessoa);
Pronto! Agora podemos ver nossa aplicação funcionando e na próxima seção vamos visualizar o resultado.
Resultado
Para visualizarmos o resultado, vamos subir um servidor web embutido do PHP. Para isso, na raiz do projeto, você deve digitar o seguinte comando no terminal ou prompt de comando:
php -S localhost:8080
Pronto, acesse o link http://localhost:8080
no seu navegador de internet.
A animação abaixo mostra o resultado da nossa aplicação feita para a Adopet:
Perfeito! Fizemos um trabalho muito legal!
Obs: Na animação acima, o JSON apareceu estilizado, a extensão que eu utilizei foi a JSON Viewer.
Se alguma validação falhar, vai aparecer uma mensagem de erro, conforme a animação abaixo ilustra:
Prontinho! Projeto finalizado, não se esqueça que ficaram alguns desafios para praticar e aperfeiçoar ainda mais esta aplicação!
Se você quiser conferir o projeto completo você pode acessar o link do repositório.
Conclusão
Neste artigo aprendemos um passo a passo detalhado: desde baixar um projeto feito pela equipe de front-end até realizar a integração das validações do back-end.
Além disso, aprendemos sobre as facilidades que o PHP pode nos trazer quando o assunto é WEB e aplicamos algumas boas práticas no projeto. Mas é claro que podemos melhorar ainda mais; todavia, isso fica para próximos desafios.
Foi muito legal trabalhar com você! Te vejo na próxima missão.