Я использую класс журналирования 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();
Хотя я видел много очень Суровые вещи говорили о паттерне Синглтона, некоторые даже называли его «Анти-паттерном». Я использую внедрение зависимостей (так хорошо, как я могу) для остальной части проекта.
Я пропускаю другой вариант или есть «правильный» способ сделать это?
Используя здесь синглтон, вы бы скрыли зависимость 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
(интерфейс) здесь и разрешить пропуск нуля.
<?phpinterface Logger {
public function log( $message );
}
class ConcreteLogger implements Logger {public function log( $message )
{
echo "Logging: " . $message;
}
}
Прошло Logger
Экземпляр будет присвоен соответствующему свойству. Однако, если ничего не передается, экземпляр VoidLogger
назначен. Это нарушает принцип DI, но в этом случае это совершенно нормально, потому что мы используем определенный шаблон.
Одним из ваших критериев было следующее:
Обратите внимание, что регистратор может быть инициирован или не инициирован, и идея заключается в том, что можно будет легко как-то определить другой регистратор.
Шаблон нулевого объекта используется, когда вам нужен объект без поведения, но вы хотите придерживаться контракта.
Так как мы называем log()
метод на Logger в нашем ErrorHandler, мы необходимость 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 Logger, вам просто нужно немного настроить подсказки типов и вызовы методов в регистраторе. Но принципы остаются прежними.
Выбирая этот тип реализации, вы получаете следующие преимущества:
Других решений пока нет …