Comportamento estranho numa loja com vinte extensões: o preço sai diferente do que a regra deveria dar, ou um método que você leu no core claramente não faz aquilo. A pergunta é sempre a mesma — quem entrou no meio.
A resposta não está no grep. Grep acha todas as declarações de preference; o que você quer saber é qual delas o Magento resolveu usar depois de mesclar todo mundo. É isso que o dev:di:info responde.
A pergunta é: quem ganhou
O comando aceita a classe ou a interface, e um segundo argumento opcional com a área:
php bin/magento dev:di:info 'Magento\Catalog\Api\ProductRepositoryInterface'A primeira coisa que sai é o cabeçalho — DI configuration for the class ... in the GLOBAL area — e logo abaixo a linha que interessa:
Preference: Magento\Catalog\Model\ProductRepositoryNuma instalação limpa vem a classe do core. Se vier Fornecedor\Modulo\Model\ProductRepository, achou o culpado em dois segundos.
Só existe uma preference vencedora por área. Quando dois módulos declaram preference para a mesma interface, quem carrega por último vence, e essa ordem sai do nó sequence do module.xml de cada módulo. Se quiser conferir a ordem já resolvida, ela está na lista de módulos do app/etc/config.php: o Magento grava ali na sequência de carregamento, não em ordem de instalação.
Aspas simples no shell não são frescura: sem elas, a barra invertida do namespace vira escape e o comando reclama que a classe não existe.
A tabela do construtor é onde mora a surpresa
Depois da preference vem Constructor Parameters:, uma tabela com três colunas: Name, Requested Type e Configured Value.
A terceira coluna é a mais útil e a que quase ninguém lê. Ela mostra o que o di.xml mandou injetar naquele parâmetro específico — que pode ser uma classe diferente da declarada no tipo, um virtual type, um array montado a mão ou um valor literal. É assim que uma extensão troca um pedaço do comportamento sem sobrescrever a classe inteira: ela só troca uma dependência.
Logo abaixo pode aparecer Virtual Types:, listando os virtual types construídos em cima da classe que você consultou. Isso vale ouro quando o código que você lê parece certo mas o objeto em execução foi montado com outra configuração.
E no fim vêm duas tabelas de plugins, com as colunas Plugin, Method e Type: Plugins:, para a classe que você pediu, e Plugins for the Preference:, para a classe que acabou sendo instanciada. Precisa olhar as duas — plugin declarado na implementação concreta não aparece na primeira.
A área muda a resposta inteira
Sem o segundo argumento o comando assume global. E metade das configurações de DI de loja de verdade não está em etc/di.xml: está em etc/frontend/di.xml, etc/adminhtml/di.xml ou etc/webapi_rest/di.xml.
php bin/magento dev:di:info 'Magento\Quote\Api\CartRepositoryInterface' frontend
php bin/magento dev:di:info 'Magento\Quote\Api\CartRepositoryInterface' webapi_restAs áreas válidas são global, frontend, adminhtml, crontab, webapi_rest, webapi_soap e graphql. Área inválida devolve erro na cara, então não tem como errar em silêncio.
Esse detalhe explica um caso que eu vejo direto: funciona pelo site e quebra pela API, ou o contrário. São configurações de DI diferentes, e o comando confirma isso em uma linha.
Duas coisas que o dev:di:info não responde, para você não perder tempo: observer (isso é events.xml, outro sistema) e bloco ou template (isso é layout XML). Ele só enxerga o que passa pelo object manager.
Última: em produção, o que ele lê é a configuração compilada. Mudou di.xml e o comando insiste em mostrar o valor velho? Falta setup:di:compile e cache:flush.
Perguntas rápidas
Dá para usar isso em produção com a loja no ar?
Dá. O comando só lê configuração, não escreve nada. Ele é um pouco pesado porque carrega a área inteira, então evite rodar em rajada no horário de pico.
Como eu descubro a ordem em que os módulos carregam?
A lista de módulos no app/etc/config.php já está na ordem resolvida pelo sequence de cada module.xml. Quem aparece depois mescla por cima de quem apareceu antes.
O comando mostra plugin desabilitado?
Não. Plugin marcado com disabled true no di.xml sai da configuração mesclada, então ele simplesmente não aparece na tabela. É por isso que desligar plugin de terceiro pelo di.xml funciona de verdade.
Pra conferir na fonte
- DiInfoCommand.php (o que o comando imprime) — GitHub — magento/magento2
- Command-line reference: Commerce on-premises — Adobe Experience League
- Component load order — Adobe Developer
- Plugins (interceptors) — Adobe Developer