Programação Defensiva em PHP na prática
Agilidade e produtividade com confiabilidade. Parece um sonho? Então vem comigo!
Se você está acostumado a trabalhar com PHP já se deparou com mensagens de erro chatas e pode até ter recorrido ao @ ou à instruções para ocultar mensagens de erro do interpretador.
Leia as mensagens a seguir e veja se você as reconhece de algum lugar:
- [] operator not supported for strings;
- Undefined index ***;
- Invalid argument supplied for foreach();
- Call to a member function ***() on null.
(Entre outras!)
O que essas mensagens tem em comum? Elas são geradas pelo interpretador por você ter programado operações sem garantir que as instruções que está executando possam ser realizadas no contexto do estado que estão.
Sim, é isso mesmo! A culpa é sua meu caro leitor!
Vamos ver a seguir como organizar a casa para evitar que esse tipo de problema possa gerar efeitos colaterais indesejados na sua aplicação.
Tipos importam
Calma, não fique bravo! Não vamos criar aqui a ditadura dos tipos, mas sim! Eles importam! Sendo assim, julgo ser adequado que os usemos do jeito certo.
Note que, neste exemplo, declaramos uma variável como string e depois a usamos como um array. Para evitar isso as principais opções que temos (além de sermos mais cautelosos na escrita do código) são usar nomes nas variáveis para indicar seus tipos e/ou usar estruturas para representar arrays.
Para o primeiro caso basta mudar a grafia do código como na imagem abaixo.
Já no segundo caso você pode pesquisar por opções de estruturas de dados no http://php.net/spl e por pacotes no https://packagist.org. Deixo inclusive uma opção abaixo que está disponível por lá e no nosso Github.
Importante lembrar que no caso de o desenvolvedor misturar as bolas e usar variáveis com valores (ainda que primitivos) que não são adequados às operações em que são usados o PHP vai “reclamar” também. Então fique atento!
Como podemos ver na imagem acima foi emitido um warning informando que a operação matemática não procedeu como o esperado.
Use a tipagem dinâmica como um trunfo e não como uma forma de ser displicente quanto aos valores (estados) que estão sendo mantidos e manipulados no seu algoritmo
Helpers! Muitos helpers!
Helpers são elementos que são inseridos nos projetos simplesmente para proverem recursos ao próprio projeto. Podemos criar funções, classes, traits, constants, com o único propósito de organizar e facilitar algumas tarefas.
Neste ponto, vale lembrar que quando o PHP recebe uma requisição ele captura os dados que vieram via HTTP e os transforma nas super globais $_GET e $_POST. Estas variáveis são arrays associativos como outro qualquer, logo a dica a seguir serve para elas também.
Na imagem acima temos um array chamado $_POST (igual ao que o PHP gera dinamicamente) e estamos tentando acessar um índice que não existe (na linha 8 teríamos o índice name, mas nada de phone). As instruções da linha 12 e da linha 14 são interessantes porque já implementam uma programação defensiva, mas podemos evoluir!
A função prop é chamada na imagem acima para resolver o teste sobre a existência do índice e passamos a não mais nos preocupar com a implementação interna dessa verificação. Podemos chamar esse tipo de função de helper, justamente pelo propósito dela dentro do projeto.
Como podemos ver acima, usando o recurso type hint nos parâmetros (array $array) podemos garantir que a entrada de dados seja adequada para a execução da função. Além disso também podemos aplicar mais testes sobre o conteúdo da variável $index. As linhas 14 à 16 poderiam prover uma defesa para um argumento inválido e reduzir a probabilidade de falhas ao acesso aos índices.
No exemplo acima, ainda mais elaborado, podemos capturar a falha e corrigir a tempo ou deixar que a aplicação capture o erro em handlers de níveis globais do projeto.
Não tenha problemas em pensar sobre o que está fazendo, seu trabalho como dev (na verdade) é esse, e, não apenas escrever códigos de forma desordenada
Ah, você ainda quer saber sobre o $_POST e o $_GET? Olha essa dica simples ai embaixo.
Vamos ser mais defensivos!
Não quero ser o chato da parada, mas você nunca deveria executar um trecho de código sem ter certeza da entrada que está recebendo.
Repare que o trecho acima está recuperando valores do HTTP. Ele procura por uma entrada pieces na Super Global $_POST. Note que não há nada que o trecho acima possa fazer para garantir que o conteúdo recebido seja de fato um array. No caso de não ser um array (ou qualquer tipo de Traversable) a estrutura de controle de fluxo vai gerar um erro por não conseguir lidar com a entrada que lhe foi fornecida.
A função nativa do PHP is_iterable vai tratar de validar se o conteúdo da variável $array pode ser usado por um foreach.
Curtiu? Quer mais exemplos? Abaixo podemos testar se uma variável existe (para evitar o clássico “Undefined variable”) e testar, inclusive, se é uma instância de um objeto antes de chamar um método que as instâncias da classe PDO possuem.
A programação defensiva precisa entrar na mente do desenvolvedor como algo rotineiro, quase que orgânico
Defender-se de si e de entradas externas
Quando estamos praticando programação defensiva não podemos esquecer de nos proteger da entrada de dados dos usuários. Neste ponto o assunto já passa a ser sobre a segurança da aplicação e não apenas sobre sua resiliência.
O PHP nos entrega uma série de filters, validations e sanitizes que podem ser usados para isso. Você pode ver mais sobre isso aqui.
A evolução do PHP
O PHP tem entregue cada vez mais features para nos ajudar na programação defensiva.
A evolução da captura de erros com a interface throwable;
O suporte à tipos primitivos nos parameter type hints;
A habilidade de garantir que a saída de métodos e funções com os return types;
E, a já prometida para a versão 7.4 typed property que garante que os atributos da classe sejam de determinados tipos;
São exemplos de como a linguagem tem trabalhado pesado para nos ajudar nisso e como essa é uma tarefa realmente nobre. Usando estas features podemos reduzir o boilerplate de estruturas de controle como if para proteger nosso código.
Era isso pessoal, espero que este artigo ajude aí na escrita de códigos mais defensivos e com menos efeitos colaterais. Lembre-se que as mensagens de erro são amigas do desenvolvedor e não tente ocultá-las ou ignorá-las.
Quer falar mais sobre esse assunto ou outros temas relacionados à PHP? Chega mais então!