В Joomla, как и во многих других PHP фреймворках и движках, уже довольно давно (с версий 1.7.x) существует возможность работы с помощью CLI - Command line interface. Конечно, вряд ли в эту статью попадут люди совсем не знакомые с консолью сервера и командной строкой, но для справки оговорюсь, что при работе с Joomla через CLI не затрагиваются ресурсы веб-сервера (Apache, Nginx etc.), запускается отдельный процесс php
(или несколько отдельных), в котором не действуют ограничения на время выполнения скрипта веб-сервера (чаще всего это 60 секунд).
Поэтому с помощью CLI решают те задачи, решение которых в веб-интерфейсе (из админки) Joomla нецелесообразно из-за возможных ограничений веб-сервера - так называемые "тяжелые" задачи. Это может быть миграция большого количества контента со старой версии Joomla на новую, синхронизация данных с внешним источником по API (синхронизация цен и остатков, выгрузка заказов), работа с файлами и каталогами, выполнение пакетных заданий на больших объёмах (десятки-сотни тысяч или даже миллионы позиций) и т.д. - всё то, что может занимать много времени и ресурсов сервера.
Оглавление
Создание плагина группы console для Joomla 4 / Joomla 5
Метод configure(). Добавление опций и аргументов команды.
Метод doExecute(). Основная работа команды.
SymfonyStyle: общение с пользователем в консоли, оформление вывода информации
![Часть команд CLI ядра Joomla 5 Часть команд CLI ядра Joomla 5](https://habrastorage.org/getpro/habr/upload_files/f3e/6e0/eb5/f3e6e0eb569573359ccb008bedcdd1b0.png)
Список литературы
Прежде всего код ядра Joomla является сам по себе документацией. PHP-классы CLI команд ядра лежат в директории
libraries/src/Console
. Смотрим их, изучаем и используем в качестве образца.Статья Joomla 4: мощь CLI приложений: в ней вкратце описываются возможности работы с CLI в Joomla, используемый синтаксис консольных команд. А так же статью дополняет список команд компонента Akeeba Backup.
Официальная документация Joomla. В ней вкратце описывается процесс создания плагина группы
console
для Joomla 4+ для добавления своих команд в CLI. Также в статье есть небольшое видео.Статья Создание WebCron плагина для Joomla 4 (Task Scheduler Plugin). В Joomla 4.1 появился планировщик задач - Task Scheduler. Можно написать плагин задачи и управлять параметрами и расписанием задачи через админку, а в CLI или в CRON вызывать только одну команду:
php /path/to/site/public_html/cli/joomla.php scheduler:run
. Это альтернативный путь решения задач данной статьи.Официальная документация Joomla CLI example - Onoffbydate и её перевод Пример написания CLI для Joomla 4 - Onoffbydate приводит пример создания плагина Joomla CLI, с помощью которого можно выключать / выключать модуль в зависимости от даты и времени года по CRON.
Статья Jiji, your new Joomla! 4 Console friend в официальном журнале международного Joomla-сообщества Joomla Community Magazine. В статье показывается пример создания
hello world
. Поскольку сама статья без примеров кода, стоит посмотреть GitHub этого плагина, где есть простой пример работы с материалами Joomla, например, импорт статей Joomla из файла. Чем больше перед глазами примеров реализации, тем проще создать что-то своё.Статья Создание плагинов с учётом новой структуры Joomla 4 рассказывает о том, как вообще создать плагин для Joomla 4 / Joomla 5 по новым правилам так, чтобы он проработал не один год и позволил отключить поддержку legacy (плагин обратной совместимости), что ощутимо увеличит быстродействие Joomla.
Создание плагина группы console для Joomla 4 / Joomla 5
Добавление команд происходит с помощью плагина. Файловая структура его в целом типичная для Joomla. В Joomla внедрена библиотека из PHP фреймворка Symfony, поэтому тем, кто уже сталкивался с CLI в Symfony - в Joomla себя будет чувствовать как у себя дома ?.
Я предполагаю, что читатели всё-таки предварительно знакомятся со списком предложенной "литературы", особенно со статьёй Создание плагинов с учётом новой структуры Joomla 4, поэтому некоторые объяснения буду в этой статье опускать в виду того, что они довольно полно описаны в предыдущих статьях.
Создадим плагин с названием testconsole
и посмотрим что умеет Joomla в CLI.
![](https://habrastorage.org/getpro/habr/upload_files/4ed/bac/14a/4edbac14a990fb313a856de37e4068cf.png)
XML-манифест плагина
Этот файл содержит описание плагина для установщика расширений Joomla (системное имя, группу плагина, дата создания, версия, сайт разработчика и т.д.), параметры конфигурации, сервер обновлений, а также задаёт Namespace плагина и директории для автозагрузки классов. Регистрация Namespace
плагина в реестре происходит при установке расширения. На момент написания статьи (Joomla 5.0.3) список классов для автозагрузки находится в файле administrator/cache/autoload_psr4.php
. Он обновляется после каждой установки или обновления расширения.
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="console" method="upgrade">
<name>Console - Testconsole</name>
<author>Sergey Tolkachyov</author>
<creationDate>Feb 2024</creationDate>
<copyright>Copyright © 2024 Sergey Tolkachyov. All rights reserved.</copyright>
<license>GNU/GPL 3</license>
<authorEmail>info@web-tolk.ru</authorEmail>
<authorUrl>https://web-tolk.ru</authorUrl>
<version>1.0.0</version>
<description>PLG_CONSOLE_SCANJOOMLASITES_DESC</description>
<namespace path="src">Joomla\Plugin\Console\Testconsole</namespace>
<languages folder="language">
<language tag="en-GB">en-GB/plg_console_testconsole.ini</language>
<language tag="en-GB">en-GB/plg_console_testconsole.sys.ini</language>
<language tag="ru-RU">ru-RU/plg_console_testconsole.ini</language>
<language tag="ru-RU">ru-RU/plg_console_testconsole.sys.ini</language>
</languages>
<files>
<folder>services</folder>
<folder plugin="testconsole">src</folder>
</files>
</extension>
Напомню, что по стандартам Joomla имя плагина должно содержать и его группу: Console - Testconsole
. Также namespace
плагина должен содержать в себе группу плагина. В нашем случае это будет Joomla\Plugin\Console\Testconsole.
![](https://habrastorage.org/getpro/habr/upload_files/dab/30a/d98/dab30ad988d5096a8a74fba21159389e.png)
Плагины группы console
в Joomla точно так же, как и любые другие плагины, могут иметь любые параметры в настройках плагина, которыми можно пользоваться в коде. Хотя при работе в CLI важнее параметры конкретной команды, но на их работу, да и логику работы плагина в целом могут влиять параметры плагина, которые указываем в админке: например, удалённые хосты, пароли, таймауты и прочие настройки.
Файл services/provider.php
Плагину нужен файл сервис-провайдер, который позволяет регистрировать плагин в DI-контейнере Joomla и даёт возможность обращаться к методам плагина извне с помощью MVCFactory
.
<?php
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Console\Testconsole\Extension\Testconsole;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @since 1.0.0
*/
public function register(Container $container)
{
$container->registerServiceProvider(new MVCFactory('Joomla\\Plugin\\Console\\Testconsole'));
$container->set(PluginInterface::class,
function (Container $container) {
$config = (array) PluginHelper::getPlugin('console', 'testconsole');
$subject = $container->get(DispatcherInterface::class);
$mvcFactory = $container->get(MVCFactoryInterface::class);
$app = Factory::getApplication();
$plugin = new Testconsole($subject, $config);
$plugin->setApplication($app);
$plugin->setMVCFactory($mvcFactory);
return $plugin;
}
);
}
};
В самом плагине вы можете обращаться к объекту приложения через $this->getApplication()
. Если нужна MVCFactory
, то и к ней через $this->getMVCFactory()
. MVCFactory
нужна для того, чтобы работать с данными в Joomla с помощью моделей компонентов - со всеми необходимыми проверками, обработками, которые предполагает логика компонента. Поэтому если Вы работаете с компонентом, написанным с учётом современной архитектуры расширений Joomla 4 / Joomla 5 - Вы можете использовать эти "плюшки". Скорее всего, если Вы работаете с компонентами, имеющими давнюю историю, Вы не сможете использовать MVCFactory
, так как многие компоненты до сих пор ещё не "переехали" на современную архитектуру.
Обратите внимание на то, что объект приложения будет не привычный SiteApplication
, а CliApplication
. Также в ядре существуют схожие классы: ConsoleApplication
- класс из Joomla-фреймворка, который был влит в CMS Joomla в версии 4.0. Этот класс облегчённый, лишен большинства "фич" CMS. Ещё существует DaemonApplication
- класс для работы Joomla в режиме демона. По идее, он умеет сохранять / передавать состояния при завершении и сам себя перезапускает.
Файл плагина src/Extension/Testconsole.php
Класс плагина, объявленный в этом файле, служит простой цели: регистрация команд в CLI Joomla. Плагин должен наследовать Joomla\Event\SubscriberInterface
, что означает, что в нём должен быть реализован метод getSubscribedEvents
, возвращающий маппинг событий и вызываемых методов плагина.
<?php
namespace Joomla\Plugin\Console\Testconsole\Extension;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Factory\MVCFactoryAwareTrait;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Event\SubscriberInterface;
use Psr\Container\ContainerInterface;
// Это namespaces классов наших команд, которые мы добавляем в Joomla CLI
use Joomla\Plugin\Console\Testconsole\Extension\Command\TestCommand;
use Joomla\Plugin\Console\Testconsole\Extension\Command\SecondtestCommand;
class Testconsole extends CMSPlugin implements SubscriberInterface
{
use MVCFactoryAwareTrait;
use DatabaseAwareTrait;
/**
* Choose which events this plugin is subscribed to and will respond to
* @return string[]
*
* @since version
*/
public static function getSubscribedEvents(): array
{
return [
\Joomla\Application\ApplicationEvents::BEFORE_EXECUTE => 'registerCommands',
];
}
}
Данный пример кода означает, что по событию application.before_execute
, описанному в libraries/vendor/joomla/application/src/ApplicationEvents.php
, будет вызван метод плагина registerCommands
. Название метода внутри плагина может быть любым, главное указать его в маппинге.
Также мы помним, что все методы плагинов в Joomla 4 / Joomla 5 должны возвращать пустоту - void
. Получение и возврат данных осуществляется через аргумент методов - $event
. В рамках Joomla CLI метод (в нашем случае registerCommands
) должен возвращать пустоту, но аргументов на вход не принимает.
Добавление класса команды происходит следующим образом:
Помещаем класс команды в Joomla DI-контейнер, указывая уникальное имя ресурса.
Указываем лоадеру CLI команд загрузить нужную команду из DI-контейнера Joomla.
<?php
namespace Joomla\Plugin\Console\Testconsole\Extension;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Factory\MVCFactoryAwareTrait;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Event\SubscriberInterface;
use Joomla\Plugin\Console\Testconsole\Extension\Command\TestCommand;
use Joomla\Plugin\Console\Testconsole\Extension\Command\SecondtestCommand;
use Psr\Container\ContainerInterface;
class Testconsole extends CMSPlugin implements SubscriberInterface
{
use MVCFactoryAwareTrait;
use DatabaseAwareTrait;
/**
* Choose which events this plugin is subscribed to and will respond to
* @return string[]
*
* @since version
*/
public static function getSubscribedEvents(): array
{
return [
\Joomla\Application\ApplicationEvents::BEFORE_EXECUTE => 'registerCommands',
];
}
/**
* Register custom commands
*
* @since version
*/
public function registerCommands(): void
{
// Test command
Factory::getContainer()->share(
'testconsole.test',
function (ContainerInterface $container) {
return new TestCommand;
},
true
);
// add test command to joomla.php cli script
Factory::getContainer()->get(\Joomla\CMS\Console\Loader\WritableLoaderInterface::class)
->add('testconsole:test', 'testconsole.test');
// Second test command
Factory::getContainer()->share(
'testconsole.secondtest',
function (ContainerInterface $container) {
return new SecondtestCommand;
},
true
);
// Add second test command to joomla.php cli script
Factory::getContainer()->get(\Joomla\CMS\Console\Loader\WritableLoaderInterface::class)
->add('testconsole:secondtest', 'testconsole.secondtest');
}
}
В Joomla DI-контейнер помещаем наш класс с помощью метода share()
. Его аргументы следующие:
$key
- уникальное имя ресурса в контейнере.$value
- вызываемая функция (Callable function) или строковое значение, которое будет возвращено при запросе ключа из контейнера$protected
- логический флаг, который защищает данный ресурс в контейнере от перезаписи. По умолчанию равенfalse
, установитеtrue
для защиты. Удобно для работы с сервисами.
Подробнее с методами Joomla DI контейнера можно познакомиться в libraries/vendor/joomla/di/src/Container.php
.
В нашем случае имя ресурса в контейнере - testconsole.start
. Значение - function (ContainerInterface $container) { return new TestCommand; }
- анонимная функция, возвращающая экземпляр класса команды. 3-й аргумент - true
- это параметр $protected
, защищающий данный класс команды от перезаписи.
Далее, в этой строчке кода происходит указание лоадеру CLI команд с помощью метода add()
добавить команду testconsole:test
, которая будет использовать значение ресурса DI-контейнера с именем testconsole.test
.
<?php
// add test command to joomla.php cli script
Factory::getContainer()->get(\Joomla\CMS\Console\Loader\WritableLoaderInterface::class)
->add('testconsole:test', 'testconsole.test');
После создания базовой файловой структуры (XML-манифест, файл плагина и сервис-провайдер) плагин можно установить. Не забудьте включить плагин в админке Joomla.
![](https://habrastorage.org/getpro/habr/upload_files/45e/47d/6a5/45e47d6a5d28240adcd19cb422f7a1fa.png)
Если в файле плагина с названиями ресурсов и/или команд допущены какие-то неточности - в консоли при попытке запуска команды увидите ошибку: The command "testconsole:test" does not exists. Для этого в консоли нужно перейти в директорию path/to/joomla/cli
и выполнить команду php joomla.php
.
![Часть статьи пишу на Windows-машине, поэтому на скриншоте консоль и пути отличаются от типичных серверных Часть статьи пишу на Windows-машине, поэтому на скриншоте консоль и пути отличаются от типичных серверных](https://habrastorage.org/getpro/habr/upload_files/9ee/64b/b4c/9ee64bb4cf4bf992694707750a390b91.png)
Файлы с классами команд для Joomla CLI
Сами файлы с командами и их классы лежат отдельно, рядом. Поскольку мы используем namespaces
- их местоположение внутри папки src
плагина может быть любым, сообразующимся с нашей логикой и здравым смыслом.
Расположение файлов
Вариант 1: папка с командами находится внутри src/Extension
, тогда namespace
классов команд для Joomla CLI должен быть Joomla\Plugin\Console\Testconsole\Extension\Command
.
![PHP классы команд находятся в папке Command плагина PHP классы команд находятся в папке Command плагина](https://habrastorage.org/getpro/habr/upload_files/718/c41/d86/718c41d86a87710d205b4e9bec626646.png)
Вариант 2: поднять папку Command на уровень вверх и расположить её рядом с папкой Extension. Тогда namespace
классов команд для Joomla CLI будет Joomla\Plugin\Console\Testconsole\Command
.
По большому счёту это абсолютно не важно и никак не влияет на работоспособность плагина и скорость его работы. Главное здесь - внятная логика и удобство поддержки расширения.
Класс команды
В нашем примере файлы классов команд находятся в src/Extension/Command
. Namespace классов команд будет: Joomla\Plugin\Console\Testconsole\Extension\Command
. Класс команды расширяет класс AbstractCommand
и должен иметь как минимум:
Свойство класса
$defaultName
, содержащее имя командыprotected static $defaultName = 'testconsole:test'
.Метод
doExecute()
, который выполняет собственно работу команды. Обычно здесь получается модель компонента черезMVCFactory
и производятся нужные манипуляции.
В документации и некоторых статьях говорится о том, что метод configure()
является обязательным, но на самом деле нет. Если в плагине этого метода нет - ошибки или Exeption не будет. Скорее речь идёт о строгой рекомендации к его наличию, что обязывает разработчиков расширений предоставлять хоть какую-то информацию и помощь по использованию для тех, кто будет пользоваться этим плагином.
Метод configure() CLI плагина Joomla 4 / Joomla 5
Метод configure()
, который служит для добавления аргументов команды и опций вида --optionName=optionValue
к команде, а также описания назначения самой команды. Поскольку в Joomla 4 / Joomla 5 используется библиотека от PHP-фреймворка Symfony, будет полезной ссылка на документацию Symfony Console Input (Arguments & Options), она же на русском языке.
<?php
/**
* Configure the command.
*
* @return void
*
* @since 4.0.0
*/
\defined('_JEXEC') or die;
protected function configure(): void
{
// Это описание команды будет видно в списке команд Joomla CLI
$this->setDescription("Test command 1 form plugin Testconsole");
$help = "<info>%command.name%</info> will demonstrate a simple Hello world function
\nUsage: <info>php joomla.php %command.full_name%</info>";
$this->setHelp($help);
}
Метод $this->setDescription()
принимает текстовое описание команды, которое видно в списке команд CLI Joomla. Это метод родительского класса \Joomla\Console\Command\AbstractCommand
, в котором можно посмотреть и некоторые другие полезные методы для класса команды.
Итак, если мы выполним в консоли команду php joomla.php
, то получим список доступных команд и их описаний.
![](https://habrastorage.org/getpro/habr/upload_files/de5/3e1/e23/de53e1e23c7a5e9dcfc077943b1534e3.png)
Для всех команд консоли Joomla есть общие параметры: --help
или -h
- отображение помощи и списка доступных опций/аргументов; или -q
, --quiet
- флаг, отключающий вывод в консоль сообщений команды и некоторые другие. При вводе нашей команды с опцией --help
мы увидим справочную информацию, которую указали в методе configure()
нашего класса команды.
![](https://habrastorage.org/getpro/habr/upload_files/9bb/e6d/151/9bbe6d1515fd7f63f0eb527074699571.png)
В коде справки можно использовать шорт-коды %command.name%
и %command.full_name%
, которые будут заменены на имя команды и полное имя команды в описании. Полное имя будет включать в себя "точку входа" в CLI - joomla.php
. %command.name%
будет равно в нашем случае testconsole:test
, а %command.full_name%
- joomla.php testconsole:test
.
Добавление опций и аргументов команды в методе configure()
Если работа команды зависит от вводных данных, то указать их можно с помощью опций команды вида --option=value
. Или укороченный вариант -o=value
. Для этого нужно указать какие именно опции мы ожидаем на вход и являются ли они обязательными или необязательными.
<?php
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
\defined('_JEXEC') or die;
/**
* Configure the command.
*
* @return void
*
* @since 4.0.0
*/
protected function configure(): void
{
// Это описание команды будет видно в списке команд Joomla CLI
$this->setDescription("Test command 1 form plugin Testconsole");
$help = "<info>%command.name%</info> will demonstrate a simple Hello world function
\nUsage: <info>php joomla.php %command.full_name%</info>";
$this->setHelp($help);
// Добавляем опции вида --param=value для команды
/**
* $name название опции
* $shortcut шорткат, краткий синтаксис опции. Может быть null, строкой шорткатов, разделенных | или массивом шорткатов
* $mode режим опции:
* InputOption::VALUE_NONE - не принимать опцию. По умолчанию.
* InputOption::VALUE_REQUIRED - обязательная опция.
* InputOption::VALUE_OPTIONAL - необязательная опция.
* InputOption::VALUE_IS_ARRAY - опция может быть массивом (значения через пробел)
* InputOption::VALUE_NEGATABLE - опция может иметь положительное или отрицательное значение (--ansi или --no-ansi)
* $description Текстовое описание опции
* $default Значение по умолчанию (должно быть null для InputOption::VALUE_NONE)
*/
$this->addOption('option', 'o', InputOption::VALUE_OPTIONAL, 'interesting description for our option', null);
// Добавляем аргумент для команды
/**
* $name название аргумента
* $mode режим аргумента
* InputArgument::REQUIRED - обязательный
* InputArgument::OPTIONAL - опциональный
* $description Текстовое описание опции
* $default Значение по умолчанию (только для InputArgument::OPTIONAL)
*/
$this->addArgument('argument', InputArgument::OPTIONAL, 'interesting description for our argument', null);
}
![](https://habrastorage.org/getpro/habr/upload_files/87e/ae7/82a/87eae782a0c6ab4739c07e709ebc7037.png)
Теперь у нашей команды есть возможность указывать аргументы и опции. Набрав в консоли команду php joomla.php testconsole:test -o=any_value argument
, мы сможем использовать эти данные при работе в основном методе плагина doExecute()
. Помним, что для опций возможны 2 варианта синтаксиса. В полном варианте команда выглядит так: php joomla.php testconsole:test --option=any_value argument
.
Метод doExecute() консоли Joomla 4 / Joomla 5
Этот метод - "рабочая лошадка" нашего плагина команды, выполняет всю необходимую работу. Метод получает на вход 2 аргумента функции:
Symfony\Component\Console\Input\InputInterface $input
Symfony\Component\Console\Output\OutputInterface $output
Можно ещё раз обратиться к документации Symfony (Console Input (Arguments & Options), Console Commands), которая хорошо дополнит эту статью.
Метод doExecute()
должен вернуть целое число int
- результат работы команды. Можно вернуть вручную 0
в случае успеха, 1
в случае ошибки или 2
в случае неверного использования опций и/или аргументов команды. Но более правильный путь использовать для этого константы класса Command
.
$input
служит для получения аргументов и опций команды, которые мы задали в методе configure()
. $output
служит для вывода данных в консоль.
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
\defined('_JEXEC') or die;
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
// Получаем опцию из командной строки
$option = $input->getOption('option');
// Проверяем полученную опциюф
if($option !== 'any_data')
{
// Неверное значение $option, опечатка
return Command::INVALID;
}
// Здесь основная работа команды
/** @var $result bool result status **/
$result = $this->mainTask();
if($result)
{
// Всё хорошо
return Command::SUCCESS;
}
// Всё плохо
return Command::FAILURE;
}
В функцию mainTask()
можно вынести часть основной работы или всю. Обычно работа идёт с данными каких-либо компонентов. Посмотрим небольшой пример работы с моделью кэша из консольной команды (файл libraries/src/Console/CleanCacheCommand.php
)
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
\defined('_JEXEC') or die;
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$symfonyStyle = new SymfonyStyle($input, $output);
// Пишем в консоль приветствие
$symfonyStyle->title('Cleaning System Cache');
// Получаем модель из MVCFactory
$cache = $this->getApplication()->bootComponent('com_cache')->getMVCFactory();
/** @var \Joomla\Component\Cache\Administrator\Model\CacheModel $model */
$model = $cache->createModel('Cache', 'Administrator', ['ignore_request' => true]);
// Проверям наличие аргумента expired
// и удаляем только протухший кэш
if ($input->getArgument('expired')) {
if (!$model->purge()) {
$symfonyStyle->error('Expired Cache not cleaned');
// Что-то пошло не так
return Command::FAILURE;
}
$symfonyStyle->success('Expired Cache cleaned');
// Всё хорошо.
return Command::SUCCESS;
}
// Чистим весь кэш
if (!$model->clean()) {
$symfonyStyle->error('Cache not cleaned');
// Что-то пошло не так
return Command::FAILURE;
}
$symfonyStyle->success('Cache cleaned');
// Жизнь прекрасна!
return Command::SUCCESS;
}
SymfonyStyle: общение с пользователем в консоли, оформление вывода информации
С помощью класса Symfony\Component\Console\Style\SymfonyStyle
можно запрашивать у пользователя данные, необходимые для работы, задавать вопросы. Файл класса расположен в libraries/vendor/symfony/console/Style/SymfonyStyle.php
, можно посмотреть доступные методы в нём или же лучше обратиться к официальной документации Symfony по этому классу.
В качестве простого примера приведем запрос значения и последующий его вывод обратно в консоль.
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Эта функция вызыватся ДО выполнения основного метода doExecute().
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
*
* @since version 1.0.0
*/
protected function initialise(InputInterface $input, OutputInterface $output): void
{
$symfonyStyle = new SymfonyStyle($input, $output);
/**
* Пример из плагина Jiji, указанного в списке литературы.
*
* $question string Вопрос для пользователя
* $default string Значение по умолчанию. Отображается для пользователя тоже.
* $validator callable Callback-функция, которую можно использовать в качестве валидатора данных
*
*/
$this->name = $symfonyStyle->ask('What is your name?', 'Super joomler', function ($value) {
if (empty($value) || !is_string($value))
{
throw new \RuntimeException('Your name cannot be empty. Are you a robot?');
}
return $value;
});
parent::initialise($input, $output);
}
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
// Если мы хотим использовать Simfony Style в консоли, то создадим его
$symfonyStyle = new SymfonyStyle($input, $output);
/** @var $this->name string Это запрошенное нами в методе initialise() имя */
$output->write($this->name);
// или аналог $symfonyStyle->text($this->name);
// Получаем опцию из командной строки
$option = $input->getOption('option');
// Проверяем полученную опциюф
if($option !== 'any_data')
{
$symfonyStyle->warning('Указан неверный параметр для опции option');
// Неверное значение $option, опечатка
return Command::INVALID;
}
// Здесь основная работа команды
/** @var $result bool result status **/
$result = $this->mainTask();
if($result)
{
$symfonyStyle->success('Всё хорошо, работу завершили.');
// Всё хорошо
return Command::SUCCESS;
}
$symfonyStyle->error('Всё плохо, скрипт не отработал. Жизнь - тлен.');
// Всё плохо
return Command::FAILURE;
}
![](https://habrastorage.org/getpro/habr/upload_files/f5e/73e/a4f/f5e73ea4f0789177b56237b6d77b12b2.png)
Полезными в работе могут оказаться методы SymfonyStyle:
askHidden
- например для запроса паролейchoice
- выбор из предложенных вариантовconfirm
- вариант вопроса
Ну и регулярно используемые методы write
- вывод строки. И writeln
- вывод с новой строки.
Так же в качестве примера на скриншоте вывел данные в консоль в виде таблицы. Для этого потребуется всего 2 массива: с заголовками таблицы и массив с данными, где первый уровень вложенности - rows
, а второй - columns
.
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$symfonyStyle = new SymfonyStyle($input, $output);
$headers = ['Column 1', 'Column 2'];
$rows = [
['Value 1','Value 2'],
['Value 1','Value 2'],
['Value 1','Value 2'],
];
$symfonyStyle->table($headers, $rows);
return Command::SUCCESS;
}
Другим примером полезной интерактивности в Joomla CLI может быть ProgressBar
(документация Simfony). К примеру, мы получили некий массив данных и для каждого элемента массива выполняем обработку. Полезно отслеживать прогресс её выполнения.
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$symfonyStyle = new SymfonyStyle($input, $output);
// Создаём ProgressBar. Можно указать параметр int $max
$symfonyStyle->createProgressBar();
// Можно не создавать ProgressBar строчкой выше, а сразу сделать progressStart(),
// и уже ему указать параметр int $max
$symfonyStyle->progressStart();
// Это просто для красоты, для самих себя
$symfonyStyle->note('Start to iterate');
$limit = 100;
$i = 0;
while ($i < $limit)
{
// Бесполезная демо-прокрастинация. Ворочаем здесь массивом,
// двигаем Progressbar вперёд
$symfonyStyle->progressAdvance();
sleep(1);
$i++;
}
// Всё закончилось.
$symfonyStyle->progressFinish();
return Command::SUCCESS;
}
Вот так это будет выглядеть в консоли.
Заключение
Статья пригодится тем, кто с Joomla с помощью CLI ещё не работал. Для опытных разработчиков в ней вряд ли нашлось что-то новое, особенно знакомых с Symfony. Но, тем не менее, мы теперь знаем, что "Joomla так может". Надеюсь, что кому-то этот небольшой мануал будет полезен.
Также с удовольствием приму аргументированные дополнения и замечания к статье от более опытных товарищей.
Полезные ресурсы Joomla
Ресурсы сообщества:
https://vc.ru/s/1146097-joomla - Сообщество Joomla на VC.
Telegram:
Вакансии и предложения работы по Joomla: фуллтайм, частичная занятость и разовые подработки. Размещение вакансий здесь.