Search by

Инструментарий консольного вывода для PHP 8.1+ без зависимостей: ANSI-стили и сообщения, таблицы, рамки, деревья, колонки, прогресс-бары и спиннеры, потоковый рендер без буфера, интерактивные запросы (ввод, скрытый пароль, мультивыбор) с валидацией, генерация автодополнения shell.

v2.0.0 2026-07-31 07:57 UTC

README

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano

CloudCastle Cli

CloudCastle Cli

Packagist Version PHP Version License Total Downloads Monthly Downloads Stars

Repository Issues CI Release

PHPStan Psalm PHPCS PHPMD Coverage Infection MSI OpenSSF Scorecard

Самодостаточный инструментарий оформления вывода и интерактива для PHP 8.1+ без runtime-зависимостей: цвета 16/256/RGB и начертания, таблицы и рамки, деревья, колонки и списки, прогресс-бар и спиннер, управление курсором, тег-разметка, интерактив с валидацией и скрытым вводом, автодополнение shell и безопасное экранирование ANSI-инъекций. Юникод учитывается по символам, а не байтам. Любой объём печатается потоково, при постоянном расходе памяти. Приёмник вывода — любой Contract\Writer: буферизованный Output, потоковый StreamOutput или собственная реализация.

Установка

composer require cloud-castle/cli

Требуется PHP 8.1+. Runtime-зависимостей нет.

Быстрый старт

<?php

use CloudCastle\Cli\BorderStyle;
use CloudCastle\Cli\Box;
use CloudCastle\Cli\Console;
use CloudCastle\Cli\Formatter;
use CloudCastle\Cli\ProgressBar;
use CloudCastle\Cli\Spinner;
use CloudCastle\Cli\Style;
use CloudCastle\Cli\Table;
use CloudCastle\Cli\Tree;
use CloudCastle\Cli\Output;

$output = new Output();
$console = new Console($output);

// Раскрашенные сообщения (ошибки уходят в отдельный поток stderr).
$console->success('Готово');       // зелёным
$console->warning('Осторожно');    // жёлтым
$console->error('Сбой');           // красным, в поток ошибок
$console->line(Style::Bold->apply('Заголовок'));

// Тег-разметка вместо ручных ANSI-последовательностей.
$console->line((new Formatter())->format('<green>ОК</green> <bold>сборка завершена</bold>'));

// Таблица с выбранным стилем рамки и выравниванием колонок.
echo (new Table(BorderStyle::Round))->render(['Имя', 'Роль'], [
    ['Пётр', 'admin'],
    ['Анна', 'user'],
]);

// Панель с рамкой и заголовком.
echo (new Box(BorderStyle::Double))->render("Релиз 1.0\nГотов к продакшену", 'Статус');

// Дерево из вложенного массива.
echo (new Tree())->render([
    'src' => ['Console.php', 'Table.php'],
    'tests' => ['ConsoleTest.php'],
]);

// Прогресс-бар с перерисовкой строки на месте.
$bar = new ProgressBar($output, 3);
$bar->advance();
$bar->advance();
$bar->finish();

// Спиннер для длительной операции без известного прогресса.
$spinner = new Spinner($output);
$spinner->tick();
$spinner->finish('Готово');

echo $output->fetch();       // обычный вывод (stdout)
echo $output->fetchErrors(); // вывод ошибок (stderr)

Возможности

Цвет и стиль

  • 16 ANSI-цветов — перечисление Color (foreground()/background(), apply()/applyBackground()) с яркими вариантами.
  • Начертания — перечисление Style (Bold, Dim, Italic, Underline, Blink, Reverse, Hidden, Strikethrough и базовые цвета) с apply().
  • 256/RGB truecolor и гиперссылкиAnsi: color256(), rgb(), background256(), backgroundRgb(), sequence(), hyperlink() (OSC 8), а также strip() (снять оформление) и escape() (обезвредить инъекции).
echo Ansi::rgb(255, 128, 0) . 'оранжевый' . Ansi::RESET;
echo Ansi::hyperlink('https://example.com', 'ссылка');

Текст и терминал

  • Юникод-текстText: width() (ширина по символам, а не байтам), wrap(), truncate(), pad() с выравниванием (Align::Left/Right/Center).
  • Терминал без порождения процессовTerminal: width(), height(), supportsColor(), isInteractive() — только из окружения и isatty.
  • КурсорCursor: перемещение (moveUp/Down/Forward/Back, moveTo), hide()/show(), clearLine()/clearScreen().
echo Text::pad('итого', 10, Align::Right);
$cols = Terminal::width();  // ширина терминала (по умолчанию 80)

Компоненты вывода

  • ТаблицыTable::render() выравнивает колонки по ширине содержимого; 5 стилей рамок через BorderStyle (Ascii, Round, Square, Double, Heavy).
  • ПанелиBox::render(): рамка с необязательным заголовком.
  • ДеревьяTree::render() из вложенного массива с соединительными линиями; обход итеративный, поэтому вложенность ограничена только памятью.
  • Списки и колонкиItemList (bullet(), numbered()), Columns::render() раскладывает элементы в заданное число колонок.
  • Прогресс и спиннерProgressBar (доля, проценты, счётчик текущее/всего, перерисовка на месте) и Spinner (tick()/finish()).
  • Тег-разметкаFormatter::format() превращает <green>…</green>, <bold>…</bold>, <bg-red>…</bg-red> и т. п. в ANSI; escape() экранирует <.

Потоковый вывод

  • Построчный рендер — у Table, Tree, Box и Columns есть lines() (Generator строк) и renderTo() (печать прямо в приёмник). Результат не собирается в памяти целиком, поэтому расход не зависит от объёма таблицы.
  • Приёмники выводаOutput буферизует (удобно в тестах), StreamOutput пишет прямо в потоки процесса (StreamOutput::standard() — поверх STDOUT/STDERR, ошибки отделены от обычного вывода).
$writer = StreamOutput::standard();

(new Table())->renderTo($writer, ['ID', 'Имя'], $millionRows);   // память не растёт

foreach ((new Tree())->lines($deepTree) as $line) {              // построчно
    $writer->writeln($line);
}

Интерактив

  • Prompt поверх потока ввода (тестируется без реального терминала): ask() (свободный ответ), confirm() (распознаёт y/yes/д/да), choice() (выбор из меню), multiChoice() (множественный выбор), askValid() (ввод с проверкой и повтором), secret() (скрытый ввод пароля, отключает эхо через stty только на реальном TTY).
  • Автодополнение shellShellCompletion генерирует скрипты дополнения имён команд для bash (bash()) и zsh (zsh()) из приложения.
$prompt = new Prompt($stdin, $output);
$name = $prompt->ask('Имя проекта', 'app');
$token = $prompt->secret('Токен');        // ввод скрыт

Безопасность

  • Экранирование ANSI-инъекцийAnsi::escape() и Formatter::escape() обезвреживают управляющие последовательности во внешних данных.
  • Разделение stdout/stderr — ошибки идут в отдельный поток (Console::error(), StreamOutput::writeError()), не смешиваясь с обычным выводом.
  • Стойкость к глубоким данным — рендер дерева итеративный: внешние данные произвольной вложенности не обрушивают стек вызовов PHP.
  • Раскрашенные сообщенияConsole: success(), error() (в stderr), warning(), info(), line().

Сравнение с аналогами

Числа производительности и памяти — из реального изолированного прогона benchmarks/compare.php (рендер таблиц N×4, PHP 8.1 без Xdebug); воспроизводятся командой XDEBUG_MODE=off composer docs:build.

Операция приведена к строгой эквивалентности: все участники рисуют одну и ту же таблицу в ASCII-рамке (стиль по умолчанию у symfony/console), и прогон падает, если вывод разошёлся хотя бы на байт. Сравнивать юникод-рамку с ASCII-рамкой было бы нечестно — те же строки весят в полтора раза больше только из-за трёхбайтных символов псевдографики.

Все таблицы ниже сгенерированы автоматически из честных сравнительных тестов (benchmarks/compare.php) на ОДИНАКОВОЙ операции для аналогов, PHP 8.1.34, без Xdebug. В каждой таблице есть графа 🏆 Победитель.

1. Функциональность

Легенда: ✅ полная · ⚠️ частичная · ❌ нет.

Возможность🏆 clisymfony/consoleleague/climateminiclinunomaduro/termwindjc21/clitable🏆 Победитель
16 ANSI-цветов⚠️cli
256/RGB truecolor⚠️cli
Начертания текста⚠️cli
Таблицы⚠️cli
Несколько стилей рамок⚠️⚠️⚠️cli
Выравнивание колонок⚠️⚠️cli
Рамки/панели (Box)cli
Деревья⚠️cli
Раскладка по колонкам⚠️cli
Списки (маркир./нумер.)⚠️⚠️cli
Прогресс-барcli
Спиннерcli
Управление курсоромcli
Гиперссылки OSC 8cli
Тег-разметкаcli
Вопросы/подтверждение/выбор⚠️cli
Множественный выбор⚠️cli
Скрытый ввод пароляcli
Ввод с валидацией и повторомcli
Автодополнение shellcli
Размер/поддержка цвета терминалаcli
Юникод-ширина⚠️cli
Экранирование ANSI-инъекций⚠️cli
Разделение stdout/stderr⚠️cli
Тестируемость интерактива (поток ввода)⚠️⚠️cli
Потоковый рендер (Generator построчно)cli
Печать напрямую в поток процесса⚠️⚠️cli
Деревья без ограничения глубиныcli
Всего ✅🏆 2819120111cli

2. Производительность

Библиотека100×4500×41000×4🏆 Победитель
🏆 cli0,2010,9881,995🏆 cli
symfony/console10,93955,633111,590
league/climate1,4597,24514,862
minicliн/д¹н/д¹н/д¹
nunomaduro/termwindн/д¹н/д¹н/д¹
jc21/clitableн/д¹н/д¹н/д¹

Единицы: мс. Рендер таблицы N×4 в строку, среднее на операцию (300 итераций, минимум из 4 прогонов).

¹ minicli, nunomaduro/termwind и jc21/clitable не предоставляют рендер таблиц как готовый примитив — сравнивать нечего, отмечено «н/д».

3. Потребление памяти

Библиотека100×4500×41000×4🏆 Победитель
🏆 cli8,128,152,1🏆 ничья: cli, symfony/console
symfony/console8,128,152,1🏆 ничья: cli, symfony/console
league/climate12,152,196,1
minicliн/д¹н/д¹н/д¹
nunomaduro/termwindн/д¹н/д¹н/д¹
jc21/clitableн/д¹н/д¹н/д¹

Единицы: KB. Footprint одной таблицы N×4 в куче: дельта удержания 20 результатов / 20 (изолированный процесс).

¹ minicli, nunomaduro/termwind и jc21/clitable не предоставляют рендер таблиц как готовый примитив — сравнивать нечего, отмечено «н/д».

4. Пиковая память рендера

Библиотека100×4500×41000×4🏆 Победитель
🏆 cli0,00,095,7🏆 cli
symfony/console606,0835,71 110,7
league/climate124,7834,61 731,4
minicliн/д¹н/д¹н/д¹
nunomaduro/termwindн/д¹н/д¹н/д¹
jc21/clitableн/д¹н/д¹н/д¹

Единицы: KB. Пик сверх подготовленных данных при печати таблицы N×4 в поток (изолированный процесс).

¹ minicli, nunomaduro/termwind и jc21/clitable не предоставляют рендер таблиц как готовый примитив — сравнивать нечего, отмечено «н/д».

² 0,0 — рендер не потребовал ни байта сверх памяти, уже занятой подготовленными данными: строки печатаются по одной и сразу освобождаются.

5. Безопасность и корректность

Свойство🏆 clisymfony/consoleleague/climateminiclinunomaduro/termwindjc21/clitable🏆 Победитель
Экранирование ANSI-инъекций во внешних данных⚠️cli
Разделение обычного вывода и ошибок⚠️cli
Определение терминала без порождения процессов¹⚠️cli
Минимальная поверхность атаки (внешние runtime-зависимости)✅ (0)❌ (много)✅ (1)✅ (0)⚠️ (2+)✅ (0)cli
Обход дерева без риска переполнения стека²cli
Ввод из изолируемого потока⚠️⚠️cli

¹ cli читает окружение/isatty и не порождает процессов (stty/mode). Исключение — скрытый ввод пароля отключает эхо через stty только на реальном TTY.

² рендер дерева итеративный (явный стек), поэтому внешние данные произвольной вложенности не обрушивают стек вызовов PHP.

6. Качество кода

Метрика🏆 clisymfony/consoleleague/climateminiclinunomaduro/termwindjc21/clitable🏆 Победитель
PHPStanmax + strict-rules~level 5частичнобазовыйcli
Psalmуровень 1не заявленне заявленне заявленне заявленне заявленcli
Покрытие тестами100%высокоесреднееестьнизкоеcli
Мутационное (MSI)100%не публикуетсяне публикуетсяне публикуетсяне публикуетсяне публикуетсяcli
Внешние runtime-зависимости0много102+0cli

🏆 Победитель — cli (по phpstan/psalm/coverage/MSI).

О памяти честно. Готовая таблица занимает в куче ровно столько же, сколько у symfony/console — вывод совпадает байт в байт, поэтому по этой метрике честная ничья, и обойти аналог тут нечем. Разница в другом: на сам рендер cli тратит на порядок меньше — 96 KB против 1,1 MB на таблице 1000×4, а до тысячи строк не выходит за пределы памяти, уже занятой данными.

Плюсы, минусы и когда применять

Сильные стороны:

  • Функционал = суперсет аналогов — 28 возможностей против 19 у symfony/console и 12 у league/climate: цвета 16/256/RGB, начертания, таблицы с 5 стилями рамок, панели, деревья, колонки, списки, прогресс-бар, спиннер, курсор, тег-разметка, потоковый рендер и полный интерактив (валидация, скрытый ввод, множественный выбор, автодополнение shell) — ни один аналог не покрывает всё сразу.
  • Быстрейший рендер таблиц — быстрее symfony/console в 56 раз и league/climate в 7 раз на строго одинаковой операции: все участники рисуют одну и ту же таблицу в ASCII-рамке, вывод совпадает байт в байт.
  • Самый лёгкий рендер — печать таблицы 1000×4 в поток стоит 96 KB против 1,1 MB у symfony/console и 1,7 MB у league/climate; готовая таблица в куче весит столько же, сколько у symfony/console.
  • Безопасность — экранирование ANSI-инъекций во внешних данных, разделение обычного вывода и ошибок (stdout/stderr), определение терминала без порождения процессов (только окружение и isatty), изолируемый поток ввода для тестов, итеративный обход дерева (данные любой вложенности не обрушивают стек).
  • Минимальный вес — ноль runtime-зависимостей, никакого стороннего груза в дереве зависимостей; приёмник вывода абстрагирован контрактом Writer.

Слабые стороны (честно):

  • Зрелость — пакет моложе и менее распространён, чем symfony/console: меньше установок, меньше публичных примеров, короче история релизов.
  • Экосистема — у symfony/console шире набор готовых интеграций с фреймворком и сторонними пакетами.

Когда применять.

  • Собственные CLI-утилиты и микрофреймворки — богатый вывод и интерактив без единой runtime-зависимости: цвета, таблицы, панели, деревья, прогресс-бар, спиннер, ввод с валидацией и скрытым паролем.
  • Длинные отчёты и выгрузки — потоковый рендер (lines()/renderTo()) печатает таблицы и деревья любого размера при памяти, не зависящей от объёма; это же снимает риск при выводе в пайп или файл.
  • Горячие пути и массовый вывод — там, где рендер таблиц заметен в профиле: выигрыш в 56 раз против symfony/console на одинаковой операции.
  • Обработка внешних данных — экранирование ANSI-инъекций и обход деревьев без рекурсии делают вывод чужого контента безопасным по умолчанию.
  • Библиотеки и пакеты — ноль зависимостей не тянет чужое дерево версий в проект потребителя.

Когда лучше аналог. Для приложения, уже построенного на Symfony, удобнее symfony/console: команды, DI, события и готовые интеграции идут пакетом — это честный компромисс ширины экосистемы против лёгкости и скорости.

Разработка

composer install
composer check    # линтеры + статический анализ + тесты
composer fix      # автоисправления (Rector, PHP CS Fixer, PHPCBF)
composer ci       # полный CI-пайплайн локально

Полный список команд с описаниями: composer run-script --list.

Документация

Лицензия

MIT © CloudCastle (alex-4-17@yandex.ru)

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano