O que é injeção de dependência em PHP e como usá-la

Uma metodologia popular e conhecida de desenvolvimento de software é chamada de injeção de dependência, que ajuda a facilitar o fluxo garantindo que seu software sempre tenha acesso às ferramentas de que precisa. Muitos tentam fazer essa metodologia parecer bastante complexa, mas realmente não é.

Vamos mergulhar no que é injeção de dependência, como funciona e como beneficiará o seu software.

O que é injeção de dependência?

Uma ótima analogia para injeção de dependência é um trabalhador com um kit de ferramentas que viaja junto com o software conforme ele é processado, garantindo que tudo flua sem problemas. O kit de ferramentas pode conter todos os tipos de coisas, incluindo variáveis, matrizes, objetos , fechamentos e qualquer outra coisa necessária para concluir a tarefa em mãos.

Quando o trabalhador inicia uma nova tarefa (ou seja, classe ou método), ele observará os requisitos necessários e, sem pensar, retirará as diferentes ferramentas necessárias para concluir o trabalho. Esta é a injeção de dependência em poucas palavras.

Você pode preencher seu kit de ferramentas com tudo o que precisar; em seguida, nas classes e métodos do software, especifique as ferramentas de que precisa e elas estarão automaticamente lá para você.

Instale o recipiente Apex

Existem muitas implementações diferentes, mas todas funcionam basicamente da mesma, e usaremos o Apex Container porque é simples e direto. Presume-se que você já tenha o PHP instalado e pode verificar se o Composer está ou não instalado com o comando:

 composer --version

Se você receber um erro "comando não encontrado", pode instalar o Composer com o seguinte comando:

 sudo curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer

Agora crie um diretório em branco e, dentro do diretório, execute os seguintes comandos:

 composer require apex/container
composer require twig/twig

Isso fará o download do Apex Container e do popular mecanismo de modelo Twig, que será usado nos exemplos abaixo. Ambos podem ser encontrados no / vendor / subdiretório.

Injetar suas ferramentas

Vamos criar uma classe rápida chamada Car com o seguinte código:

 
<?php
use TwigLoaderArrayLoader;
class Car
{
public function __construct(
public string $model,
public string $color,
public ArrayLoader $db
) {
echo "I'm a $color $model and have a " . $db::class . "
";
}
}

Esta é uma classe simples com duas propriedades, a marca e a cor de um carro, e carrega a classe 'ArrayLoader' do Twig. Salve-o como car.php e prepare-se para colocar a magia da injeção de dependência em uso. Abra outro arquivo em branco e adicione o seguinte código:

 
<?php
use ApexContainerContainer;
// Load Composer packages, and the car.php file
require_once(__DIR__ . '/vendor/autoload.php');
require_once(__DIR__ . '/car.php');
// Create container, and add a couple tools
$cntr = new Container(use_attributes: true);
$cntr->set('model', 'Jaguar');
$cntr->set('color', 'silver');
// Create our car object
$car = $cntr->make('Car');
$car2 = $cntr->make('car', ['color' => 'red']);

Salve e execute este código no terminal, e os resultados serão:

 I'm a silver Jaguar and have a TwigLoaderArrayLoader
I'm a red Jaguar and have a TwigLoaderArrayLoader

No código acima, você instanciou o contêiner (ou seja, kit de ferramentas) e adicionou algumas ferramentas, a cor e a marca de um carro. Em vez de criar o objeto carro com o novo Car normal (); , ele foi criado por meio do método make () do contêiner. Isso passou pela classe primeiro para verificar os requisitos, depois olhou quais itens (ou seja, ferramentas) que tínhamos disponíveis e os injetou na classe antes de devolvê-los.

Você notará a declaração de uso no topo do arquivo para o ArrayLoader, e o terceiro argumento no construtor também pede um ArrayLoader . Quando o contêiner examinou os requisitos da classe, percebeu esses dois aspectos, criou automaticamente uma instância de ArrayLoader `e a injetou no construtor. Isso é chamado de fiação automática.

Estendendo com injeção de atributo

Indo um passo adiante, em vez de apenas injetar no construtor, também podemos injetar diretamente nas propriedades por meio de atributos. Modifique o arquivo do carro e altere-o para:

 
<?php
use TwigLoaderArrayLoader;
use ApexContainerContainer;
class Car
{
#[Inject(Container::class)]
public Container $cntr;
public function __construct(
public string $model,
public string $color,
public ArrayLoader $db
) {
echo "I'm a $color $model and have a " . $db::class . "
";
}
function getCost()
{
echo "Class is " . $this->cntr::class . "
";
}
}

As únicas modificações foram que uma nova declaração de uso foi adicionada, a propriedade com o atributo da classe Container foi adicionada e nós adicionamos uma nova função getCost () . Na parte inferior do código de teste que você executou anteriormente, adicione a linha:

 $car->getCost();

Agora execute o código novamente e os resultados serão:

 I'm a silver Jaguar and have a TwigLoaderArrayLoader
I'm a red Jaguar and have a TwigLoaderArrayLoader
Class is ApexContainerContainer

Desta vez, quando a classe car.php foi carregada, o contêiner também olhou suas propriedades, percebeu o atributo Inject que chamava a classe do contêiner e injetou uma instância dela. Às vezes, é preferível injetar via atributos dessa maneira, pois ajuda a manter as coisas mais limpas e legíveis.

Obtenha suas próprias ferramentas

Em vez de sempre injetar itens, o que acontece se você quiser recuperar um item do recipiente? Isso pode ser feito facilmente com o método get () do contêiner. No arquivo car.php , modifique a função getCost () adicionada anteriormente com:

 
function getCost()
{
$price = $this->cntr->get('car_price');
echo "The price is $price
";
}

Agora, dentro do código de teste que você está executando, em qualquer lugar antes da linha que chama getCost (), adicione uma linha como:

 $cntr->set('car_price', 24995);

Agora execute o código e os resultados serão:

 I'm a silver Jaguar and have a TwigLoaderArrayLoader
I'm a red Jaguar and have a TwigLoaderArrayLoader
Price is 24999

Você não precisa necessariamente injetar seus itens e sempre pode facilmente pegar o que precisa com o método get () conforme mostrado acima.

Seguindo em frente com injeção de dependência

Agora você tem uma boa visão geral do que é exatamente injeção de dependência e como ela funciona. Novamente, o acima é apenas uma das muitas implementações, mas todas as implementações por aí funcionam da mesma maneira com os métodos get () / set () / make () .

Ainda há mais para injeção de dependência, como arquivo de definições e injeção de método. Se estiver interessado, consulte o manual do recipiente Apex ou outras implementações.