
Наверное вы замечали, что при проблемах в работе системы управления сайтом появляются различные сообщения. Зачастую они выводятся одной строкой. Чтобы разобрать её содержание придётся потратить какое-то время. Было бы намного легче читать сообщения, где каждый блок информации отделён от другого. Поэтому в данной статье мы напишем класс (FormatAbstract), который будет отвечать за обработку получаемых сообщений и их оформление согласно используемым шаблонам.
Открываем корневую папку компонента «Log«. В директории «abstract» создаём папку «class». В ней создадим файл «FormatAbstract.php». Откроем его. Создаваемый класс является абстрактным. Кроме того он полностью реализует интерфейс «FormatInterface«.
Для начала укажем пространство имён (namespace) в котором будет находиться наш класс. Это «Hydra\Log\Abstracts». Далее необходимо импортировать три интерфейса, которые понадобятся классу FormatAbstract. Для этого используется оператор use
<?php namespace Hydra\Log\Abstracts; use DateTimeInterface; use Hydra\Log\Interfaces\FormatInterface; use JsonSerializable;
Теперь создаём абстрактный класс «FormatAbstract».
Класс «FormatAbstract»
Он реализует интерфейс «FormatInterface» используется оператор implements
. Что это означает? А то, что все методы, указанные в этом интерфейсе, необходимо либо полностью описать в этом классе, либо определить как абстрактные:
abstract class FormatAbstract implements FormatInterface { // полное описание метода public function имя_метода() { ...содержание тела функции... } // определяем метод как абстрактный abstract public function имя_метода(); }
В теле класса объявляем две переменные:
/** * %datetime% - дата и время * %code% - код ошибки * %level_name% - уровень ошибки * %message% - содержание ошибки * %extra% - дополнительные данные об ошибке */ protected string $template = '[%datetime%] %code% %level_name%: %message% | %extra% | Line: %line% | File: %file%'; protected string $dateFormat = 'Y-m-d\TH:i:sP';
Переменная $template — это место временного хранения нашего шаблона сообщения. В шаблоне используется ряд условных обозначений, которые заключены между знаками «% %». Они могут располагаться в любом порядке и в любой части шаблона. Всё зависит от разработчика. Шаблоны могут быть только текстовыми или же являться html блоками/страницами. В данном случае шаблон является текстовым и используется по умолчанию.
Переменная $dateFormat — это формат выводимой даты. Вы, как разработчик, можете указать любой интересующий вас формат.
Методы setTemplate() и getTemplate()
Первая пара методов «setTemplate()» и «getTemplate()«:
public function setTemplate($set): FormatAbstract { $this->template = $set; return $this; } public function getTemplate(): string { return $this->template; }
Первая функция сохраняет шаблон в переменную. Шаблон передаётся (если это необходимо) в пользование данному классу перед самым выводом. Вторая функция возвращает содержимое переменной $template.
Методы setDateFormat() и getDateFormat()
Следующая пара «setDateFormat()» и «getDateFormat()«:
public function setDateFormat($set): FormatAbstract { $this->dateFormat = $set; return $this; } public function getDateFormat(): string { return $this->dateFormat; }
Первая функция сохраняет формат даты в переменную. Вторая функция возвращает содержимое переменной $dateFormat.
Метод format()
Следующий метод «format()» класса «FormatAbstract» преобразует все передаваемые данные в строковый формат. Этот метод является рекурсивным.
public function format($record) { if (null === $record || is_scalar($record)) { if (is_float($record)) { if (is_infinite($record)) { return ($record > 0 ? '' : '-') . 'INF'; } if (is_nan($record)) { return 'NaN'; } } return strval($record); } if (is_array($record)) { $normalized = []; foreach ($record as $key => $value) { $normalized[$key] = $this->format($value); } return $normalized; } if ($record instanceof DateTimeInterface) { return $record->format($this->dateFormat); } if (is_object($record)) { $value = ''; if ($record instanceof JsonSerializable) { $value = $record->jsonSerialize(); } elseif (method_exists($record, '__toString')) { $value = $record->__toString(); } return [get_class($record) => $value]; } if (is_resource($record)) { return sprintf('[resource(%s)]', get_resource_type($record)); } return '[unknown(' . gettype($record) . ')]'; }
Данный метод работает с параметром «$record». Он может быть числом, массивом, объектом или ресурсом.
- Если параметр $record является числом (
if (null === $record || is_scalar($record))
), то оно будет возвращено в виде строки. - Если параметр $record — это объект (
if (is_object($record))
), тогда метод вернёт ассоциативный массив [имя класса => строковое представление объекта]. - Если параметр $record — это объект, который реализует интерфейс «DateTimeInterface» (
if ($record instanceof DateTimeInterface)
), тогда метод возвращает дату, отформатированную согласно переданному формату - Если параметр $record является ресурсом (
if (is_resource($record))
), тогда возвращается тип ресурса. - Если параметр $record является массивом (
if (is_array($record))
), тогда метод «format()» вызывает сам себя. То есть используется простая рекурсия.
В любом ином случае возвращается сообщение о том, что тип параметра $record не определён.
Метод build()
Метод «build()» отвечает за окончательное формирование строкового сообщения.
public function build(array $record)
{
$vars = $this->format($record);
$output = $this->template;
if (empty($vars['extra'])) {
unset($vars['extra']);
$output = str_replace('%extra%', '', $output);
} else {
foreach ($vars['extra'] as $var => $val) {
if (false !== strpos($output, '%extra.' . $var . '%')) {
$output = str_replace('%extra.' . $var . '%', strval($val), $output);
unset($vars['extra'][$var]);
}
}
}
foreach ($vars as $var => $val) {
if (false !== strpos($output, '%' . $var . '%')) {
$output = str_replace('%' . $var . '%', strval($val), $output);
}
}
// remove leftover %extra.xxx% if any
if (false !== strpos($output, '%')) {
$output = preg_replace('/%(?:extra)\..+?%/', '', $output);
}
return $output;
}
Этот метод получает, в качестве аргумента, массив данных. В этом массиве указываются следующие данные:
- Дата и время ( ключ
datetime
). В этом случае используется объект, реализующий интерфейс «DateTimeInterface» - Код ошибки / предупреждения ( ключ
code
). В виде числа - Уровень ошибки / предупреждения (ключ
level_name
). Тоже в виде числа - Само сообщение (ключ
message
) - Некоторые дополнительные данные (ключ
extra
)
Весь этот массив передаётся методу «format()». Этот метод представляет каждый элемент массива (в том числе элементы вложенных массивов), в строковом формате. Результат этого представления сохраняется в переменной «$vars» в виде ассоциативного массива. Далее мы обращаемся к шаблону нашей записи. Его копия сохраняется в переменной «$output».
Теперь мы перебираем массив «$vars». Каждый ключ этого массива, заключённый между символами «%»:
- %datetime%
- %code%
- %level_name%
- %message%
- %extra.любой ключ вложенного массива %
Используя функцию «str_replace()» все эти ключи заменяются на соответствующие им значения. На выходе получаем готовое сообщение, которое остаётся вывести на экран монитора.
Метод formatBatch()
Этот метод тоже представляет всё содержимое массива «$records» в строковом формате, но в качестве аргумента получает массив массивов.
public function formatBatch(array $records): array { foreach ($records as $key => $record) { $records[$key] = $this->format($record); } return $records; }
После преобразования возвращается тоже массив массивов, который можно использовать в дальнейшем.
На этом описание класса «FormatAbstract» окончено. Так как он является абстрактным, то нам потребуется создать класс-наследник. Им будет класс «LogFormat«. Файл с этим классом у нас создаётся в корневой папке компонента «Log».
<?php
namespace Hydra\Log;
use Hydra\Log\Abstracts\FormatAbstract;
class LogFormat extends FormatAbstract
{
public function __construct()
{
return $this;
}
}
Здесь мы только описываем конструктор класса. Если вам потребуется дополнительный функционал, то вы можете добавить в этот класс все необходимые методы.
Заметьте, что в конструкторе не написано выражение parent::__constructor()
. Это связано с тем, что в абстрактном классе «FormatAbstract» используется конструктор по умолчанию.