我正在使用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
类中定义一个静态方法,以便我可以使用如下内容: / p>
$logger = Logger::getInstance();
虽然我已经看到很多关于Singleton模式的非常苛刻的事情,但有些甚至称它为“反模式”。我正在为项目的其余部分使用依赖注入(尽我所能)。
我错过了另一种选择,还是有“正确”的方式来做到这一点?
答案 0 :(得分:1)
通过在这里使用单例,您可以隐藏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()
函数的需求,我们通过定义合同来确保它。
这里有趣的部分是构造函数。我们在这里输入Logger
(接口)并允许传递空值。
<?php
interface Logger {
public function log( $message );
}
class ConcreteLogger implements Logger {
public function log( $message )
{
echo "Logging: " . $message;
}
}
传递的Logger
实例将被分配给相应的属性。但是,如果没有传递任何内容,则会分配VoidLogger
的实例。它违反了DI的原则,但在这种情况下完全没问题,因为我们使用了特定的模式。
您的一个标准如下:
请注意,Logger可能会也可能不会启动,而且我们的想法是能够以某种方式轻松定义另一个Logger。
当您需要一个没有行为但希望遵守合同的对象时,使用Null对象模式。
由于我们在ErrorHandler中调用Logger上的log()
方法,我们需要一个Logger
实例(我们无法在任何情况下调用方法)。但没有人禁止我们创建一个没有任何作用的Logger的具体实现。而且这正是Null Object模式的原因。
<?php
class VoidLogger implements Logger {
public function log( $message ){}
}
现在,如果您不想启用日志记录,请不要在实例化过程中将任何内容传递给错误处理程序,也不要自己传递VoidLogger
。
<?php
$errorHandler = new ConcreteErrorHandler(); // Or Pass a Concrete Logger instead
set_error_handler([$errorHandler, 'handle']);
echo $notDefined;
要使用PSR记录器,您只需稍微调整记录器上的类型提示和方法调用。但原则保持不变。
通过选择此类实施,您将获得以下好处: