PHP

DateTimeImmutable modify: por que a data não muda no PHP 8.3

Trocou DateTime por DateTimeImmutable e o ->modify('+1 day') parou de fazer efeito. Não parou: ele nunca alterou o objeto. Todo método de mudança da classe imutável devolve uma instância nova e deixa a original intacta. Se você não guardar o retorno, jogou fora o resultado.

<?php
$d = new DateTimeImmutable('2026-08-31 10:00', new DateTimeZone('America/Sao_Paulo'));

$d->modify('+1 day');
echo $d->format('Y-m-d'), PHP_EOL;   // 2026-08-31 — retorno descartado

$d = $d->modify('+1 day');
echo $d->format('Y-m-d'), PHP_EOL;   // 2026-09-01

Vale o mesmo para add, sub, setTime, setDate e setTimezone. A regra prática: nessa classe, o retorno é o único lugar onde a mudança existe.

O bug que isso evita em loop

<?php
$inicio = new DateTimeImmutable('2026-09-01');
$dias   = [];

for ($i = 0; $i < 5; $i++) {
    $dias[] = $inicio->add(new DateInterval('P' . $i . 'D'));
}

foreach ($dias as $dia) {
    echo $dia->format('d/m'), PHP_EOL;  // 01/09 02/09 03/09 04/09 05/09
}

Troque a primeira linha por DateTime e o mesmo laço imprime 11/09 cinco vezes. O array guarda cinco referências ao único objeto que existe, e cada add empurra ele mais pra frente até somar os dez dias do acumulado. É o clássico do relatório que sai com todas as linhas na mesma data, e o motivo de eu usar a versão imutável como padrão em cálculo de prazo.

Uma diferença nova no 8.3

Até o PHP 8.2, string de data inválida em modify() soltava um Warning e devolvia false — com display_errors desligado, o erro só aparecia lá na frente, na forma de uma data errada. No 8.3 ela lança DateMalformedStringException. Se o texto vem do usuário ou de planilha importada, embrulhe a chamada.

try {
    $ate = $inicio->modify($_POST['ate'] ?? '');
} catch (DateMalformedStringException $e) {   // PHP 8.3+
    $ate = $inicio->modify('+30 days');
}

Se o projeto ainda roda 8.2, o teste é contra false antes de usar o valor. Migrar pro 8.3 só troca onde o erro aparece: agora ele derruba a requisição em vez de contaminar o cálculo silenciosamente.

Post Recentes

Defer nos scripts do WordPress 6.9 sem plugin, com wp_enqueue_script

Use o parâmetro strategy do wp_enqueue_script para adiar o JavaScript do tema, sem plugin de otimização, e entenda por que…

1 dia atrás

Banner de cookie que bloqueia de verdade: script só carrega depois do aceite

O banner aparece, o visitante não clica em nada e o Analytics já gravou cookie. Veja como segurar o script…

2 dias atrás

Revisar git diff com IA antes do commit: script que lê só o staged

Var_dump esquecido e senha de teste passam batido no commit. Um script PHP que lê o git diff --staged e…

2 dias atrás

Cannot query field no GraphQL do Magento 2: o campo existe e some

O campo está no schema.graphqls e a API insiste em Cannot query field. Limpe o cache certo, recompile o generated…

3 dias atrás

Block pattern não aparece no inseridor: registre pelo arquivo em /patterns

Padrão registrado no functions.php que não aparece na aba Padrões. O caminho curto do WordPress 6.9: um arquivo PHP na…

4 dias atrás

Tamanho das tabelas MySQL 8: a query que acha o vilão do disco

Disco lotando por causa do banco? Uma query no information_schema mostra qual tabela cresceu, quanto é dado, quanto é índice…

4 dias atrás