В мире разработки часто возникает потребность в работе с командной строкой — будь то выполнение системных команд, обработка их вывода или работа с различными кодировками. Столкнувшись с такими задачами, программисты часто начинают искать готовые решения, чтобы сэкономить время и избежать повторения однотипного кода.
Именно для таких случаев была разработана библиотека PhpFluentConsole — простое и гибкое решение для работы с командной строкой, с поддержкой кодировок и множеством полезных фич для работы с выводом команд.
Зачем нужна эта библиотека?
PhpFluentConsole не заменяет существующие библиотеки, а выступает как легковесная основа для гибкого построения CLI-запросов. Её цель — упростить интеграцию системных команд без необходимости изучать объёмную документацию или вникать в сложный API.
Основная цель библиотеки — упрощение и автоматизация выполнения команд, а также обработка вывода, что делает её удобной для разработчиков, которые хотят создавать свои собственные решения на основе этой библиотеки.
Преимущества использования
Гибкость и удобство:
Библиотека поддерживает текучий интерфейс для построения команд, что делает код максимально читаемым и лаконичным.
 Пример:
$cli = new ConsoleRunner()
     ->setCommand('echo')
     ->addKey('Hello, World!')
Поддержка кодировок:
Для работы с кириллицей и другими символами, библиотека включает поддержку различных кодировок, таких как CP866, UTF-8, CP1251, и других. Это особенно важно при работе в windows-среде, где часто возникают проблемы с выводом текста в стандартных кодировках, что так же может затруднить обработку вывода с помощью регулярных выражений.
 Пример:
$cli = new ConsoleRunner()
     ->setCommand('dir')
     ->encoding('866')
Обработка ошибок и кодов возврата:
Библиотека позволяет получать код возврата после выполнения команды и искать ошибки в выводе с помощью регулярных выражений. Это важно, если вам нужно удостовериться, что команда завершилась успешно или отловить ошибку в выводе.
 Пример:
if (!$cli->run()) {
    echo "Ошибка выполнения команды!" . $cli->getReturnCode();
}
Поиск совпадений по паттернам:
Вы можете искать совпадения в выводе команды, используя регулярные выражения. Это полезно, если нужно извлечь конкретную информацию из вывода команд, например, лог-файлов или диагностики системы.
 Пример:
if ($cli->run()) {
   $matches = $cli->getMatches('/Error/');
   print_r($matches);
}
Стандартный вывод:
Если нам просто нужно получить массив строк.
 Пример:
if ($cli->run()) {
   print_r($cli->getOutput());
}
Установка
composer require mikhailovlab/php-fluent-console
Описание методов
Устанавливает команду для выполнения.
public function setCommand(string $cmd): self
Добавляет аргумент или часть команды.
public function addKey(string $key): self
Устанавливает кодировку для вывода. Например, '866' для отображения кириллицы в Windows.
public function encoding(?string $encoding = null): self
Устанавливает флаг, что нужно выполнить обратную конвертацию кодировки. Метод полезен для возврата вывода в исходной кодировке для работы с cmd.
public function decoding(): self
Возвращает текущую команду.
public function getCommand(): string
Выполняет команду и возвращает true, если код возврата равен 0.
public function run(): bool
Возвращает вывод после выполнения команды.
public function getOutput(): array
Возвращает код возврата выполнения команды.
public function getReturnCode(): int
Проверяет вывод на наличие ошибки по регулярному выражению.
public function hasError(string $pattern): bool
Возвращает все строки вывода, совпавшие с регулярным выражением.
public function getMatches(string|array $patterns): array
Примеры
Проверять функционал будем на примере фреймворка Laravel 11, но так же можно использовать любой другой, это непринципиально:
Пример 1: Получаем IP адрес в операционной системе windows
$cli = new ConsoleRunner()
     ->setCommand('ipconfig')
     ->addKey('/all')
     ->encoding('866'); //ожидаем вывод с кириллицей
if ($cli->run()) {
   dd($cli->getOutput());
}
Вывод:
array:59 [▼ // app\Http\Controllers\TestController.php:21
  0 => ""
  1 => "Настройка протокола IP для Windows"
  2 => ""
  3 => "   Имя компьютера  . . . . . . . . . : DESKTOP-HRTB4N9"
  4 => "   Основной DNS-суффикс  . . . . . . :"
  5 => "   Тип узла. . . . . . . . . . . . . : Гибридный"
  6 => "   IP-маршрутизация включена . . . . : Нет"
  7 => "   WINS-прокси включен . . . . . . . : Нет"
  8 => "   Порядок просмотра суффиксов DNS . : lan"
  9 => ""
  10 => "Адаптер Ethernet Ethernet 2:"
...
Кириллица отображается корректно, можно двигаться дальше.
Пример 2: Получаем список контейнеров с электронными подписями
$cli = new ConsoleRunner()
     ->setCommand('csptest')
     ->addKey('-keyset')
     ->addKey('-enum_cont')
     ->addKey('-verifycontext')
     ->addKey('-fqcn');
if ($cli->run()) {
    dd($cli->getMatches('#\\\\.*#'));
}
$pattern = '/\[ErrorCode:\s*(0x[0-9A-Fa-f]+)\]/';
dd('Error code: ' . $cli->getMatches($pattern)[0]);
Вывод:
array:1 [▼ // app\Http\Controllers\TestController.php:23
  0 => "\\.\REGISTRY\d58fe6c13-d917-2a53-8e9c-8c4b8158220-test"
]
В принципе работает, но если мы собираемся разрабатывать свою библиотеку, такой подход может показаться избыточным.
Пример 3: Расширяем базовый класс
Мы можем наследоваться от базового класса ConsoleRunner и вместо указания методов через addKey, вызывать их динамически, используя магический метод __call. Как правило, в консольных утилитах мы можем получить список всех доступных методов и аргументов используя ключ -help и добавить их в массив, что бы исключить ошибки, т.к. при несовпадении будет выброшено исключение.
class customRunner extends ConsoleRunner
{
    private $methods = [
        'keyset',
        'enum_cont',
        'verifycontext',
        'fqcn'
    ];
    public function __call(string $name, array $arguments): self
    {
        if (in_array($name, $this->methods)) {
            $this->addKey('-' . $name);
            if (!empty($arguments)) {
                foreach ($arguments as $arg) {
                    $this->addKey((string) $arg);
                }
            }
            return $this;
        }
        throw new \BadMethodCallException("Method $name is not supported");
    }
}
try{
    $cli = new customRunner()
         ->setCommand('csptest')
         ->keyset()
         ->enum_cont() 
         ->verifycontext()
         ->fqcn();
    if ($cli->run()) {
        dd($cli->getMatches('#\\\\.*#'));
    }
   $pattern = '/\[ErrorCode:\s*(0x[0-9A-Fa-f]+)\]/';
   dd('Error code: ' . $cli->getMatches($pattern)[0]);
}catch (Exception $e){
    dd($e->getMessage());
}
Вывод:
array:1 [▼ // app\Http\Controllers\TestController.php:23
  0 => "\\.\REGISTRY\d58fe6c13-d917-2a53-8e9c-8c4b8158220-test"
]
Мы можем перенести логику и обработку вывода в отдельные методы, что бы сделать код еще более лаконичным.
try{
    $containers = new customRunner()
                ->getContainers()
                ->run();
    dd($containers);
    
}catch (Exception $e){
    dd($e->getMessage());
}
Таким образом мы можем наследоваться от базового класса и создавать свои классы-обертки, в которых будем описывать методы и обрабатывать вывод.
Заключение
PhpFluentConsole — это удобный и гибкий инструмент для работы с командной строкой. Он позволяет строить команды через простой и понятный интерфейс, поддерживает работу с кодировками, предоставляет возможности для обработки ошибок и извлечения данных из вывода команд. Библиотека является отличной основой для создания более сложных решений или даже для разработки других библиотек на её основе, таких как CryptoProBuilder, о которой я расскажу в следующей статье.
Если у вас есть вопросы или предложения по улучшению этой библиотеки, вы всегда можете обратиться к репозиторию на GitHub, где вы сможете внести свой вклад или создать issue для обсуждения новых возможностей.
 
           
 
onpremise
Отличная статья! PhpFluentConsole действительно закрывает важную нишу в PHP-разработке. Вот мои мысли по вашей библиотеке:
Библиотека предлагает элегантное решение для главной боли разработчиков - работы с командной строкой, особенно под Windows. Fluent-интерфейс создаёт потрясающе читаемый код, а поддержка кодировок (CP866, CP1251) - это именно то, что постоянно не хватает при работе с кириллицей. Особенно впечатлила обработка вывода через getMatches() с regex - такой подход действительно экономит часы рутинной работы.
Расширяемость через наследование (как в примере с customRunner) - правильный архитектурный ход. Это превращает библиотеку в платформу для создания специализированных инструментов вроде упомянутого CryptoProBuilder.
Для дальнейшего развития стоит рассмотреть:
Более гибкую обработку динамических методов без жесткого white-list'а
Добавление работы со стандартным потоком ошибок (STDERR)
Поддержку пайпов между командами и таймаутов выполнения
Создание врапперов для популярных утилит (OpenSSL, Git, Docker) как отдельный пакет
Главное преимущество - продуманная работа с Windows-кодировками. Это та фича, из-за которой стоит попробовать библиотеку прямо сейчас. Жду с нетерпением продолжения про CryptoProBuilder - такой подход действительно может изменить экосистему инструментов для работы с ЭЦП в PHP.
DmitriiMikhailov Автор
Спасибо за идеи! В этом действительно есть смысл. Это первая версия библиотеки, я изначально набросал её как решение для кроссплатформенной работы с КриптоПро через консоль, без необходимости встраивать SDK, но с возможностью удобно собирать команды через fluent-интерфейс. Улучшения, о которых вы пишете, точно сделают её более гибкой и полезной. Обязательно учту в будущих версиях.
SerafimArts
Судя по тексту - это обычный GPT бот, зачем вы отвечаете на его сообщения?
DmitriiMikhailov Автор
Мне об этом неизвестно. Я стараюсь не быть чрезмерно категоричным. Идеи в любом случае полезные.