Лучший способ передать класс в функцию обратного вызова

Я использую класс журналирования 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();

Хотя я видел много очень Суровые вещи говорили о паттерне Синглтона, некоторые даже называли его «Анти-паттерном». Я использую внедрение зависимостей (так хорошо, как я могу) для остальной части проекта.

Я пропускаю другой вариант или есть «правильный» способ сделать это?

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 (интерфейс) здесь и разрешить пропуск нуля.

<?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, вам просто нужно немного настроить подсказки типов и вызовы методов в регистраторе. Но принципы остаются прежними.

Выбирая этот тип реализации, вы получаете следующие преимущества:

  • Легко заменяемые регистраторы для обработчика ошибок
  • Даже легко заменяемые обработчики ошибок
  • Loose Couplding (отделенная регистрация от ошибки обработки)
  • Легко расширяемые обработчики ошибок (вы можете вводить другие вещи, а не только регистратор)
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]