cloud-castle / cli
Инструментарий консольного вывода для PHP 8.1+ без зависимостей: ANSI-стили и сообщения, таблицы, рамки, деревья, колонки, прогресс-бары и спиннеры, потоковый рендер без буфера, интерактивные запросы (ввод, скрытый пароль, мультивыбор) с валидацией, генерация автодополнения shell.
Package info
pkg:composer/cloud-castle/cli
Requires
- php: >=8.1
Requires (Dev)
- deptrac/deptrac: ^3.0 || ^4.0
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.75
- icanhazstring/composer-unused: ^0.9
- infection/infection: ^0.29 || ^0.33
- league/climate: ^3.11
- php-parallel-lint/php-parallel-lint: ^1.4
- phpmd/phpmd: ^2.15
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^1.12 || ^2.1
- phpstan/phpstan-deprecation-rules: ^1.2 || ^2.0
- phpstan/phpstan-phpunit: ^1.4 || ^2.0
- phpstan/phpstan-strict-rules: ^1.6 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.5
- psalm/plugin-phpunit: ^0.19 || ^0.20
- rector/rector: ^1.2 || ^2.0
- roave/security-advisories: dev-latest
- squizlabs/php_codesniffer: ^3.12 || ^4.0
- symfony/console: ^6.4
- vimeo/psalm: ^6.0
- webmozart/assert: ^1.11
Suggests
- cloud-castle/di: Ленивое создание команд из контейнера зависимостей
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-16 05:57:57 UTC
README
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano
CloudCastle Cli
Самодостаточный инструментарий оформления вывода и интерактива для 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).- Автодополнение shell —
ShellCompletionгенерирует скрипты дополнения имён команд для 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. Функциональность
Легенда: ✅ полная · ⚠️ частичная · ❌ нет.
| Возможность | 🏆 cli | symfony/console | league/climate | minicli | nunomaduro/termwind | jc21/clitable | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| 16 ANSI-цветов | ✅ | ✅ | ✅ | ⚠️ | ✅ | ❌ | cli |
| 256/RGB truecolor | ✅ | ⚠️ | ✅ | ❌ | ✅ | ❌ | cli |
| Начертания текста | ✅ | ✅ | ✅ | ⚠️ | ✅ | ❌ | cli |
| Таблицы | ✅ | ✅ | ✅ | ❌ | ⚠️ | ✅ | cli |
| Несколько стилей рамок | ✅ | ✅ | ⚠️ | ❌ | ⚠️ | ⚠️ | cli |
| Выравнивание колонок | ✅ | ✅ | ⚠️ | ❌ | ✅ | ⚠️ | cli |
| Рамки/панели (Box) | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | cli |
| Деревья | ✅ | ⚠️ | ❌ | ❌ | ❌ | ❌ | cli |
| Раскладка по колонкам | ✅ | ❌ | ✅ | ❌ | ⚠️ | ❌ | cli |
| Списки (маркир./нумер.) | ✅ | ⚠️ | ⚠️ | ❌ | ✅ | ❌ | cli |
| Прогресс-бар | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | cli |
| Спиннер | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | cli |
| Управление курсором | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | cli |
| Гиперссылки OSC 8 | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | cli |
| Тег-разметка | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | cli |
| Вопросы/подтверждение/выбор | ✅ | ✅ | ✅ | ⚠️ | ❌ | ❌ | cli |
| Множественный выбор | ✅ | ⚠️ | ✅ | ❌ | ❌ | ❌ | cli |
| Скрытый ввод пароля | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | cli |
| Ввод с валидацией и повтором | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | cli |
| Автодополнение shell | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | cli |
| Размер/поддержка цвета терминала | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | cli |
| Юникод-ширина | ✅ | ✅ | ⚠️ | ❌ | ✅ | ❌ | cli |
| Экранирование ANSI-инъекций | ✅ | ✅ | ❌ | ❌ | ⚠️ | ❌ | cli |
| Разделение stdout/stderr | ✅ | ✅ | ❌ | ⚠️ | ❌ | ❌ | cli |
| Тестируемость интерактива (поток ввода) | ✅ | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | cli |
| Потоковый рендер (Generator построчно) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | cli |
| Печать напрямую в поток процесса | ✅ | ✅ | ✅ | ⚠️ | ✅ | ⚠️ | cli |
| Деревья без ограничения глубины | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | cli |
| Всего ✅ | 🏆 28 | 19 | 12 | 0 | 11 | 1 | cli |
2. Производительность
| Библиотека | 100×4 | 500×4 | 1000×4 | 🏆 Победитель |
|---|---|---|---|---|
| 🏆 cli | 0,201 | 0,988 | 1,995 | 🏆 cli |
| symfony/console | 10,939 | 55,633 | 111,590 | — |
| league/climate | 1,459 | 7,245 | 14,862 | — |
| minicli | н/д¹ | н/д¹ | н/д¹ | — |
| nunomaduro/termwind | н/д¹ | н/д¹ | н/д¹ | — |
| jc21/clitable | н/д¹ | н/д¹ | н/д¹ | — |
Единицы: мс. Рендер таблицы N×4 в строку, среднее на операцию (300 итераций, минимум из 4 прогонов).
¹ minicli, nunomaduro/termwind и jc21/clitable не предоставляют рендер таблиц как готовый примитив — сравнивать нечего, отмечено «н/д».
3. Потребление памяти
| Библиотека | 100×4 | 500×4 | 1000×4 | 🏆 Победитель |
|---|---|---|---|---|
| 🏆 cli | 8,1 | 28,1 | 52,1 | 🏆 ничья: cli, symfony/console |
| symfony/console | 8,1 | 28,1 | 52,1 | 🏆 ничья: cli, symfony/console |
| league/climate | 12,1 | 52,1 | 96,1 | — |
| minicli | н/д¹ | н/д¹ | н/д¹ | — |
| nunomaduro/termwind | н/д¹ | н/д¹ | н/д¹ | — |
| jc21/clitable | н/д¹ | н/д¹ | н/д¹ | — |
Единицы: KB. Footprint одной таблицы N×4 в куче: дельта удержания 20 результатов / 20 (изолированный процесс).
¹ minicli, nunomaduro/termwind и jc21/clitable не предоставляют рендер таблиц как готовый примитив — сравнивать нечего, отмечено «н/д».
4. Пиковая память рендера
| Библиотека | 100×4 | 500×4 | 1000×4 | 🏆 Победитель |
|---|---|---|---|---|
| 🏆 cli | 0,0 | 0,0 | 95,7 | 🏆 cli |
| symfony/console | 606,0 | 835,7 | 1 110,7 | — |
| league/climate | 124,7 | 834,6 | 1 731,4 | — |
| minicli | н/д¹ | н/д¹ | н/д¹ | — |
| nunomaduro/termwind | н/д¹ | н/д¹ | н/д¹ | — |
| jc21/clitable | н/д¹ | н/д¹ | н/д¹ | — |
Единицы: KB. Пик сверх подготовленных данных при печати таблицы N×4 в поток (изолированный процесс).
¹ minicli, nunomaduro/termwind и jc21/clitable не предоставляют рендер таблиц как готовый примитив — сравнивать нечего, отмечено «н/д».
² 0,0 — рендер не потребовал ни байта сверх памяти, уже занятой подготовленными данными: строки печатаются по одной и сразу освобождаются.
5. Безопасность и корректность
| Свойство | 🏆 cli | symfony/console | league/climate | minicli | nunomaduro/termwind | jc21/clitable | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| Экранирование ANSI-инъекций во внешних данных | ✅ | ✅ | ❌ | ❌ | ⚠️ | ❌ | cli |
| Разделение обычного вывода и ошибок | ✅ | ✅ | ❌ | ⚠️ | ❌ | ❌ | cli |
| Определение терминала без порождения процессов¹ | ✅ | ❌ | ⚠️ | ✅ | ✅ | ✅ | cli |
| Минимальная поверхность атаки (внешние runtime-зависимости) | ✅ (0) | ❌ (много) | ✅ (1) | ✅ (0) | ⚠️ (2+) | ✅ (0) | cli |
| Обход дерева без риска переполнения стека² | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | cli |
| Ввод из изолируемого потока | ✅ | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | cli |
¹ cli читает окружение/isatty и не порождает процессов (stty/mode). Исключение — скрытый ввод пароля отключает эхо через stty только на реальном TTY.
² рендер дерева итеративный (явный стек), поэтому внешние данные произвольной вложенности не обрушивают стек вызовов PHP.
6. Качество кода
| Метрика | 🏆 cli | symfony/console | league/climate | minicli | nunomaduro/termwind | jc21/clitable | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| PHPStan | max + strict-rules | ~level 5 | частично | — | базовый | — | cli |
| Psalm | уровень 1 | не заявлен | не заявлен | не заявлен | не заявлен | не заявлен | cli |
| Покрытие тестами | 100% | высокое | среднее | — | есть | низкое | cli |
| Мутационное (MSI) | 100% | не публикуется | не публикуется | не публикуется | не публикуется | не публикуется | cli |
| Внешние runtime-зависимости | 0 | много | 1 | 0 | 2+ | 0 | cli |
🏆 Победитель — 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.
Документация
- API-справочник (сайт): https://cloud-castle.gitverse.site/cli/
- Wiki (обзор и навигация): wiki/docs/ru/Home.md
- Сравнение с аналогами: wiki/docs/ru/Comparison.md
- Потоковый рендер: wiki/docs/ru/Streaming.md
- Приёмники вывода: wiki/docs/ru/StreamOutput.md
- Репозиторий: https://gitverse.ru/cloud-castle/cli
- История изменений: CHANGELOG.md
- Как внести вклад: CONTRIBUTING.md
- Кодекс поведения: CODE_OF_CONDUCT.md
- Политика безопасности: SECURITY.md
- Поддержка и вопросы: SUPPORT.md
- Обновление между версиями: UPGRADING.md
Лицензия
MIT © CloudCastle (alex-4-17@yandex.ru)
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano