1

PSR-3 ロギング クラスを使用しており、 と組み合わせて使用​​しようとしていset_error_handler()ます。私の質問は、ログ オブジェクトを適切に「取得」する にはどうすればよいですか?

簡単な例:

私のErrorHandler.php

set_error_handler(function ($errno, $errstr , $errfile , $errline , $errcontext) {
    // This error code is not included in error_reporting
    if (!(error_reporting() & $errno)) {
        return;
    }

    $logger->log(/* How? */);

});

私のLogger.php

class Logger extends PsrLogAbstractLogger implements PsrLogLoggerInterface { 
    public function log($level, $message, array $context = array()) { 
        // Do stuff
    }
}

ロガーは開始される場合と開始されない場合があることに注意してください。アイデアは、何らかの方法で別のロガーを簡単に定義できるようにすることです。

少なくとも 2 つのオプションがあることに気付きました。これは、単純に呼び出されたグローバル変数$loggerまたは同様のものを使用し、それを使用することです (Logger私の特定の例では、オブジェクトはグローバル スコープで初期化されませんが)。 「今回だけ」のシングルトン パターン。Loggerクラス内で静的メソッドを定義して、次のようなものを使用できるようにします。

$logger = Logger::getInstance();

私はシングルトン パターンについて非常に厳しいことを言っているのをたくさん見てきましたが、それを「アンチパターン」と呼んでいる人さえいます。プロジェクトの残りの部分では、依存性注入を (できる限りうまく) 使用しています。

別のオプションがありませんか、それともこれを行う「正しい」方法はありますか?

4

1 に答える 1

1

ここでシングルトンを使用すると、Logger の依存関係が隠されます。ここではグローバルなアクセス ポイントは必要ありません。また、すでに DI に準拠しようとしているので、おそらくコードを散らかしてテスト不能にしたくないでしょう。

実際、それを実装するためのよりクリーンな方法があります。それを見てみましょう。

set_error_handler はオブジェクトを受け入れます

関数にクロージャまたは関数名を渡す必要はありませんset_error_handler。ドキュメントの状態は次のとおりです。

次のシグネチャを持つコールバック。代わりに NULL を渡して、このハンドラーをデフォルトの状態にリセットすることができます。関数名の代わりに、オブジェクト参照とメソッド名を含む配列を指定することもできます。

これを知っていれば、専用のオブジェクトを使用してエラーを処理できます。オブジェクトのハンドラー メソッドは、次のように呼び出されます。set_error_handler

set_error_handler([$errorHandler, 'handle']);

where$errorHandlerは呼び出されるオブジェクトと handleメソッドです。

エラーハンドラ

クラスはErrorHandlerエラー処理を担当します。クラスを使用することで得られる利点は、DI を簡単に利用できることです。

<?php

interface ErrorHandler {

    public function handle( $errno, $errstr , $errfile = null , $errline = null , $errcontext = null );

}


class ConcreteErrorHandler implements ErrorHandler {

    protected $logger;

    public function __construct( Logger $logger = null )
    {
        $this->logger = $logger ?: new VoidLogger();
    }

    public function handle( $errno, $errstr , $errfile = null , $errline = null , $errcontext = null )
    {
        echo "Triggered Error Handler";
        $this->logger->log('An error occured. Some Logging.');
    }

}

このhandle()方法については、これ以上説明する必要はありません。その署名は関数のニーズに準拠しておりset_error_handler()、コントラクトを定義することでそれを確認します。

ここで興味深いのはコンストラクタです。Loggerここでは (インターフェイス)をタイプヒントしており、null を渡すことができます。

<?php


interface Logger {

    public function log( $message );

}

class ConcreteLogger implements Logger {


    public function log( $message )
    {
        echo "Logging: " . $message;
    }

}

渡されたLoggerインスタンスは、対応するプロパティに割り当てられます。ただし、何も渡されない場合は、a のインスタンスVoidLoggerが割り当てられます。DI の原則に反しますが、特定のパターンを使用しているため、その場合はまったく問題ありません。

Null オブジェクト パターン

あなたの基準の1つは次のとおりでした。

ロガーは開始される場合と開始されない場合があることに注意してください。アイデアは、何らかの方法で別のロガーを簡単に定義できるようにすることです。

Null オブジェクト パターンは、動作のないオブジェクトが必要であるが、コントラクトに従いたい場合に使用されます。

log()ErrorHandler で Loggerのメソッドを呼び出すため、インスタンスが必要です(何もないメソッドを呼び出すことはできません)。Loggerしかし、何もしない Logger の具体的な実装を作成することを禁止する人は誰もいません。Null Object パターンはまさにそれです。

<?php

class VoidLogger implements Logger {

    public function log( $message ){}

}

ここで、ロギングを有効にしたくない場合は、インスタンス化中にエラー ハンドラに何も渡さないか、VoidLogger自分で a を渡します。

使用法

<?php 

$errorHandler = new ConcreteErrorHandler(); // Or Pass a Concrete Logger instead
set_error_handler([$errorHandler, 'handle']);

echo $notDefined;

PSR ロガーを使用するには、型ヒントとロガーのメソッド呼び出しを少し調整する必要があります。しかし、原則は変わりません。

利点

このタイプの実装を選択すると、次の利点が得られます。

  • エラー ハンドラ用の簡単に交換可能なロガー
  • 簡単に交換可能なエラー ハンドラも
  • 疎結合 (処理エラーからログを切り離す)
  • 簡単に拡張可能なエラー ハンドラー (ロガーだけでなく、他のものを注入できます)
于 2015-02-04T08:33:57.433 に答える