Как стать автором
Обновить

Добавление своих команд для CLI в Joomla 4 и Joomla 5 с помощью плагина

Время на прочтение18 мин
Количество просмотров852

В Joomla, как и во многих других PHP фреймворках и движках, уже довольно давно (с версий 1.7.x) существует возможность работы с помощью CLI - Command line interface. Конечно, вряд ли в эту статью попадут люди совсем не знакомые с консолью сервера и командной строкой, но для справки оговорюсь, что при работе с Joomla через CLI не затрагиваются ресурсы веб-сервера (Apache, Nginx etc.), запускается отдельный процесс php (или несколько отдельных), в котором не действуют ограничения на время выполнения скрипта веб-сервера (чаще всего это 60 секунд).

Поэтому с помощью CLI решают те задачи, решение которых в веб-интерфейсе (из админки) Joomla нецелесообразно из-за возможных ограничений веб-сервера - так называемые "тяжелые" задачи. Это может быть миграция большого количества контента со старой версии Joomla на новую, синхронизация данных с внешним источником по API (синхронизация цен и остатков, выгрузка заказов), работа с файлами и каталогами, выполнение пакетных заданий на больших объёмах (десятки-сотни тысяч или даже миллионы позиций) и т.д. - всё то, что может занимать много времени и ресурсов сервера.

Оглавление

Часть команд CLI  ядра Joomla 5
Часть команд CLI ядра Joomla 5

Список литературы

  1. Прежде всего код ядра Joomla является сам по себе документацией. PHP-классы CLI команд ядра лежат в директории libraries/src/Console. Смотрим их, изучаем и используем в качестве образца.

  2. Статья Joomla 4: мощь CLI приложений: в ней вкратце описываются возможности работы с CLI в Joomla, используемый синтаксис консольных команд. А так же статью дополняет список команд компонента Akeeba Backup.

  3. Официальная документация Joomla. В ней вкратце описывается процесс создания плагина группы console для Joomla 4+ для добавления своих команд в CLI. Также в статье есть небольшое видео.

  4. Статья Создание WebCron плагина для Joomla 4 (Task Scheduler Plugin). В Joomla 4.1 появился планировщик задач - Task Scheduler. Можно написать плагин задачи и управлять параметрами и расписанием задачи через админку, а в CLI или в CRON вызывать только одну команду: php /path/to/site/public_html/cli/joomla.php scheduler:run. Это альтернативный путь решения задач данной статьи.

  5. Официальная документация Joomla CLI example - Onoffbydate и её перевод Пример написания CLI для Joomla 4 - Onoffbydate приводит пример создания плагина Joomla CLI, с помощью которого можно выключать / выключать модуль в зависимости от даты и времени года по CRON.

  6. Статья Jiji, your new Joomla! 4 Console friend в официальном журнале международного Joomla-сообщества Joomla Community Magazine. В статье показывается пример создания hello world. Поскольку сама статья без примеров кода, стоит посмотреть GitHub этого плагина, где есть простой пример работы с материалами Joomla, например, импорт статей Joomla из файла. Чем больше перед глазами примеров реализации, тем проще создать что-то своё.

  7. Статья Создание плагинов с учётом новой структуры 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.

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.

Плагины группы 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) должен возвращать пустоту, но аргументов на вход не принимает.

Добавление класса команды происходит следующим образом:

  1. Помещаем класс команды в Joomla DI-контейнер, указывая уникальное имя ресурса.

  2. Указываем лоадеру 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.

Если в файле плагина с названиями ресурсов и/или команд допущены какие-то неточности - в консоли при попытке запуска команды увидите ошибку: The command "testconsole:test" does not exists. Для этого в консоли нужно перейти в директорию path/to/joomla/cli и выполнить команду php joomla.php.

Часть статьи пишу на Windows-машине, поэтому на скриншоте консоль и пути отличаются от типичных серверных
Часть статьи пишу на Windows-машине, поэтому на скриншоте консоль и пути отличаются от типичных серверных

Файлы с классами команд для Joomla CLI

Сами файлы с командами и их классы лежат отдельно, рядом. Поскольку мы используем namespaces - их местоположение внутри папки src плагина может быть любым, сообразующимся с нашей логикой и здравым смыслом.

Расположение файлов

Вариант 1: папка с командами находится внутри src/Extension, тогда namespace классов команд для Joomla CLI должен быть Joomla\Plugin\Console\Testconsole\Extension\Command.

PHP классы команд находятся в папке Command плагина
PHP классы команд находятся в папке Command плагина

Вариант 2: поднять папку Command на уровень вверх и расположить её рядом с папкой Extension. Тогда namespace классов команд для Joomla CLI будет Joomla\Plugin\Console\Testconsole\Command.

По большому счёту это абсолютно не важно и никак не влияет на работоспособность плагина и скорость его работы. Главное здесь - внятная логика и удобство поддержки расширения.

Класс команды

В нашем примере файлы классов команд находятся в src/Extension/Command. Namespace классов команд будет: Joomla\Plugin\Console\Testconsole\Extension\Command. Класс команды расширяет класс AbstractCommand и должен иметь как минимум:

  1. Свойство класса $defaultName, содержащее имя команды protected static $defaultName = 'testconsole:test'.

  2. Метод 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, то получим список доступных команд и их описаний.

Для всех команд консоли Joomla есть общие параметры: --help или -h - отображение помощи и списка доступных опций/аргументов; или -q, --quiet - флаг, отключающий вывод в консоль сообщений команды и некоторые другие. При вводе нашей команды с опцией --help мы увидим справочную информацию, которую указали в методе configure() нашего класса команды.

В коде справки можно использовать шорт-коды %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);
  
}

Теперь у нашей команды есть возможность указывать аргументы и опции. Набрав в консоли команду 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;
}

Полезными в работе могут оказаться методы 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

Ресурсы сообщества:

Telegram:

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Всего голосов 6: ↑6 и ↓0+6
Комментарии7

Публикации

Истории

Работа

PHP программист
113 вакансий

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
11 сентября
Митап по BigData от Честного ЗНАКа
Санкт-ПетербургОнлайн
14 сентября
Конференция Practical ML Conf
МоскваОнлайн
19 сентября
CDI Conf 2024
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн