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

Converter data dd/mm/aaaa para o MySQL com DateTime::createFromFormat

strtotime com data brasileira devolve false e o INSERT quebra no MySQL 8. Use createFromFormat com getLastErrors antes de mandar…

1 hora atrás

Erro 500 no Magento 2: achar o relatório em var/report pelo ID da tela

O hash que o Magento mostra junto do erro é o nome de um arquivo em var/report. Veja como abrir,…

1 dia atrás

Campo extra no checkout em blocos do WooCommerce sem voltar pro shortcode

Seu snippet com woocommerce_checkout_fields parou de aparecer no checkout em blocos. Registre o campo pela API nova e leia o…

2 dias atrás

Fuso horário no PHP 8.3: log com 3 horas de diferença no servidor

O pedido entrou às 21h e o relatório mostra 00h do dia seguinte. Onde setar date.timezone no PHP 8.3 e…

3 dias atrás

Magento 2 não envia e-mail de pedido: async sending e cron parado

O pedido entra na loja e o e-mail nunca sai: antes de culpar o SMTP, veja o envio assíncrono, a…

3 dias atrás

cron_schedules WordPress: intervalo de 5 minutos no WP-Cron

O WordPress só oferece hourly, twicedaily, daily e weekly. Use o filtro cron_schedules para criar um intervalo de 5 minutos…

4 dias atrás