Você atualiza a stack, sobe para PHP 8.4 e o log de erro do PHP começa a crescer de minuto em minuto com a mesma linha repetida centenas de vezes:
Deprecated: Implicitly marking parameter $context as nullable is deprecated, the explicit nullable type must be used insteadA loja funciona, nada dá 500, e é justamente por isso que muita gente ignora. Só que ignorar tem prazo de validade.
O que mudou no PHP 8.4
A RFC Deprecate Implicitly Nullable Parameter Types foi aprovada e entrou no PHP 8.4. Ela mira exatamente esta assinatura:
function foo(T $var = null) {}O valor padrão null fazia o PHP aceitar null naquele parâmetro sem você declarar. Continua aceitando — mas agora avisa. A correção é escrever o que sempre foi verdade:
// antes
public function __construct(LoggerInterface $logger = null) {}
// depois
public function __construct(?LoggerInterface $logger = null) {}Com union type, o equivalente é acrescentar |null: string|int|null $x = null.
Dois detalhes que mudam o tamanho do problema. Primeiro, a RFC diz claramente que o suporte será removido no PHP 9: hoje é aviso, depois vira erro. Segundo, e é o que explica o volume no log, o aviso é emitido em tempo de compilação. Não é só quando a função é chamada — é toda vez que o arquivo é compilado. Em servidor com OPcache frio ou com validate_timestamps ligado, isso é muito log.
Onde isso pega no Magento
Primeiro, o calendário. Segundo a tabela de requisitos da Adobe, o 2.4.8 roda em PHP 8.4 e 8.3, e o 2.4.9 — lançado em 22 de maio de 2026 — pede PHP 8.5. Ou seja: quem está atualizando agora vai encarar isso de qualquer jeito.
Segundo, o padrão de código que todo mundo aprendeu a escrever no Magento. Quando você precisa injetar uma dependência nova numa classe que já existe sem quebrar quem estende ela, a receita é sempre a mesma: argumento novo no fim do construtor, opcional, com = null, e o ObjectManager::getInstance() lá dentro como plano B.
public function __construct(
Context $context,
ProductRepositoryInterface $productRepository,
Json $serializer = null
) {
$this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
}Esse Json $serializer = null é exatamente o caso da RFC. Como a prática é antiga e universal, módulo de terceiro escrito entre 2017 e 2023 costuma ter dezenas de ocorrências. Some o vendor/ inteiro e você entende o tamanho do log.
Não confunda com $x = null em parâmetro sem tipo. function foo($var = null) não tem tipo declarado, então não dispara nada. O aviso só vale para parâmetro tipado com padrão nulo.
Corrija em massa, sem revisar arquivo por arquivo
O PHP-CS-Fixer tem uma regra que faz exatamente essa troca, e a documentação a classifica como non-risky — ela só alinha a declaração com o valor padrão, sem mudar comportamento.
composer require --dev friendsofphp/php-cs-fixerAntes de escrever qualquer coisa, veja o estrago em modo seco:
vendor/bin/php-cs-fixer fix app/code \
--rules=nullable_type_declaration_for_default_null_value \
--dry-run --diffDuas coisas importantes nesse comando. Passar --rules com uma regra só faz o Fixer aplicar apenas ela — sem isso ele reformata indentação, aspas e imports do seu módulo inteiro, e aí o diff vira um monstro que ninguém revisa. E aponte para app/code, nunca para vendor/: código de terceiro você não altera no lugar, senão o próximo composer update apaga tudo.
Gostou do diff? Tire o --dry-run:
vendor/bin/php-cs-fixer fix app/code --rules=nullable_type_declaration_for_default_null_valueQuem já usa Rector no projeto tem o equivalente na regra ExplicitNullableParamTypeRector, que faz parte do conjunto de PHP 8.4 dele. Tanto faz a ferramenta; o que não vale é sair editando na mão.
Depois de corrigir
Três passos que eu não pulo:
- Limpe o código gerado. Interceptor e factory em
generated/codesão compilados igual a qualquer PHP. Roderm -rf generated/*ephp bin/magento setup:di:compiledepois da correção. - Rode os testes. A troca é segura no papel, mas o diff pode ter tocado em arquivo que você nem lembrava que existia. Se o módulo tem teste unitário, é a hora.
- Não silencie. Tirar
E_DEPRECATEDdoerror_reportingresolve o log e não resolve o problema — no PHP 9 o mesmo código para de compilar.
Para o que veio do vendor/, a saída é outra: abra issue ou pull request no repositório do módulo, ou aplique um patch versionado com cweagans/composer-patches. Se o fornecedor não responde há dois anos, isso já é resposta sobre continuar usando aquele módulo — assunto que eu trato no guia de desenvolvimento de módulos.
E aproveite a passagem: se você mesmo escreve módulo, pare de adicionar dependência nova com = null por reflexo. Em código novo, declare ?Tipo desde o começo e você não paga essa conta de novo.
Perguntas rápidas
Isso pode derrubar minha loja hoje?
Não. É um aviso, o código continua rodando exatamente igual. Os efeitos reais são log gigante, disco enchendo e pipeline de CI que trata deprecated como erro falhando. O prazo é o PHP 9, quando a construção deixa de ser aceita.
Preciso corrigir isso antes de atualizar para o 2.4.9?
Não é bloqueio para atualizar, mas é bom fazer junto. Como o 2.4.9 pede PHP 8.5, você vai passar por essa faixa de versão de qualquer forma, e é bem mais barato rodar o fixer antes do que caçar log depois que a loja já está no ar.
O PHP-CS-Fixer vai bagunçar meu código?
Só se você deixar. Passando --rules com uma única regra, ele altera apenas os parâmetros com padrão nulo. Rode primeiro com --dry-run e --diff, confira o resultado e só então aplique.
Pra conferir na fonte
- PHP RFC: Deprecate Implicitly Nullable Parameter Types — php.net
- Rule nullable_type_declaration_for_default_null_value — PHP-CS-Fixer
- System requirements — Adobe Experience League
- Magento Open Source 2.4.9 release notes — Adobe Experience League