Консольные команды с PHPixie Console
PHPixie Console — это новый компонент позволяющий создавать, роутить и запускать консольные команды. Как и другие библиотеки фреймворка он может легко использоваться без самой PHPixie как более простая альтернатива аналогичной библиотеки из Symfony. В первую очередь это статься рассчитана на тех кто уже пользуется PHPixie и в ней будет короткое описание стандартных команд фреймворка, но в конце я так же приведу пример того как запустить PHPixie Console в отдельности.
- Скопируйте https://github.com/PHPixie/Project/blob/master/console в корень проекта. Если вы на линуксе то поставьте на этот файл права на исполнение (chmod +x console)
- Добавьте «phpixie/framework-bundle»:»~3.0» в composer.json
- Поключите бандл \PHPixie\FrameworkBundle и добавьте /*GeneratorPlaceholder*/
как тут: https://github.com/PHPixie/Project/blob/master/src/Project/Framework/Bundles.php
Опционально можно также скопировать стандартную архитектуру и демо-команду с обновленного скелета проекта:
- Console.php
- Console/Greet.php
- В Builder классе банда подключите класс консоли как тут: Builder.php
Внимание, замените NamespacePlaceholder на неймспейс своего проекта (по умолчанию Project) и BundleNamePlaceholder на имя своего бандла (скорее всего это App).
После этого ваша структура будет такая-же как у свежего проекта.
Использование
Запустите консоль чтобы увидеть список доступных команд:
cd your_project_directory/
./console
# или
php ./console
Результат будет примерно таким:
Available commands:
app:greet Greet the user
framework:installWebAssets Symlink or copy bundle web files to the projects web folder
framework:generateBundle Generate a new bundle
help Print command list and usage
Команда help покажет больше информации и список доступных параметров:
./console help framework:installWebAssets
framework:installWebAssets [ --copy ]
Symlink or copy bundle web files to the projects web folder
Options:
copy Whether to copy web directories instead of symlinking them
Стандартные команды
- framework: installWebAssets — создает ярлыки в папке /web/bundles указующие на /web папки внутри бандлов, например /web/bundles/app → /bundles/app/web. Это делается для того чтобы бандлы инсталлированные с помощью композера могли предоставить свои веб файлы. Флажок --copy скопирует файлы вместо создания ярлыков. Это удобно например для создания архива для CDN.
- framework: generateBundle — генерирует и подключает новый бандл в проект. Больше не придется вручную создавать бандлы путем копирования существующего.
Добавление собственных команд
У вас в проекте уже добавлена простая команда app: greet. Работа с командами полностью аналогична добавлению HTTP процессоров, используя класс \Project\App\Console. Достаточно добавить имя команды в массив возвращаемый методом commandNames () и добавить метод build
В конструкторе команды вы можете задать описание и список параметров и аргументов:
namespace Project\App\Console;
class Greet extends \PHPixie\Console\Command\Implementation
{
public function __construct($config)
{
// Описание
$config->description('Greet the user');
// Добавим параметр 'message'
$config->argument('message')
->description("Message to display");
parent::__construct($config);
}
/**
* Этот метод вызывается при запуске команды.
* $argumentData и $optionData работают так же
* как HTTP $request->query() и $request->data()
*/
public function run($argumentData, $optionData)
{
$message = $argumentData->get('message', "Have fun coding!");
$this->writeLine($message);
}
}
Аргументы и опции
Допустим мы хотим добавить следующую команду:
sqldump --user=root --skip-missing -f myDatabase users items
Здесь myDatabase имя базы данных, а за ней список таблиц которые мы хотели бы забэкапить. Это аргументы нашей команды. А user, skip-missing, и f опции. Заметьте что для аргументов важен порядок в котором они задаются, а для опций нет. Также короткие опции с одной буквы используют один знак - вместо двух.
В коде это будет выглядеть вот так:
$config->option('user')
//Обязательная опция
->required()
//Ее описание, будет показано при запуске команды 'help'
->description("User to connect to the database with");
$config->option('skip-missing')
->description("Don't throw an error if the tables are missing")
//Задать опцию как флажок.
//Опциям-флажкам не задается значение,
//взамен они устанавливаются в 'true' если они присутствуют.
->flag();
$config->option('f')
->flag()
->description("Force database dump");
При описании аргументов надо помнить что задавать их надо в том порядке в котором они должны присутствовать в команде. В нашем случае аргумент database идеи перед tables:
$config->argument('database')
->required()
->description("Which database to dump the tables from");
$config->argument('tables')
->description("Tables to dump")
// Принимает массив значений
// В команде может быть толко один такой аргумент,
// и логично что он должен быть последним
->arrayOf();
Теперь при запуске help мы получим вот такой результат:
./console help app:sqldump
app:sqldump --user=VALUE [ -f ] [ --skip-missing ] DATABASE [ TABLES... ]
Options:
user User to connect to the database with
f Force database dump
skip-missing Dont throw an error if the tables are missing
Arguments:
DATABASE Which database to dump the tables from
TABLES Tables to dump
При запуске команды методу run () передадутся опции и аргументы, откуда их можно получить аналогично как и в HTTP процессоре:
public function run($argumentData, $optionData)
{
$database = $argumentData->get('database');
// С указанием дефолтного значения
$user = $optionData->get('user', 'phpixie');
}
Ввод и вывод
Самый простой метод вывода в консоль это просто return-уть текст. Но если процесс должен работать долгое время и надо выводить промежуточный результат, то можно использовать дополнительные методы:
public function run($argumentData, $optionData)
{
// Вывод текста
$this->write("Hello ");
// Вывод текста с новой строкой
$this->writeLine("World");
// Считать ввод пользователя с консоли
$str = $this->readLine();
// Если бросить CommandException то в консоли отобразится текст ошибки
// и сама команда возвратит не-нулевой статус код (полезно при работе с Bash).
throw new \PHPixie\Console\Exception\CommandException("Something bad happened");
}
Дополнительно можно получить доступ к CLI контексту и работать уже с ним:
public function run($argumentData, $optionData)
{
$context = $this->cliContext();
$inputStream = $cliContext->inputStream();
$outputStream = $cliContext->outputStream();
$errorStream = $cliContext->errorStream();
$outputStream->write("Hello");
$errorStream->writeLine("Something bad happened");
$context->setExitCode(1); // указать код статуса
Код статуса пригодится при проверке удачно ли исполнилась команда извне, например из Bash:
if ./console app:somecommand ; then
echo "Command succeeded"
else
echo "Command failed"
fi
Как я уже писал с самого начала, компонент можно легко использовать и без фреймворка:
class YourCommandRegistry extends \PHPixie\Console\Registry\Provider\Implementation
{
public function commandNames()
{
return ['greet'];
}
public function buildGreetCommand($config)
{
return new Greet($config);
}
}
$slice = new \PHPixie\Slice();
$cli = new \PHPixie\CLI();
$registry = new YourCommandRegistry();
$console = new \PHPixie\Console($slice, $cli, $registry);
$console->runCommand();
Таким образом добавить консольные возможности можно в любой проект, все это с минимальными зависимостями и весьма интуитивно.
Комментарии (2)
2 ноября 2016 в 21:11
0↑
↓
А почему было решено делать свою версию? Вроде получилась плюс-минус та же Symfony Console.2 ноября 2016 в 21:30
0↑
↓
Во-первых хочется чтобы фреймворк был изоморфным. Компонент консоли разработан так чтобы если вы уже используете PHPixie, то работа с консолью аналогична работе с вебом. Сам фреймворк модульный, так что использовать Симфони консоль и так было возможно с самого начала.Как дополнительный бонус контекст командной строки представлен простым интерфейсом, который можно легко подменить на что-то другое, например сделать запуск команд из браузера, а вывод записывать в переменную и передавать аяксом итд.
И даже если смотреть на использование без фреймворка, как я писал, зависимостей меньше, работает даже на PHP 5.3 (ну если очень надо). Но главная разница в парадигме.