将类传递给回调函数的最佳方法

AB卡罗尔

我正在使用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
    }
}

请注意,记录器可能会启动也可能不会启动,其想法是使用户能够以某种方式轻松定义另一个记录器。

在我看来,我至少有两个选择,可以简单地使用称为$logger或类似名称的全局变量,并使用它(即使Logger在我的特定示例中不会在全局范围内初始化对象),也可以使用单例模式“仅此一次”,我将在Logger内部定义一个静态方法,以便可以使用诸如:

$logger = Logger::getInstance();

虽然我已经看到了很多非常恶劣的事情说一下Singleton模式,有的甚至称这是“反模式”。我正在为项目的其余部分使用依赖项注入(尽我所能)。

我是否缺少其他选择,还是有“正确”的方法来做到这一点?

pl

通过在此处使用单例,您将隐藏Logger的依赖项。您在这里不需要全局访问点,并且由于您已经在尝试遵循DI,因此您可能不想使代码混乱并使之不可测试。

确实,有更清洁的方法可以实现这一目标。让我们经历一下。

set_error_handler接受对象

您无需将闭包或函数名称传递给该set_error_handler函数。这是文档说明的内容:

具有以下签名的回调。可以改为传递NULL,以将此处理程序重置为其默认状态。除了函数名称,还可以提供包含对象引用和方法名称的数组。

知道了这一点,您可以使用专用对象来处理错误。对象上的处理程序方法将像这样被调用set_error_handler

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

$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实例将被分配给相应的属性。但是,如果未传递任何内容,VoidLogger则会分配a的实例它违反了DI的原理,但是在这种情况下完全可以,因为我们使用了特定的模式。

空对象模式

您的条件之一是:

请注意,记录器可能会启动也可能不会启动,其想法是使用户能够以某种方式轻松定义另一个记录器。

当您需要没有任何行为但想要遵守合同的对象时,可以使用“空对象模式”。

由于我们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记录器,您只需要稍微调整一下记录器上的类型提示和方法调用即可。但是原理保持不变。

好处

通过选择这种类型的实现,您可以获得以下好处:

  • 轻松替换错误处理程序的记录器
  • 甚至简单的可交换错误处理程序
  • 松散耦合(将日志记录与处理错误分离)
  • 易于扩展的错误处理程序(您可以注入其他内容,而不仅仅是记录器)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章