首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将类传递给回调函数的最佳方法

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

Stack Overflow用户
提问于 2015-02-04 04:33:59
回答 1查看 463关注 0票数 1

我使用的是PSR-3日志类,我试图将它与set_error_handler()结合使用。我的问题是如何正确地“抓取”日志对象?

简单的例子:

我的ErrorHandler.php

代码语言:javascript
复制
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

代码语言:javascript
复制
class Logger extends PsrLogAbstractLogger implements PsrLogLoggerInterface { 
    public function log($level, $message, array $context = array()) { 
        // Do stuff
    }
}

请注意,Logger可能启动,也可能没有启动,其想法是您可以轻松地定义另一个Logger。

我突然意识到,我至少有两个选项,简单地使用一个名为$logger或类似的全局变量,并使用它(即使在我的特定示例中的全局范围内不会初始化Logger对象),或者使用“仅这一次”的单例模式,其中我将在Logger类中定义一个静态方法,这样我就可以使用如下内容:

代码语言:javascript
复制
$logger = Logger::getInstance();

虽然我见过很多关于Singleton模式的非常苛刻的东西,有些人甚至称之为“反模式”。我正在对项目的其余部分使用依赖注入(尽可能好)。

我是错过了另一种选择,还是有一种“正确”的方法来做到这一点?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-02-04 08:33:57

通过在这里使用单例,您将隐藏Logger的依赖项。这里不需要全局访问点,而且由于您已经在尝试坚持DI,所以您可能不想把代码弄得乱七八糟,使其无法测试。

事实上,有更清洁的方法来实现这一点。让我们仔细研究一下。

set_error_handler接受对象

您不需要向set_error_handler函数传递闭包或函数名。下面是docs的声明:

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

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

代码语言:javascript
复制
set_error_handler([$errorHandler, 'handle']);

其中$errorHandler是对象,handle是要调用的方法。

错误处理程序

ErrorHandler类将负责您的错误处理。我们通过使用类获得的好处是我们可以很容易地使用DI。

代码语言:javascript
复制
<?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。

代码语言:javascript
复制
<?php


interface Logger {

    public function log( $message );

}

class ConcreteLogger implements Logger {


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

}

传递的Logger实例将被分配给相应的属性。但是,如果没有传递任何内容,则分配VoidLogger的实例。它违背了DI的原则,但是在这种情况下,它是非常好的,因为我们使用了特定的模式。

空对象模式

您的标准之一是:

请注意,Logger可能启动,也可能没有启动,其想法是您可以轻松地定义另一个Logger。

当您需要一个没有行为但想要遵守契约的对象时,就会使用Null对象模式。

由于我们在log()中调用了Logger上的ErrorHandler方法,所以需要一个Logger实例(我们不能不调用任何方法)。但是没有人禁止我们创建一个具体的Logger实现,它什么也不做。这正是Null对象模式的特点。

代码语言:javascript
复制
<?php

class VoidLogger implements Logger {

    public function log( $message ){}

}

现在,如果您不希望启用日志记录,那么在实例化期间不要将任何内容传递给Error Handler,也不要自己传递一个VoidLogger

用法

代码语言:javascript
复制
<?php 

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

echo $notDefined;

要使用,只需稍微调整记录器上的类型提示和方法调用即可。但原则是一样的。

好处

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

  • 很容易将记录器替换为错误处理程序
  • 即使是简单的可互换错误处理程序
  • 松散耦合(将日志与处理错误解耦)
  • 容易扩展的错误处理程序(您可以注入其他内容,而不仅仅是记录器)
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28313565

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档