Иногда маленькие вещи случаются не потому, что ты их планировал, а потому что... ну а кто ещё это сделает? Я пару месяцев подряд писал небольшие ML-прототипы на PHP (да, знаю, иногда судьба заносит в странные места), и каждый раз сталкивался с одним и тем же раздражением: массивы печатаются так, будто пытаются скрыть правду.
Я смотрю на этот разъехавшийся print_r(), и думаю: а что, если я просто хочу увидеть аккуратную табличку? А если я хочу форматирование как у Python? А если я хочу имитировать пайторчевский tensor([...])?
Спойлер: я таки написал маленький хелпер. И чем больше я его использовал, тем больше понимал - возможно, он не только про удобство, но и про какое-то внутреннее право на ясность.
Этот хелпер - не "фреймворк", не "революция", не "супер-мегапринтер", просто очень маленькая утилитка, которая делает одну вещь и делает её хорошо.
И иногда именно такие инструменты оказываются самыми честными.
Откуда растут ноги
Мне всегда нравилась идея, что инструменты должны подстраиваться под человека, а не наоборот. Но в PHP (особенно когда ты работаешь с массивами, которые пытаешься трактовать как тензоры), тебя быстро настигает весёлая реальность: вывод данных ощущается увлекательное приключение с целью прочитать, понять и ещё раз простить понять.
Вот, например, типичная сцена. Ты печатаешь невинную матрицу:
print_r($matrix);и получаешь вот это:
Array
(
[0] => Array
(
[0] => 1
[1] => 23
[2] => 456
)
[1] => Array
(
[0] => 12
[1] => 3
[2] => 45
)
)Да, формально - всё отлично. Но попробуй глазами сравнить столбцы. Или понять, "ровный" ли результат модели. Или вообще отличить строку от столбца, когда отступы живут своей жизнью. А если это матрица с разными типами данных, например: стринги, числа и булевы значения? Или матрица 3-х мерная с 100 столбцами?
В какой-то момент я поймал себя на том, что трачу больше времени на мысленное выравнивание, чем на саму отладку. А это уже красный флаг.
Кому это может мешать, спросите вы? Ну, мне, например и или вот ещё:
ML-прототипистам на PHP - когда матрицы растут, а их пытаются охватить одним взглядом.
Разработчикам, проверяющие логи - когда в логах цифры скачут как хотят, а мозг тратит дополнительную энергию буквально на "распознавание текста".
Всем, кто работает с 2D/3D структурами -
print_r()иvar_dump()видят массив, а не тензор. Им всё равно. А вам - нет.
И вот тут у меня впервые возник вопрос: почему простая потребность - увидеть структуру в структурированном виде - звучит как запрос на чудо?
Тогда-то у меня и родилась мысль: а почему бы не сделать свой маленький callable-pretty-printer, который ведёт себя как Python-овский pprint, но для PHP?
Так появился PrettyPrint - компактный помощник для вывода массивов в человеческом виде. Без зависимостей, без магии. Просто немного структурированной любви.
composer require apphp/pretty-printКак это выглядит в работе
Немного примеров, потому что без примеров - это уже будет лекция, а не история.
Печать обычных значений
Тут всё тривиально
pprint('Hello', 123, 4.56, true);
// Hello 123 4.5600 TrueТаблички: аккуратно и по линейке
Это мне нравится больше всего
pprint([1, 23, 456], [12, 3, 45]);
// [[ 1, 23, 456],
// [12, 3, 45]]Лейбл + матрица
pprint('Confusion matrix:', [[1, 23], [456, 7]]);
// Confusion matrix:
// [[ 1, 23],
// [456, 7]]Псевдо-PyTorch стиль
pprint($matrix);
// tensor([
// [ 1, 2, 3, 4, 5],
// [ 6, 7, 8, 9, 10],
// [11, 12, 13, 14, 15]
// ])Нужен другой префикс? Пожалуйста!
pprint($matrix, label: 'arr');
// arr([
// [ 1, 2, 3, 4, 5],
// [ 6, 7, 8, 9, 10],
// [11, 12, 13, 14, 15]
// ])И, конечно же, свёртка больших массивов - голова/хвост, как у настоящих тензоров:
$matrix = [
[ 1, 2, 3, 4, 5, true],
[ 6, 7, 8, 9, 10, false],
[11, 12, 13, 14, 15, true],
[16, 17, 18, 19, 20, true],
[21, 22, 23, 24, 25, true],
];
pprint($matrix, headRows: 2, tailRows: 1, headCols: 2, tailCols: 3);
// tensor([
// [ 1, 2, ..., 4, 5, True],
// [ 6, 7, ..., 9, 10, False],
// ...,
// [21, 22, ..., 24, 25, True]
// ])А если вы с��всем смелый — вот трёхмерный тензор с усечением блоков:
$tensor3d = [
[[1, 2, 3],[4, 5, 6]],
[[7, 8, 9],[10, 11, 12]],
[[13, 14, 15],[16, 17, 18]],
];
pprint($tensor3d, headB: 1, tailB: 1, headRows: 1, tailRows: 1, headCols: 1, tailCols: 1);
// tensor([
// [[1, ..., 3],
// [4, ..., 6]],
//
// ...,
//
// [[13, ..., 15],
// [16, ..., 18]]
// ])Ну и всякие приятные мелочи:
start— префиксend— чем закончитьsep— чем разделять аргументыppd()— напечатать и умереть
Можно распечатать любые типы данных, в том числе на странице в браузере (не только к терминале). Но... настройки ограничены, чтобы не утонуть в возможностях.
А теперь немного мыслей вслух
Если честно, я не делал это ради GitHub-звёздочек. Это скорее попытка чуть-чуть навести порядок в маленьком кусочке вселенной. В индустрии такие вещи часто прох��дят мимо радаров - "слишком маленькое" или "слишком несущественное". А потом мы удивляемся, почему разработчики часами смотрят на неудобный вывод данных и ошибаются в самых простых местах.
Я однажды видел в продакшен-ML-пайплайне (тоже PHP, да) отличный пример аккуратности: команда сделала свой собственный минималистичный логгер для матриц, потому что они решили, что "небрежность - это источник багов". И что бы вы думали? Он реально поднимал качество дебага. Вот такой подход - редкость. И, по-моему, стоило бы обращать на такие мелочи внимание гораздо чаще.
Почему об этом вообще стоит задумываться?
Наверное, не ради самой библиотеки. Она маленькая, без претензий. Её можно написать за выходные. Но иногда инструмент, который внешне незначителен, меняет то, как ты смотришь на процесс разработки.
Мне эта штука в очередной раз напомнила о простом, почти бытовом наблюдении: если тебе что-то мешает - почини это. Даже если кажется, что это "слишком мелко".
Потому что накапливаются мелочи, а не катастрофы.
Потому что ясность стоит дороже усилий.
Потому что маленькие инструменты иногда делают больше, чем большие платформы.
Ну и да - приятнее смотреть на tensor([...]), чем на распухший var_dump.
Тесты, традиции и другие земные радости
Если вдруг кто-то захочет ковыряться:
composer install
composer test
composer test:coverageГлавное - включить Xdebug, иначе PHPUnit посмотрит на вас так, будто вы забыли что-то важное.
Без вывода
Не знаю, станет ли PrettyPrint кому-то полезным. Может быть, это одна из тех мелких утилит, которые живут тихо в чьём-то проекте и ��икогда не попадают в топ-репозитории.
И это нормально.
Но если после прочтения вы вдруг поймаете себя на мысли: "А какая маленькая штука в моём проекте меня бесит, и почему я её до сих пор не пофиксил?" - значит, эта статья сработала. Удачи вам в ваших проектах!