将 class 传递给回调函数的最佳方式
Best way to pass a class into a callback function
我正在使用 PSR-3 日志记录 class,并且我正尝试将其与 set_error_handler()
结合使用。我的问题是如何 正确地 "grab" 日志对象?
快速示例:
我的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
对象不会在我的特定示例中在全局范围内初始化),或者使用单例模式 "just this one time",我将在 Logger
class 中定义一个静态方法,以便我可以使用例如:
$logger = Logger::getInstance();
尽管我已经看到很多关于单例模式的非常 严厉的言论,有些人甚至称其为"Anti-Pattern"。我正在为项目的其余部分使用依赖注入(尽我所能)。
我是否遗漏了另一个选项,或者是否有 "Right" 方法来做到这一点?
通过在此处使用单例,您可以隐藏 Logger 的依赖性。您在这里不需要全局访问点,并且由于您已经在尝试遵守 DI,您可能不希望弄乱您的代码并使其无法测试。
确实有更简洁的方法来实现它。让我们来看看吧。
set_error_handler 接受对象
您不需要将闭包或函数名称传递给 set_error_handler
函数。以下是文档说明的内容:
A callback with the following signature. NULL may be passed instead, to reset this handler to its default state. Instead of a function name, an array containing an object reference and a method name can also be supplied.
了解这一点,您可以使用专用对象来处理错误。在 set_error_handler
中将像这样调用对象上的处理程序方法
set_error_handler([$errorHandler, 'handle']);
其中 $errorHandler
是对象,handle
是要调用的方法。
错误处理程序
ErrorHandler
class 将负责您的错误处理。我们使用 class 获得的好处是我们可以轻松地使用 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
的实例。它违反了 DI 的原则,但在这种情况下完全没问题,因为我们使用了特定的模式。
空对象模式
您的标准之一如下:
Note that the Logger may or may not be initiated, and the idea is that one would be able to easily define another Logger somehow.
当您需要一个没有行为的对象,但又想遵守约定时,可以使用空对象模式。
由于我们在 ErrorHandler 的 Logger 上调用了 log()
方法,我们 需要 一个 Logger
实例(我们不能调用任何东西的方法)。但是没有人禁止我们创建一个什么都不做的 Logger 的具体实现。而这正是空对象模式。
<?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 记录器,您只需稍微调整记录器上的类型提示和方法调用。但原则保持不变。
好处
通过选择这种实施方式,您可以获得以下好处:
- 错误处理程序的易于交换的记录器
- 甚至是易于交换的错误处理程序
- 松散耦合(将日志记录与处理错误分离)
- 易于扩展的错误处理程序(您可以注入其他东西,而不仅仅是记录器)
我正在使用 PSR-3 日志记录 class,并且我正尝试将其与 set_error_handler()
结合使用。我的问题是如何 正确地 "grab" 日志对象?
快速示例:
我的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
对象不会在我的特定示例中在全局范围内初始化),或者使用单例模式 "just this one time",我将在 Logger
class 中定义一个静态方法,以便我可以使用例如:
$logger = Logger::getInstance();
尽管我已经看到很多关于单例模式的非常 严厉的言论,有些人甚至称其为"Anti-Pattern"。我正在为项目的其余部分使用依赖注入(尽我所能)。
我是否遗漏了另一个选项,或者是否有 "Right" 方法来做到这一点?
通过在此处使用单例,您可以隐藏 Logger 的依赖性。您在这里不需要全局访问点,并且由于您已经在尝试遵守 DI,您可能不希望弄乱您的代码并使其无法测试。
确实有更简洁的方法来实现它。让我们来看看吧。
set_error_handler 接受对象
您不需要将闭包或函数名称传递给 set_error_handler
函数。以下是文档说明的内容:
A callback with the following signature. NULL may be passed instead, to reset this handler to its default state. Instead of a function name, an array containing an object reference and a method name can also be supplied.
了解这一点,您可以使用专用对象来处理错误。在 set_error_handler
set_error_handler([$errorHandler, 'handle']);
其中 $errorHandler
是对象,handle
是要调用的方法。
错误处理程序
ErrorHandler
class 将负责您的错误处理。我们使用 class 获得的好处是我们可以轻松地使用 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
的实例。它违反了 DI 的原则,但在这种情况下完全没问题,因为我们使用了特定的模式。
空对象模式
您的标准之一如下:
Note that the Logger may or may not be initiated, and the idea is that one would be able to easily define another Logger somehow.
当您需要一个没有行为的对象,但又想遵守约定时,可以使用空对象模式。
由于我们在 ErrorHandler 的 Logger 上调用了 log()
方法,我们 需要 一个 Logger
实例(我们不能调用任何东西的方法)。但是没有人禁止我们创建一个什么都不做的 Logger 的具体实现。而这正是空对象模式。
<?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 记录器,您只需稍微调整记录器上的类型提示和方法调用。但原则保持不变。
好处
通过选择这种实施方式,您可以获得以下好处:
- 错误处理程序的易于交换的记录器
- 甚至是易于交换的错误处理程序
- 松散耦合(将日志记录与处理错误分离)
- 易于扩展的错误处理程序(您可以注入其他东西,而不仅仅是记录器)