Продвинутое использование компонента VarDumper

Дата обновления перевода 2021-12-25

Продвинутое использование компонента VarDumper

Функция dump() - это просто тонкая обёртка и более удобный способ вызова VarDumper::dump(). Вы можете изменить поведение этой функции, вызвав VarDumper::setHandler($callable). Вызовы к dump() будут потом перенаправлены к $callable.

Добавив обработчик, вы можете настроить Cloner, Dumper и Caster, как объясняется ниже. Простая реализация функции обработчика может выглядеть таким образом:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\VarDumper\VarDumper;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;

VarDumper::setHandler(function ($var) {
    $cloner = new VarCloner();
    $dumper = 'cli' === PHP_SAPI ? new CliDumper() : new HtmlDumper();

    $dumper->dump($cloner->cloneVar($var));
});

Cloner

Клонировщик используется для создания среднего представления любой переменной PHP. Его вывод - это объект Data, который окружает это представление.

Вы можете создать объект Data таким образом:

1
2
3
4
5
6
7
use Symfony\Component\VarDumper\Cloner\VarCloner;

$cloner = new VarCloner();
$data = $cloner->cloneVar($myVar);
// это часто потом передаётся дамперу
// см. пример наверху этой страницы
// $dumper->dump($data);

Независимо от структуры клонированных данных, результирующие объекты Data всегда поддаются сериализации.

Клонировщик применяет ограничения при создании представления, так что один может представляь только поднабор клонированной переменной. До вызова cloneVar(), вы можете сконфигурировать эти ограничения:

setMaxItems()
Конфигурирует максимальное количество объектов, которые будут клонированы после минимальной глубины вложения. Объекты считаются, используя алогритм по уровням, так что объекты низшего уровня будут иметь более высокий приоритет, чем глубоко вложенные объекты; Указав -1, вы удалите ограничение.
setMinDepth()
Конфигурирует минимальную глубину дерева, где мы гарантированно клонируем все объекты. После того, как достигнута эта глубина, только объекты setMaxItems будут клонированы. Значение по умолчанию - 1, что не вступает в конфликт с более старыми версиями Symfony.
setMaxString()
Конфигурирует максимальное количество символов, которое будет клонировано перед обрезкой слишком длинных строк. Указав -1, вы удалите ограничение.

До сброса, вы можете ещё больше ограничить результирующий объект Data, используя следующие методы:

withMaxDepth()
Ограничивается сбросы в измерении глубины.
withMaxItemsPerDepth()
Ограничивает количество объектов на уровень глубины.
withRefHandles()
Удаляет обработки внутренних объектов для более просторного вывода (полезно для тестов).
seek()
Выбирает только подчасти уже клонированных массивов, объектов или источников.

В отличие от предыдущих ограничений в клонировщиках, которые специально удаляют данные, эти могут меняться до сброса, так как они не влияют на промежуточное представление внутренне.

Note

Когда не применяется никакое ограничение, объект Data настолько же точен, насколько родная функция serialize, и следовательно может быть использован вне отладки.

Dumper

Дампер ответственный за вывод представления строки переменной PHP, используя объект Data в качестве ввода. Назначение и форматирование этого вывода отличается в различных дамперах.

Этот компонент поставляется с HtmlDumper для вывода HTML, и CliDumper для опционально маркированного цветом вывода командной строки.

Например, если вы хотите сбросить некоторую $variable, просто выполните:

1
2
3
4
5
6
7
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;

$cloner = new VarCloner();
$dumper = new CliDumper();

$dumper->dump($cloner->cloneVar($variable));

Используя первый аргумент конструктора, вы можете выбрать поток вывода, куда будет записан сброс. По умолчанию, CliDumper пишет в php://stdout, а HtmlDumper - в php://output. Но любой PHP потом (источник или URL) приемлем.

Вместо места назначения потока, вы можете также передать ему callable, которое будет вызвано повторно для каждой строчки, сгенерированной дампером. Это вызываемое может быть сконфигурировано, используя первый аргумент конструктора дампера, а также используя метод setOutput() или второй аргумент метода dump().

Например, чтобы получить сброс в виде строки в переменной, вы можете:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;

$cloner = new VarCloner();
$dumper = new CliDumper();
$output = '';

$dumper->dump(
    $cloner->cloneVar($variable),
    function ($line, $depth) use (&$output) {
        // Негативная глубина означает "конец сброса"
        if ($depth >= 0) {
            // Добавляет два пробела отступа к строчке
            $output .= str_repeat('  ', $depth).$line."\n";
        }
    }
);

// $output теперь наполняется представлением сброса $variable

Другой опцией для того, чтобы сделать то же самое, может быть:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;

$cloner = new VarCloner();
$dumper = new CliDumper();
$output = fopen('php://memory', 'r+b');

$dumper->dump($cloner->cloneVar($variable), $output);
$output = stream_get_contents($output, -1, 0);

// $output теперь наполняется представлением сброса $variable

Tip

Вы можете передать true второму аргументу метода dump(), чтобы заставить его возвращать сброс в виде строки:

1
$output = $dumper->dump($cloner->cloneVar($variable), true);

Дамперы реализуют интерфейс DataDumperInterface, который указывает метод dump(Data $data). Они также обычно реализуют DumperInterface, который освобождает их от повторной реализации логики, необходимой для прохождения внутренней структуры объекта Data.

HtmlDumper использует темную тему по умолчанию. Используйте метод setTheme(), чтобы установить светлую тему:

1
2
// ...
$htmlDumper->setTheme('light');

HtmlDumper ограничивает длину строки и глубину вложения вывода, чтобы сделать его более читаемым. Эти опции можно переопределить третьим необязательным параметром метода dump(Data $data):

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\VarDumper\Dumper\HtmlDumper;

$output = fopen('php://memory', 'r+b');

$dumper = new HtmlDumper();
$dumper->dump($var, $output, array(
    // 1 и 160 являются значениями по умолчанию для этих опций
    'maxDepth' => 1,
    'maxStringLength' => 160,
));

Формат вывода дампера может быть тонко настроен двумя флажками: DUMP_STRING_LENGTH и DUMP_LIGHT_ARRAY, которые передаются в виде битовой карты третьему аргументу конструктора. Они также могут быть установлены через переменные окружения при использовании assertDumpEquals($dump, $data, $filter, $message) во время модульного тестирования.

Аргумент $filter assertDumpEquals() может быть использован для передачи части констант поля Caster::EXCLUDE_* и влияет на ожидаемый вывод, созданный разными кастерами.

Если установлен DUMP_STRING_LENGTH, то длина строки отображается радом с её содержанием:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\AbstractDumper;
use Symfony\Component\VarDumper\Dumper\CliDumper;

$varCloner = new VarCloner();
$var = ['test'];

$dumper = new CliDumper();
echo $dumper->dump($varCloner->cloneVar($var), true);

// array:1 [
//   0 => "test"
// ]

$dumper = new CliDumper(null, null, AbstractDumper::DUMP_STRING_LENGTH);
echo $dumper->dump($varCloner->cloneVar($var), true);

// (добавлена длина строки перед строкой)
// array:1 [
//   0 => (4) "test"
// ]

Если установлен DUMP_LIGHT_ARRAY, то массивы сбрасываются в сокращённом формате, схожим с краткой нотацией массива в PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\AbstractDumper;
use Symfony\Component\VarDumper\Dumper\CliDumper;

$varCloner = new VarCloner();
$var = ['test'];

$dumper = new CliDumper();
echo $dumper->dump($varCloner->cloneVar($var), true);

// array:1 [
//   0 => "test"
// ]

$dumper = new CliDumper(null, null, AbstractDumper::DUMP_LIGHT_ARRAY);
echo $dumper->dump($varCloner->cloneVar($var), true);

// (no more array:1 prefix)
// [
//   0 => "test"
// ]

Если вы хотите использовать обе опции, то вы можете просто объедиить их, используя логический оператор OR |:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\AbstractDumper;
use Symfony\Component\VarDumper\Dumper\CliDumper;

$varCloner = new VarCloner();
$var = ['test'];

$dumper = new CliDumper(null, null, AbstractDumper::DUMP_STRING_LENGTH | AbstractDumper::DUMP_LIGHT_ARRAY);
echo $dumper->dump($varCloner->cloneVar($var), true);

// [
//   0 => (4) "test"
// ]

Caster

Объекты и источники, вложенные в PHP переменную "приводятся" в массивы в промежуточном представлении Data. Вы можете настроить представление массива для каждго объекта / источника, подсоединив Caster к этому процессу. Компонент уже включает в себя caster для базовых PHP классов и других общих классов.

Если вы хотите построить собственный Caster, то вы можете зарегистрировать его перед клонированием переменной PHP. Caster регистрируется используя либо конструктор Cloner'а, либо его метод addCasters():

1
2
3
4
5
6
7
8
use Symfony\Component\VarDumper\Cloner\VarCloner;

$myCasters = array(...);
$cloner = new VarCloner($myCasters);

// или

$cloner->addCasters($myCasters);

Предоставленный аргумент $myCasters является массивом, соединяющим класс, интерфейс или тип источника с вызываемым:

1
2
3
4
$myCasters = array(
    'FooClass' => $myFooClassCallableCaster,
    ':bar resource' => $myBarResourceCallableCaster,
);

Как вы можете заметить, типы источников имеют префиксы :, чтобы избежать коллизий с именами классов.

Так как объект имеет один основной класс и потенциально множество родительских классов или интерфейсов, многие caster могут быть применены к одному объекту. В этом случае, caster вызываются один за одним, начиная с caster, привязаных к интерфейсам, классам родителей и потом к основному классу. Несколько caster также могут быть зарегистрированы для одного типа / класса / интерфейса источника. Они называются порядком регистрации.

Casterы отвечают за возвращение свойств объекта или источника, который клонируется в массив. Они являются вызываемыми, которые принимают 5 аргументов:

  • объект или источник, который приводится;
  • массив, смоделированный для объектов после нативного оператора-приведения PHP (array);
  • объект Stub, представляющий главные свойства объекта (класс, тип и т.д.);
  • true/false, когда вызывается вложенный или не вложенный в структуру caster;
  • Немного констант поля Caster ::EXCLUDE_*.

Вот простоой caster, который ничего не делает:

1
2
3
4
5
6
7
8
use Symfony\Component\VarDumper\Cloner\Stub;

function myCaster($object, $array, Stub $stub, $isNested, $filter)
{
    // ... наполнить/изменить $array в соответствии с вашими нуждами

    return $array;
}

Для объектов, параметр $array поставляется предварительно наполненный с использованием родного оператора приведения PHP (array) или возвратного значения $object->__debugInfo(), если существует волшебный метод. Далее, возвратное значение одного castera подаётся в виде аргумента массива следующему castery в цепочке.

При приведении опретаора (array), PHP добавляет к защищённым свойства префикс \0*\0, а к приватным - класс, владеющий свойством. Например, \0Foobar\0 будет префиксом для всех приватных свойств объектов типа Foobar. Casterы следуют этому соглашению и добавляют ещё два префикса: \0~\0 используется для виртуальных свойств, а \0+\0 - для динамических (свойства добавленного времени прогона не в объявлении класса).

Note

Несмотря на то, что вы можете это сделать, не рекомендуется изменять состояние объекта при проведении его в Caster.

Tip

До написанися собственных casterов, стоит рассмотреть уже существующие.

Добавление семантики с метаданными

Так как casterы прикреплены к конкретным классам или интерфейсам, они знают об объектах, которые они изменяют. Изменив объект $stub (третий аргумент любого casterа), можно передать эти знания результирующему объекту Data, то есть дамперам. Чтобы помочь вам сделать это (см. исходный код, чтобы понять, как это работает), компонент поставляется с набором оболочек для распространённой дополнительной семантики. Вы можете использовать:

  • ConstStub, чтобы обернуть значение, которое лучше всего представляется PHP константой;
  • ClassStub, чтобы обернуть идентификатор PHP (т.е. имя класса или метода, интерфейс и др.);
  • CutStub, чтобы заменить большие шумные объекты / строки / и т.д. эллипсами;
  • CutArrayStub. чтобы оставить только неоторые полезные ключи массива;
  • ImgStub, чтобы обернуть изображение;
  • EnumStub, чтобы обернуть набор виртуальных значений (т.е. значений, которые не существуют в виде свойств исходной структуры PHP данных, но стоят указания на ряду с настоящими);
  • LinkStub, чтобы обернуть строки, которые можно превратить в ссылки с помощью дамперов;
  • TraceStub и их
  • FrameStub и родственников
  • ArgsStub, чтобы обернуть протоколы PHP (используется ExceptionCaster).

Например, если вы знаете, что ваш объект Product имеет свойство brochure, которое содержит имя файла или URL, вы можете обернуть его в LinkStub, чтобы сообщить HtmlDumper сделать его нажимаемым:

1
2
3
4
5
6
7
8
9
use Symfony\Component\VarDumper\Caster\LinkStub;
use Symfony\Component\VarDumper\Cloner\Stub;

function ProductCaster(Product $object, $array, Stub $stub, $isNested, $filter = 0)
{
    $array['brochure'] = new LinkStub($array['brochure']);

    return $array;
}