[英]Best way to pass a class into a callback function
我正在使用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模式,有的甚至称这是“反模式”。 我正在为项目的其余部分使用依赖项注入(尽我所能)。
我是否缺少其他选择,还是有“正确”的方法来做到这一点?
通过在此处使用单例,您将隐藏Logger的依赖项。 您在这里不需要全局访问点,并且由于您已经在尝试遵守DI,因此您可能不想弄乱代码并使之不可测试。
确实,有更清洁的方法可以实现这一目标。 让我们经历一下。
您无需将闭包或函数名称传递给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()
函数的需求,我们通过定义合同来确保它的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
分配VoidLogger
的实例。 它违反了DI的原理,但是在这种情况下完全可以,因为我们使用了特定的模式。
您的条件之一是:
请注意,记录器可能会启动也可能不会启动,其想法是使用户能够以某种方式轻松定义另一个记录器。
当您需要一个没有行为但想要遵守合同的对象时,可以使用“空对象模式”。
由于我们在ErrorHandler的Logger上调用了log()
方法,因此我们需要一个Logger
实例(我们不能一无所获地调用方法)。 但是没有人禁止我们创建不执行任何操作的Logger的具体实现。 而这正是Null Object模式。
<?php
class VoidLogger implements Logger {
public function log( $message ){}
}
现在,如果您不想启用日志记录,请不要在实例化过程VoidLogger
任何内容传递给错误处理程序,也不要自己传递VoidLogger
。
<?php
$errorHandler = new ConcreteErrorHandler(); // Or Pass a Concrete Logger instead
set_error_handler([$errorHandler, 'handle']);
echo $notDefined;
要使用PSR记录器,您只需要稍微调整一下记录器上的类型提示和方法调用即可。 但是原理保持不变。
通过选择这种类型的实现,您可以获得以下好处:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.