PHP Array Walk Sanitizar Objeto de forma recursiva

function sanitizeObject($object): void {
  array_walk($object, function($value, $key) use($object) {
    if (is_object($value) || is_array($value)) {
      self::sanitizeObject($value);
    }

    if ($value === null
        || (is_string($value) && empty($value))
        || (is_array($value) && !$value)
        || (is_object($value) && !(array)$value)
    ) {
      unset($object->{$key});
    }
  });
}

Bom pessoal, ao invés de explicar o que este código faz vou tentar explicar qual era o problema que eu tinha de resolver, acho que dessa forma o artigo fará mais sentido pra quem estiver lendo.

Eu estava trabalhando numa integração entre sistemas e a API que recebia as informações era bastante sensível quanto ao conteúdo da request. Por exemplo, o comportamento da API poderia variar muito entre eu enviar uma propriedade com conteúdo NULL ou então com a propriedade com conteúdo vazio. Em ambos os casos o melhor cenário seria não enviar a propriedade na request.

Já no início recebi uma dica de usar o array_filter para eliminar as propriedades nulas ou vazias, como este código aqui por exemplo:

function clearNullFields($metadata) {
  return (object)array_filter((array)$metadata, static function ($value) {
    return !(null === $value || trim($value) === '');
  });
}

O array_filter até poderia resolver a questão, isso se o objeto tivesse um único nível. Mas não era esse o caso, uma request de API normalmente é composta por vários outros objetos em múltiplos níveis.

Depois de pesquisar um pouco me deparei com um artigo onde a solução era dada por um array_walk. Bingo!

Bastou esta centelha e um pouco de criatividade para adicionar a recursividade fazendo com que todas as propriedades nulas ou vazias fossem sistematicamente removidas com unset.

Até mesmo um objeto mais interno cujas propriedades eram todas nulas ou vazias acabou sendo completamente removido. Isto porque a recursividade resolve as remoções do nível mais baixo para o nivel mais alto.

Assim após remover todas as propriedades escalares de um objeto, ele mesmo será um objeto vazio. Consequentemente ao retornar da recursividade inferior o objeto estará vazio e será removido exatamente por este motivo.

Mas um aviso importante!

O array_walk opera sobre o objeto fornecido alterando suas características originais. Assim para não comprometer nenhum processo que utilizasse o objeto original a solução foi criar um objeto totalmente novo para ser sanitizado. Como?

$sanitize = \json_decode(\json_encode($original));
sanitizeObject($sanitize);

Fantástico, funciona melhor do que o programado!

PHP DateTime Diff Calculando diferença de dias entre datas

function calculateDiffInDays(string $date1, string $date2) {
  $datetime1 = new \DateTime($date1);
  $datetime2 = new \DateTime($date2);
  
  return $datetime1->diff($datetime2)->days;
}

echo calculateDiffInDays('1970-08-25', '2020-12-30');
// 18390
echo calculateDiffInDays('2020-12-30', '1970-08-25');
// 18390

Recentemente precisei ajustar um processo que já fazia um cálculo de diferença de dias entre datas e me deparei com um código bem similar a este, só que não funcionava.

Daí fui pesquisar a boa e velha documentação do PHP e encontrei a falha.

Primeiro vamos esclarecer que o DateTime::diff retornar um DateInterval o qual então pode ser formatado de várias formas… https://www.php.net/manual/en/class.dateinterval.php

Observe que days representa o total de número de dias entre a primeira e segunda datas e isso significa dizer que o resultado será um número inteiro.

Enquanto isso d representa simplesmente um possível dia, ou seja, será um número entre 0 e 30 pois o cálculo é feito levando em conta apenas a porção dia. Assim a hipótese para o maior resultado seria o dia 31 menos o dia 01 sendo o resultado 30.

Então, o código original com o qual me deparei formatava o resultado usando a tag ->d quando na verdade deveria estar utilizando ->days para obter a diferença total de dias.

return $datetime1->diff($datetime2)->d;

Confesso que apenas lendo a documentação fica difícil de ter certeza do resultado assim de imediato. Como sempre com PHP um teste rápido é o seu melhor aliado.

Mas todos nós sabemos que o PHP faz o que é preciso fazer ou faz o que a gente quer que ele faça, não importa.

O que algumas pessoas costumam usar para criticar o PHP, na verdade é o que eu mais gosto nele!