Перейти к содержанию

(PHP) 1.3 Оформление сообщений об ошибке. Класс «FormatAbstract»

Last updated on 14.06.2021

FormatAbstract

Наверное вы замечали, что при проблемах в работе системы управления сайтом появляются различные сообщения. Зачастую они выводятся одной строкой. Чтобы разобрать её содержание придётся потратить какое-то время. Было бы намного легче читать сообщения, где каждый блок информации отделён от другого. Поэтому в данной статье мы напишем класс (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» используется конструктор по умолчанию.

Опубликовано в рубрике1. Ошибки и ИсключенияЯдро CMS (PHP)