<?php

/*
 * DIコンテナの構成
 * 依存関係を集中管理し、呼び出し側の処理をすっきりさせる
 */

/**************************************************/

// メール送信インターフェイス
interface MailerInterface
{
    public function 
send($body);
}

// ログ記録インターフェイス
interface LoggerInterface
{
    public function 
record($body);
}

/**************************************************/

// Sendmailでメール送信
class SendmailMailer implements MailerInterface
{
    public function 
send($body)
    {
        echo 
'Sendmailで送信しました:' $body "\n";
    }
}

// SMTPでメール送信
class SmtpMailer implements MailerInterface
{
    public function 
send($body)
    {
        echo 
'SMTPで送信しました:' $body "\n";
    }
}

/**************************************************/

// ファイルでログ記録
class FileLogger implements LoggerInterface
{
    public function 
record($body)
    {
        echo 
'Fileで記録しました:' $body "\n";
    }
}

// データベースでログ記録
class DatabaseLogger implements LoggerInterface
{
    public function 
record($body)
    {
        echo 
'Databaseで記録しました:' $body "\n";
    }
}

/**************************************************/

// ニュースレター送信
class NewsletterTransfer
{
    protected 
$mailer$logger;

    public function 
__construct(MailerInterface $mailerLoggerInterface $logger)
    {
        
$this->mailer $mailer;
        
$this->logger $logger;
    }

    public function 
send($message)
    {
        
$this->mailer->send($message);
        
$this->logger->record(date('Y-m-d H:i:s') . ':送信完了');
    }
}

/**************************************************/

// DIコンテナ
class Container
{
    function 
createMailer()
    {
        return new 
SendmailMailer;
    }

    function 
createLogger()
    {
        return new 
FileLogger;
    }

    function 
createNewsletterTransfer()
    {
        return new 
NewsletterTransfer($this->createMailer(), $this->createLogger());
    }
}

/**************************************************/

// 送信処理
$container = new Container;
$newsletterTransfer $container->createNewsletterTransfer();
$newsletterTransfer->send('今月のピックアップニュース');

/**************************************************/

/*
■コンテナの命名規則(自分の理解での補足)

クラスを生成して返すのでメソッドに「create」と付けているが、
シングルトンにするなら「singleton」、インスタンスなら「instance」にすると良さそう

■Facadeとの比較(自分の理解での補足)

単純に以下のような共通メソッドを定義して、
Facadeとして集中管理するという手段もある

class SenderManager
{
    public static sendNewsletter($message)
    {
        $newsletter = new NewsletterTransfer(new SendmailMailer(), new FileLogger());
        $newsletter->send($message);
    }
}

が、DIコンテナの方が「メールの送信はこのクラス」「ログの記録はこのクラス」の管理が明確になる
Containerに例えば以下のようなメソッドを追加した場合でも、
メールとログのクラスはコンテナで作成されており、差し替えがしやすい

    function createOrderletterTransfer()
    {
        return new OrderletterTransfer($this->createMailer(), $this->createLogger());
    }

■コンテナを多用しすぎる弊害(自分の理解での補足)

何でもかんでもコンテナで管理すると、コンテナの情報量が多くなりすぎてかえって収集がつかなくなる
また実装クラスが隠蔽されるため、「本来の処理はどこにあるの?」となって見通しが悪くなる
コンテナでの管理は疎結合が必要とされる処理に限定する。そのような処理はアプリケーション内でごく一部となるはず
疎結合が必要とされない箇所はFacadeで処理の窓口を作るくらいでいい
使用フレームワークの処理を参考にしてルールを決めるなどするといい

■参考ページ

DIとは?DIコンテナとは?試してみた(前編)[PHP][DI] - あざらし備忘録。
http://shiro-goma.hatenablog.com/entry/2014/06/22/102236

Dependency Injectionを特定のDIコンテナに頼らず実現する - Qiita
http://qiita.com/Hiraku/items/48fbdfca4b63c74494e9

DIコンテナの本当の使いどころ | 技術トピックス | ウルシステムズ株式会社
https://www.ulsystems.co.jp/topics/025
*/