Neste artigo, veremos alguns exemplos de uso do componente Symfony DependencyInjection. Você aprenderá o básico da injeção de dependência, que permite um código mais limpo e modular, e verá como usá-lo em suas aplicações PHP com o componente Symfony.
O que é o componente DependencyInjection do Symfony?
O componente DependencyInjection do Symfony fornece uma maneira padrão de instanciar objetos e lidar com o gerenciamento de dependências em seus aplicativos PHP. O coração do componente DependencyInjection é um contêiner, que contém todos os serviços disponíveis no aplicativo.
Durante a fase de bootstrap do seu aplicativo, você deve registrar todos os serviços do seu aplicativo no contêiner. Em um estágio posterior, um contêiner é responsável por criar serviços conforme necessário. Mais importante, um container também é responsável por criar e injetar dependências dos serviços.
O benefício dessa abordagem é que você não precisa codificar o processo de instanciar objetos, pois as dependências serão detectadas e injetadas automaticamente. Isso cria um acoplamento fraco entre as partes do seu aplicativo.
Neste artigo, exploraremos como você pode liberar o poder do componente DependencyInjection. Como de costume, começaremos com as instruções de instalação e configuração e implementaremos alguns exemplos do mundo real para demonstrar os principais conceitos.
Instalação e configuração
Nesta seção, iremos em frente e instalaremos o componente DependencyInjection. Suponho que você já tenha instalado o Composer em seu sistema, pois precisaremos dele para instalar o componente DependencyInjection disponível no Packagist.
Então vá em frente e instale o componente DependencyInjection usando o seguinte comando.
$composer require symfony/dependency-injection
Isso deveria ter criado o compositor.json arquivo, que deve ficar assim:
{ "require": { "symfony/dependency-injection": "^5.4" } }
Também instalaremos alguns outros componentes que serão úteis em nossos exemplos.
Se você deseja carregar serviços de um arquivo YAML em vez de defini-lo no código PHP, é o componente Yaml que vem em socorro, pois ajuda a converter strings YAML em tipos de dados compatíveis com PHP e vice-versa.
$composer require symfony/yaml
Finalmente, instalaremos o componente Config, que fornece várias classes de utilitários para inicializar e lidar com valores de configuração definidos em diferentes tipos de arquivos, como YAML, INI e XML. No nosso caso, vamos usá-lo para carregar serviços do arquivo YAML.
$composer require symfony/config
Vamos modificar o compositor.json arquivo para se parecer com o seguinte.
{ "require": { "symfony/dependency-injection": "^5.4", "symfony/yaml": "^5.4", "symfony/config": "^5.4" }, "autoload": { "psr-4": { "Services\": "src" }, "classmap": ["src"] } }
Como adicionamos um novo classmap
entrada, vamos em frente e atualizar o autoloader do compositor executando o seguinte comando.
$composer dump -o
Agora, você pode usar o Services
namespace para carregar automaticamente as classes sob o src diretório.
Então essa é a parte da instalação, mas como você deve usá-la? Na verdade, é apenas uma questão de incluir o autoload.php arquivo criado pelo Composer em seu aplicativo, conforme mostrado no trecho a seguir.
Como trabalhar com um contêiner
Nesta seção, veremos um exemplo para demonstrar como você pode injetar serviços em um contêiner. Um contêiner deve atuar como um repositório central que contém todos os serviços em seu aplicativo. Mais tarde, poderíamos usar um contêiner para buscar serviços conforme necessário.
Para começar, vamos definir um serviço bem básico em src/DemoService.php com o seguinte conteúdo.
Este é um serviço muito simples, que apenas implementa o
helloWorld
método para o momento.Em seguida, vá em frente e crie o basic_container.php arquivo com o seguinte conteúdo na raiz do seu aplicativo.
register('demo.service', 'ServicesDemoService'); // fetch service from the service container $demoService = $containerBuilder->get('demo.service'); echo $demoService->helloWorld();Para começar, instanciamos o
ContainerBuilder
objeto com onew ContainerBuilder()
construtor. A seguir, utilizamos oregister
método doContainerBuilder
objeto para injetar nosso serviço personalizadoServicesDemoService
no recipiente. odemo.service
atua como um alias para o nosso serviço.Por fim, usamos o
get
método doContainerBuilder
objeto para buscar nosso serviço do contêiner e usá-lo para chamar ohelloWorld
método.Então essa foi uma demonstração básica de como trabalhar com um container. Na próxima seção, estenderemos este exemplo para explorar como as dependências de classe são resolvidas usando um contêiner.
Um exemplo do mundo real
Nesta seção, criaremos um exemplo que demonstra como as dependências de classe são resolvidas usando o componente DependencyInjection.
Para demonstrá-lo, criaremos um novo serviço
DependentService
que exige oDemoService
service, criado na seção anterior, como uma dependência. Assim, veremos como oDemoService
service é injetado automaticamente como uma dependência quando oDependentService
serviço é instanciado.Vá em frente e crie o src/DependentService.php arquivo com o seguinte conteúdo para definir o
DependentService
serviço.demo_service = $demoService; } public function helloWorld() { return $this->demo_service->helloWorld(); } }Como você pode ver, o
ServicesDemoService
serviço é necessário para instanciar oDependentService
serviço.Em seguida, vá em frente e crie o di_container.php arquivo com o seguinte conteúdo.
register('demo.service', 'ServicesDemoService'); // add dependent service into the service container $containerBuilder->register('dependent.service', 'ServicesDependentService') ->addArgument(new Reference('demo.service')); // fetch service from the service container $dependentService = $containerBuilder->get('dependent.service'); echo $dependentService->helloWorld();Estamos usando o mesmo
register
método para injetar nosso serviço personalizadoServicesDependentService
no recipiente.Além disso, também usamos o
addArgument
método para informar o container sobre a dependência doDependentService
serviço. Nós usamos oReference
classe para informar ao container que ele precisa injetar odemo.service
serviço quando odependent.service
serviço é instanciado. Dessa forma, uma dependência é injetada automaticamente conforme necessário!Por fim, usamos o
get
método doContainerBuilder
objeto para buscar odependent.service
serviço doContainerBuilder
objeto e usei-o para chamar ohelloWorld
método.Dessa forma, o componente DependencyInjection fornece uma maneira padrão de instanciar objetos e injetar dependências em seu aplicativo.
Como carregar serviços dinamicamente usando o arquivo YAML
Nesta seção, exploraremos como você pode carregar serviços dinamicamente do arquivo YAML. Basicamente, atualizaremos o exemplo discutido na seção anterior.
Além do componente DependencyInjection, também precisaremos de mais dois componentes Symfony para implementar o exemplo YAML—Config e Yaml. Lembre-se de que já instalamos esses dois componentes no Instalação e configuração seção junto com o próprio componente DependencyInjection. Então estamos prontos!
Vá em frente e crie o services.yaml arquivo com o seguinte conteúdo na raiz do seu aplicativo.
services: demo.service: class: ServicesDemoService dependent.service: class: ServicesDependentService arguments: ["@demo.service"]Como você pode ver, é bastante simples definir serviços usando a sintaxe YAML. Para definir as dependências do seu serviço, você precisará usar o
arguments
chave.Em seguida, vá em frente e crie o di_yaml_container.php arquivo com o seguinte conteúdo.
load('services.yaml'); // fetch service from the service container $serviceOne = $containerBuilder->get('dependent.service'); echo $serviceOne->helloWorld();Tudo é praticamente o mesmo, exceto que estamos carregando serviços do services.yaml arquivo em vez de defini-lo no próprio código PHP. Isso permite que as dependências do aplicativo sejam definidas dinamicamente.
Como injetar um serviço preguiçoso
Em alguns casos, você deseja injetar um serviço lento. Às vezes, você tem um serviço que é muito pesado para instanciar. Portanto, se você deseja injetar esse serviço, deseja que ele seja injetado apenas quando for verdade necessário, e não antes disso. A resposta a esta pergunta é um serviço preguiçoso.
Mas, como isso funciona exatamente? Na verdade, quando você configura um serviço lento, em vez de injetar o serviço real, um proxy do serviço é injetado. Na superfície, um serviço de proxy funciona como o serviço real, mas assim que você começa a interagir com o serviço de proxy, o serviço real é instanciado.
Para usar serviços preguiçosos, precisamos instalar o
symfony/proxy-manager-bridge
pacote. Vamos fazer isso em primeiro lugar.$composer require symfony/proxy-manager-bridgeA seguir, revisaremos o di_container.php exemplo para entender como usar o gerenciador de proxy para criar e injetar um serviço de proxy.
Vá em frente e substitua o código do di_container.php arquivo com o seguinte conteúdo.
get('demo.service'); // fill your object with values here return true; // confirm that initialization occurred correctly }; $proxy = $factory->createProxy('ServicesDemoService', $initializer); // code for creating proxy ends global $containerBuilder; $containerBuilder = new ContainerBuilder(); $containerBuilder->register('demo.service', 'ServicesDemoService'); $containerBuilder->register('dependent.service', 'ServicesDependentService')->addArgument($proxy); $dependentService = $containerBuilder->get('dependent.service'); echo $dependentService->helloWorld();Em primeiro lugar, criamos uma instância do
LazyLoadingValueHolderFactory
classe. A seguir, usamos ocreateProxy
método desta classe para definir como a instância doServicesDemoService
classe será criada. O segundo argumento docreateProxy
é uma função anônima, que será chamada quando a instância real da classe precisar ser instanciada em vez de um objeto proxy. E assim, a função anônima lida com a lógica de criar uma instância real.Tirando isso, é praticamente a mesma coisa. Depois que a instância de proxy for criada, vamos passá-la no
addArgument
método, em vez de criar uma instância com oReference
classe.O benefício dessa abordagem é que sempre que criamos uma instância do
ServicesDependentService
classe, ele não criará o objeto real daServicesDemoService
class, em vez disso, ele criará um objeto proxy. O objeto real daServicesDemoService
classe será criada somente quando você chamar qualquer método dela.Para confirmar, você pode simplesmente despejar o
$dependentService
objeto, e você deve ver algo assim.ServicesDependentService Object ( [demo_service:ServicesDependentService:private] => ProxyManagerGeneratedProxy__PM__ServicesDemoServiceGeneratedccdf8241e892da36c12500ed72d82829 Object ( [valueHoldera1fd8:ProxyManagerGeneratedProxy__PM__ServicesDemoServiceGeneratedccdf8241e892da36c12500ed72d82829:private] => [initializere3996:ProxyManagerGeneratedProxy__PM__ServicesDemoServiceGeneratedccdf8241e892da36c12500ed72d82829:private] => Closure Object ( [parameter] => Array ( [&$wrappedObject] =>[$proxy] => [$method] => [$parameters] => [&$initializer] => ) ) ) ) Como você pode ver, ele criou o objeto proxy em vez do real
ServicesDemoService
objeto.Se você não tivesse usado o método proxy, a saída teria sido assim.
ServicesDependentService Object ( [demo_service:ServicesDependentService:private] => ServicesDemoService Object ( ) )Como você pode ver, ele criou uma instância do
ServicesDemoService
classe!Então é assim que funciona o carregamento preguiçoso de serviços!
Conclusão
O componente Symfony DependencyInjection foi o centro das atenções neste tutorial. Vimos como instalar e configurar o DependencyInjection, bem como alguns exemplos do mundo real de como ele pode ser usado.
Estou realmente fascinado e animado com os componentes desacoplados do framework Symfony que você pode simplesmente escolher para sua aplicação. Conecte-os ao seu código e eles simplesmente funcionam! Em suma, só posso ver os benefícios dessa abordagem de estrutura para nossa comunidade PHP!