Magento 2 no PHP 8.3 e 8.4: corrigindo problemas de compatibilidade
O Magento 2.4.8 roda no PHP 8.3 e 8.4, e as versões mais antigas do PHP estão chegando ao fim da vida. Para a maioria das lojas, o core não é o problema. A Adobe testa o core nas versões suportadas do PHP. Os problemas vêm dos módulos customizados em app/code e das extensões de terceiros que foram escritas para o PHP 7.4 e nunca mais tocadas.
Este artigo cobre as mudanças de linguagem que de fato quebram código Magento, como achá-las sem ler todos os arquivos e como corrigir mantendo o código legível.
O que mudou entre o PHP 7.4 e o 8.4
Se uma loja pula do PHP 7.4 ou 8.1 direto para o 8.3 ou 8.4, ela junta vários anos de mudanças de uma vez. Estas são as que mais vejo em código Magento.
Passar null para funções internas (8.1)
Desde o PHP 8.1, passar null para um parâmetro não nullable de uma função nativa gera uma deprecação. Código como strlen($value), trim($value) ou explode(',', $value), em que $value vem de getData(), está em toda extensão Magento. O getData() devolve null para chaves ausentes, então a deprecação dispara com frequência.
// Before
$sku = trim($product->getData('vendor_sku'));
// After
$sku = trim((string) $product->getData('vendor_sku'));
Um cast resolve quando uma string vazia é um fallback válido. Quando null significa algo diferente de vazio, trate explicitamente em vez de esconder.
Propriedades dinâmicas (8.2)
Criar uma propriedade que não foi declarada na classe está deprecado desde o 8.2. Código Magento antigo faz isso com frequência em blocks e helpers, por exemplo atribuindo $this->cache dentro de um método sem declarar. Classes que estendem Magento\Framework\DataObject não são afetadas no acesso estilo setData(), porque ele passa por métodos mágicos e um array interno. Classes simples são afetadas.
class PriceHelper
{
private array $cache = [];
public function get(int $productId): ?float
{
return $this->cache[$productId] ?? null;
}
}
A correção é declarar a propriedade, com tipo sempre que der. Não adicione o atributo #[\AllowDynamicProperties] como correção geral. Ele esconde o problema no seu próprio código, onde declarar a propriedade é uma mudança de uma linha.
Parâmetros implicitamente nullable (8.4)
Esta é a mudança que gera mais warnings no PHP 8.4. Um parâmetro com tipo e default null costumava ser nullable automaticamente. Essa forma agora está deprecada.
// Deprecated in PHP 8.4
public function execute(OrderInterface $order = null)
// Correct
public function execute(?OrderInterface $order = null)
Os constructors do Magento estão cheios desse padrão, porque dependências opcionais eram comumente declaradas como Json $serializer = null para manter a compatibilidade retroativa. A correção é mecânica e segura, o que a torna um bom trabalho para o Rector.
Outras mudanças que vale conferir
- A interpolação de string
${var}está deprecada desde o 8.2. Use{$var}. utf8_encode()eutf8_decode()estão deprecadas desde o 8.2. Usemb_convert_encoding().- O PHP 8.0 mudou a comparação de string com número.
0 == 'abc'agora é false. Código que dependia de comparações soltas com valores de atributos pode mudar de comportamento sem nenhum warning. - O PHP 8.0 fez muitas funções internas lançarem
TypeErrorouValueErrorem vez de devolver false com um warning. Código que checava false depois de passar um tipo errado agora recebe uma exceção. - Declarações de tipo de retorno em interfaces internas. Classes que implementam
ArrayAccess,Iterator,CountableouJsonSerializablesem os tipos de retorno correspondentes disparam deprecações desde o 8.1.
Achando os problemas
Rodar a loja e esperar os erros só encontra os caminhos de código que você por acaso percorre. A análise estática encontra o resto. Eu uso três ferramentas juntas.
PHPCompatibility
O PHPCompatibility é um standard do PHP_CodeSniffer que verifica o código contra uma versão alvo do PHP. O coding standard do Magento já o puxa, então se você tem o magento/magento-coding-standard instalado provavelmente tem esse também. Confira qual versão você tem, porque releases antigas não conhecem as versões mais novas do PHP.
vendor/bin/phpcs -i
vendor/bin/phpcs --standard=PHPCompatibility \
--runtime-set testVersion 8.3- \
--extensions=php,phtml \
app/code
testVersion 8.3- significa PHP 8.3 e acima. Rode em app/code e nas pastas vendor das extensões que você mantém ou pretende corrigir.
Rector para as correções mecânicas
O Rector reescreve código automaticamente. Em um upgrade eu prefiro regras selecionadas a level sets completos do PHP, porque os sets completos também reescrevem o código para usar sintaxe nova, o que deixa o diff enorme e mais difícil de revisar. Uma config mínima para a correção de nullable do 8.4:
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector;
return RectorConfig::configure()
->withPaths([__DIR__ . '/app/code'])
->withRules([
ExplicitNullableParamTypeRector::class,
]);
vendor/bin/rector process --dry-run
vendor/bin/rector process
Rode sempre o dry run primeiro e leia o diff. O Rector é confiável para essas regras simples, mas você continua responsável pelo que vai para o commit.
PHPStan
O PHPStan pega os erros que não são deprecações: tipos errados passados a métodos, chamadas a métodos que não existem, nulls chegando em código que não sabe lidar com eles. Com a extensão Magento da bitExpert ele entende factories e proxies gerados. Comece em um level baixo e suba com o tempo. Mais sobre o setup no guia do PHPStan.
Vendo deprecações em runtime
As ferramentas estáticas perdem código montado a partir de strings ou carregado por configuração. Para pegá-lo, registre as deprecações em log enquanto testa. Na configuração do PHP local ou de staging:
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /var/log/php/deprecations.log
Depois navegue pelos fluxos principais: categoria, produto, carrinho, checkout, conta do cliente, telas de produto e pedido no admin. Agrupe o log por arquivo e corrija primeiro as mais frequentes. A maior parte do ruído costuma vir de poucos métodos helper chamados em toda página.
Extensões de terceiros
Para extensões instaladas pelo Composer, a correção certa é uma versão nova do fornecedor. Procure em cada fornecedor um release que declare suporte à versão alvo do PHP no composer.json. Se a constraint do pacote exclui a sua versão do PHP, o Composer vai se recusar a instalar, que é o comportamento que você quer.
Quando o fornecedor sumiu ou é lento, você tem duas opções:
- Aplicar um patch no pacote com um Composer patch enquanto espera um release oficial. Mantenha o patch pequeno e limitado a correções de compatibilidade.
- Trocar a extensão. Extensões antigas que ninguém mantém são um risco de segurança além de um risco de compatibilidade. Uma auditoria de extensões ajuda a decidir quais manter.
Nunca resolva um conflito do Composer com --ignore-platform-reqs em builds de produção. Ele instala pacotes que os próprios autores disseram não suportar a sua versão do PHP, e a falha aparece depois, em runtime.
Uma ordem de trabalho sensata
- Atualize o PHP no ambiente local mantendo a versão atual do Magento, se essa combinação for suportada.
- Rode PHPCompatibility e PHPStan em app/code. Aplique o Rector nas correções mecânicas.
- Atualize ou corrija as extensões de terceiros.
- Teste com o log de deprecações ligado e corrija o que aparecer.
- Faça o release do upgrade de PHP em produção sozinho.
- Atualize o Magento em um release separado.
Separar o upgrade de PHP do upgrade do Magento é o hábito mais útil aqui. Quando algo quebra, você sabe qual mudança causou.
FAQ
Qual versão do PHP devo usar no Magento 2.4.8?
O 2.4.8 suporta PHP 8.3 e 8.4. Confira a página de requisitos de sistema para o seu patch release exato e confirme que toda extensão que você usa suporta a versão escolhida.
Deprecações do PHP são erros?
Não. Uma deprecação é um aviso de que o código vai quebrar em uma versão futura do PHP. A loja continua funcionando, mas você deve corrigi-las antes que o próximo upgrade as transforme em erros fatais.
Posso usar AllowDynamicProperties para silenciar os warnings do PHP 8.2?
Funciona, mas esconde um problema que normalmente se resolve declarando a propriedade. Use só para código de fornecedor que você ainda não consegue mudar.
O Rector muda o comportamento do meu código?
Regras como a ExplicitNullableParamTypeRector só mudam a sintaxe. Level sets maiores podem mudar mais, então rode um dry run e revise o diff antes de fazer o commit.
Atendo como freelancer em Magento e Adobe Commerce, incluindo upgrades de PHP e correções de extensões. Se precisar de ajuda com isso, fale comigo.
Artigos relacionados
Checklist de upgrade para o Magento 2.4.8, passo a passo
Checklist prático para atualizar Magento Open Source ou Adobe Commerce para a 2.4.8: requisitos de stack, extensões, passos do Composer, testes e go live.
Ferramentas de IA no desenvolvimento Magento: o que funciona e o que falha
Usando assistentes de código com IA em projetos Magento 2: onde economizam tempo, onde inventam código, como dar contexto e como verificar o resultado.
Coding standard do Magento 2 com PHPCS: setup e uso
Configure o coding standard do Magento 2 com PHP_CodeSniffer: o ruleset Magento2, phpcs.xml do projeto, severity, phpcbf e checagem dos arquivos alterados.
PHPStan no Magento 2: guia de setup de análise estática
Rode o PHPStan no código customizado do Magento 2: a extensão phpstan-magento para classes geradas, escolha do level, baselines para código legado e integração com CI.