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

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

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)