Bağımlılık Otomatik Enjeksiyonu

Webman'da bağımlılık otomatik enjeksiyonu isteğe bağlı bir özelliktir ve varsayılan olarak kapalıdır. Bağımlılık otomatik enjeksiyonuna ihtiyacınız varsa, php-di kullanmanızı öneririz. Aşağıda, Webman ile php-di'nin nasıl kullanılacağına dair bilgiler bulunmaktadır.

Kurulum

composer require psr/container ^1.1.1 php-di/php-di ^6.3 doctrine/annotations ^1.14

config/container.php dosyasını düzenleyin. Son hali şöyle olmalıdır:

$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAnnotations(true);
return $builder->build();

config/container.php dosyası sonunda PSR-11 standardına uygun bir konteyner örneği döndürmelidir. Eğer php-di kullanmak istemiyorsanız, burada başka bir PSR-11 standardına uygun konteyner örneği oluşturup döndürebilirsiniz.

Yapılandırıcı Enjeksiyonu

app/service/Mailer.php dosyasını oluşturun (dizini yoksa kendiniz oluşturun) ve içerik şöyle olsun:

<?php
namespace app\service;

class Mailer
{
    public function mail($email, $content)
    {
        // E-posta gönderme kodu atlandı
    }
}

app/controller/UserController.php dosyasının içeriği aşağıdaki gibidir:

<?php
namespace app\controller;

use support\Request;
use app\service\Mailer;

class UserController
{
    private $mailer;

    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    public function register(Request $request)
    {
        $this->mailer->mail('hello@webman.com', 'Merhaba ve hoş geldiniz!');
        return response('ok');
    }
}

Normal şartlarda, app\controller\UserController'ın örneğini oluşturmak için aşağıdaki koda ihtiyaç vardır:

$mailer = new Mailer;
$user = new UserController($mailer);

php-di kullanıldığında ise geliştiricilerin kontrolördeki Mailer'ı manuel olarak örneklemelerine gerek kalmaz, webman bunu otomatik olarak sizin için tamamlayacaktır. Mailer'ı örneklerken başka sınıfların bağımlılıkları varsa, webman bunları da otomatik olarak örnekleyip enjekte eder. Geliştiricilerin herhangi bir başlangıç işine ihtiyacı yoktur.

Dikkat
Bağımlılık otomatik enjeksyonu yalnızca çerçeve veya php-di tarafından oluşturulan örnekler ile tamamlanabilir, manuel olarak oluşturulan new örnekleri bağımlılık otomatik enjeksiyonunu tamamlayamaz; eğer enjeksyona ihtiyaç varsa, new ifadesini support\Container arayüzü ile değiştirmeniz gerekmektedir. Örneğin:

use app\service\UserService;
use app\service\LogService;
use support\Container;

// new anahtar kelimesiyle oluşturulan örnek bağımlılık enjekte edilemez
$user_service = new UserService;
// new anahtar kelimesiyle oluşturulan örnek bağımlılık enjekte edilemez
$log_service = new LogService($path, $name);

// Container ile oluşturulan örnekler bağımlılık enjekte edilebilir
$user_service = Container::get(UserService::class);
// Container ile oluşturulan örnekler bağımlılık enjekte edilebilir
$log_service = Container::make(LogService::class, [$path, $name]);

Notasyon Enjeksiyonu

Yapılandırıcı bağımlılık otomatik enjeksiyonuna ek olarak, notasyon enjeksiyonu da kullanabiliriz. Önceki örneği devam ettirerek, app\controller\UserController'ı aşağıdaki gibi değiştirelim:

<?php
namespace app\controller;

use support\Request;
use app\service\Mailer;
use DI\Annotation\Inject;

class UserController
{
    /**
     * @Inject
     * @var Mailer
     */
    private $mailer;

    public function register(Request $request)
    {
        $this->mailer->mail('hello@webman.com', 'Merhaba ve hoş geldiniz!');
        return response('ok');
    }
}

Bu örnek, @Inject notasyonu kullanarak enjekte edilir ve @var notasyonu ile nesne türü belirtilir. Bu örneğin etkisi, yapılandırıcı bağımlılık otomatik enjeksiyonu ile aynıdır, ancak kod daha özlüdür.

Dikkat
Webman 1.4.6 sürümünden önce kontrolör parametre enjeksiyonunu desteklememektedir; örneğin, aşağıdaki kod webman <= 1.4.6 iken desteklenmemektedir:

<?php
namespace app\controller;

use support\Request;
use app\service\Mailer;

class UserController
{
    // 1.4.6 sürümünden önce kontrolör parametre enjeksiyonu desteklenmez
    public function register(Request $request, Mailer $mailer)
    {
        $mailer->mail('hello@webman.com', 'Merhaba ve hoş geldiniz!');
        return response('ok');
    }
}

Özel Yapılandırıcı Enjeksiyonu

Bazen yapılandırıcıya geçen parametreler, nesne örneği değil de, string, sayı veya dizi gibi veriler olabilir. Örneğin, Mailer yapılandırıcısının smtp sunucu ip ve portunu geçmesi gerekir:

<?php
namespace app\service;

class Mailer
{
    private $smtpHost;

    private $smtpPort;

    public function __construct($smtp_host, $smtp_port)
    {
        $this->smtpHost = $smtp_host;
        $this->smtpPort = $smtp_port;
    }

    public function mail($email, $content)
    {
        // E-posta gönderme kodu atlandı
    }
}

Bu durumda, önceki yapılandırıcı otomatik enjeksiyonu doğrudan kullanamazsınız çünkü php-di $smtp_host ve $smtp_port değerlerinin ne olduğunu belirleyemez. Bu durumda özel enjeksiyonu denemek gerekir.

config/dependence.php dosyasına (dosya yoksa kendiniz oluşturun) aşağıdaki kodu ekleyin:

return [
    // ... Diğer yapılandırmalar atlandı

    app\service\Mailer::class =>  new app\service\Mailer('192.168.1.11', 25);
];

Böylece bağımlılık enjeksiyonu app\service\Mailer örneğini alırken bu yapılandırmada oluşturulan app\service\Mailer örneğini otomatik olarak kullanacaktır.

Görünüşe göre, config/dependence.php dosyasında new ile Mailer sınıfı örneklendirilmiştir. Bu örnek, bu durumda hiçbir sorun yaratmaz. Ancak, Mailer sınıfı başka sınıflara bağımlıysa veya Mailer sınıfı içinde notasyon enjeksiyonu kullanılıyorsa, new ile başlatmak bağımlılık otomatik enjekte etmeyecektir. Çözüm, özel arayüz enjeksiyonu kullanmak ve sınıfı başlatmak için Container::get(class_name) veya Container::make(class_name, [constructor_parameters]) metodunu kullanmaktır.

Özel Arayüz Enjeksiyonu

Gerçek projelerde, belirli sınıflar yerine arayüzlere yönelik programlama tercih ederiz. Örneğin, app\controller\UserController içinde app\service\MailerInterface'i içermelidir, app\service\Mailer yerine.

MailerInterface arayüzünü tanımlayın.

<?php
namespace app\service;

interface MailerInterface
{
    public function mail($email, $content);
}

MailerInterface arayüzünün uygulanmasını tanımlayın.

<?php
namespace app\service;

class Mailer implements MailerInterface
{
    private $smtpHost;

    private $smtpPort;

    public function __construct($smtp_host, $smtp_port)
    {
        $this->smtpHost = $smtp_host;
        $this->smtpPort = $smtp_port;
    }

    public function mail($email, $content)
    {
        // E-posta gönderme kodu atlandı
    }
}

Somut bir uygulama yerine MailerInterface arayüzünü içeri aktarın.

<?php
namespace app\controller;

use support\Request;
use app\service\MailerInterface;
use DI\Annotation\Inject;

class UserController
{
    /**
     * @Inject
     * @var MailerInterface
     */
    private $mailer;

    public function register(Request $request)
    {
        $this->mailer->mail('hello@webman.com', 'Merhaba ve hoş geldiniz!');
        return response('ok');
    }
}

config/dependence.php dosyasında MailerInterface arayüzü kullanılarak uygulanmasına şöyle tanım verin:

use Psr\Container\ContainerInterface;
return [
    app\service\MailerInterface::class => function(ContainerInterface $container) {
        return $container->make(app\service\Mailer::class, ['smtp_host' => '192.168.1.11', 'smtp_port' => 25]);
    }
];

Bu şekilde, iş ihtiyaçları MailerInterface arayüzünü kullandığında, otomatik olarak Mailer uygulamasını kullanacaktır.

Arayüze yönelik programlamanın faydası, bir bileşeni değiştirdiğimizde, iş kodunu değiştirmeden yalnızca config/dependence.php dosyasındaki somut uygulamayı değiştirerek işimizi halledebilmemizdir. Bu, birim testleri yaparken de oldukça faydalıdır.

Diğer Özel Enjeksiyonlar

config/dependence.php dosyası sadece sınıf bağımlılıklarını değil, aynı zamanda diğer değerleri de tanımlayabilir; örneğin, string, sayı, dizi gibi.

Örneğin, config/dependence.php dosyası şu şekilde tanımlanabilir:

return [
    'smtp_host' => '192.168.1.11',
    'smtp_port' => 25
];

Bu durumda, @Inject ile smtp_host ve smtp_port özelliklerine enjekte edebiliriz.

<?php
namespace app\service;

use DI\Annotation\Inject;

class Mailer
{
    /**
     * @Inject("smtp_host")
     */
    private $smtpHost;

    /**
     * @Inject("smtp_port")
     */
    private $smtpPort;

    public function mail($email, $content)
    {
        // E-posta gönderme kodu atlandı
        echo "{$this->smtpHost}:{$this->smtpPort}\n"; // 192.168.1.11:25 olarak çıktıyı verecektir
    }
}

Dikkat: @Inject("key") içindeki anahtar çift tırnak içinde olmalıdır.

Daha Fazla İçerik

Lütfen php-di belgesi için başvurun.