Консольные команды в плагине webasyst и Shop-Script 5/6 в частности

В документации по фреймворку webasyst описано, как создавать консольные команды, то есть команды которые можно вызывать из консоли, без участия веб-сервера.
Статья справки размещена здесь: www.webasyst.ru/developers/docs/features/cli/
Минус описанного в ней метода в том, что скрипт придётся помещать в папку wa-apps/[APP_ID]/lib/cli, то есть фактически в папку с приложением класть свой код, что приведёт в конечном итоге к бардаку и хаосу.

Также, в справочной системе есть статья о плагинах, где написано, что плагины тоже могут иметь консольные команды.
вот эта статья: www.webasyst.ru/help/98/shop-script-5-plugin-development/

Однако, как совместить консольные скрипты и плагины – не сказано.
Поскольку ждать ответа от техподдержки можно долго, я предпочел самостоятельно расковырять исходный код, и найти как поместить консольный скрипт в свой плагин.

Предположим, что у вас уже есть свой плагин, записанный в соответствующее приложение (в моём случае это shop).
Плагин может быть совсем пустым, но он должен “восприниматься” приложением, и быть включённым в настройках.

Я пишу плагин для синхронизации базы товаров с товарной номенклатурой поставщика, который предоставляет доступ к своей базе данных, поэтому плагин называется GoodsSyncronizerPlugin, и лежит он в папке /wa-apps/shop/plugins/goodsSync/

Для того, чтобы заработал мой консольный скрипт понадобилось проделать следующее:

1. Создать папку /wa-apps/shop/plugins/goodsSync/lib/cli – на самом деле, это не обязательно, но чтобы придерживаться общей концепции фреймворка, лучше складывать файлы по их назначению в соответствующие папки. В действительности, при загрузке ваших файлов классов, система ищет их рекурсивно в папках /wa-apps/[APP_ID]/lib, /wa-apps/[APP_ID]/plugins и /wa-apps/[APP_ID]/api, так что где бы вы ни положили ваши файлы – лишь бы они были где-то внутри этих папок, в нашем случае – внутри папки с плагином.

2. Создать класс, наследованный от waCliController shopGoodsSyncronizerCronCli, причём имя класса должно состоять из: [APP_ID][ВашеНазвание]Cli, где “ваше название” по возможности должно быть однозначным, или даже включать в себя имя плагина, в противном случае, оно может совпасть с другим таким-же названием класса чужого плагина. [APP_ID] – это имя вашего плагина, а “Cli” – обязательное окончание (Слово Cron не обязательно, оно является частью моего название, потому что мне показалось правильным его вписать).
Имя файла в данном случае: shopGoodsSyncronizerCron.cli.php, то есть оно строится по примерно такой же схеме, а именно: [APP_ID][Ваше название].cli.php

3. Прописать в классе единственно необходимую функцию execute(), а в ней, для проверки вписать что-то для проверки работоспособности. Например:

<?php 
 
class shopGoodsSyncronizerCronCli extends waCliController
{
  public function execute()
  {
    die("Выполнился скрипт GoodsSyncronizerCron")
  }
}

Для проверки вызываем скрипт из консоли:

$ php /var/www/localsite/public_html/cli.php shop GoodsSyncronizerCron

После выполнения должна вывестись наша строка “Выполнился скрипт GoodsSyncronizerCron”

Параметры:

При вызове скрипта из консоли можно передавать ему параметры, которые станут доступны в массиве, возвращаемом waRequest::param() (вызывать без параметров чтобы получить весь массив)

1) Параметры имеющие только значения:
После имени класса консольной команды просто указываем слова, каждое из которых станет значением параметра. Ключи параметров будут числовыми.
Например, при выполнении:

$ php /var/www/localsite/public_html/cli.php shop GoodsSyncronizerCron the answer is 42

Функция waRequest::param() вёрнёт вам:

array(
  0 => "the",
  1 => "answer",
  2 => "is",
  3 => "42"
)

2) Параметры ключ-значение:
При выполнении команды:

$ php /var/www/localsite/public_html/cli.php shop GoodsSyncronizerCron --answer 42 -question unknown

Функция waRequest::param() вёрнёт вам:

array(
  "answer" => "42",
  "questiong" => "unknown"
)

(При этом, параметр, означающий ключ, должен начинаться с 1 или 2-х минусов.)

3) Комбинированные параметры (и то и другое):
Оба метода можно свободно комбинировать:
При выполнении команды:

$ php /var/www/localsite/public_html/cli.php shop GoodsSyncronizerCron --answer 42 and question unknown

Функция waRequest::param() вёрнёт вам:

array(
  "answer" => "42",
  0 => "and",
  1 => "question",
  2 => "unknown"
)

Определённо, что параметры задаваемые через ключ-значение удобнее простых параметров тем, что к ним можно обращаться прямо по ключу, например так:

echo waRequest::param("answer");

Параметры же с числовыми ключами – придётся искать через array_search или in_array

Дополнительно: отладка cli с помощью xdebug

Я привык пользоваться удобными инструментами разработки, вколючающими Netbeans IDE и Xdebug для отладки скриптов на PHP. Но консольный скрипт cli.php не запускается из браузера, сообщая “Run from CLI only!”.

Для того, чтобы отлаживать мои консольные скрипты в браузере (ведь для отладки через XDebug в консоли придётся полностью менять настройки Netbeans (если такая его настройка вообще возможна)) я подправил файл wa-system/cli.php, чтобы он больше не ругался на то что запущен из браузера.

Вот какие строчки не давали этому скрипту запускаться где-то кроме консоли:

if (PHP_SAPI !== 'cli') {
  die('Run from CLI only!');
}

Заменим этот блок на вот такой:

if (PHP_SAPI !== 'cli') {
	// Здесь должно быть указано имя вашего локального домена
	if ($_SERVER['SERVER_NAME']=='localsite')
	{
		//Если не переданы важные параметры запуска - запросим их
		if (!isset($_GET['app']) || !isset($_GET['class']))
		{
			die("<form>" .
				"APP_ID:<input type='text' name='app'><br>" .
				"Class:<input type='text' name='class'><br>" .
				"Parameters:<input type='text' name='params'>(divided by space)<br>" .
				"<input type='submit'></form>");
		}
		else
		{
			//получаем параметры из поля 'params' и разбиваем его на отдельные параметры пробелами
			$params = isset($_GET['params']) ? explode(" ", $_GET['params']) : array();
			//Формируем "фиальшивые" параметры командной строки
			$_SERVER['argv'] = array_merge(
						array("cli.php",
							$_GET['app'],
							$_GET['class']), $params);
 
			//откроем стандартный вывод чтобы все ошибки попадали не в лог сервера
			//а прямо в выдачу браузера
			define("STDERR", fopen("php://output","w"));
		}
		$_SERVER['argc']=count($_SERVER['argv']);
	}
	else
	{
		//Сервер оказался не локальный, всё-таки умраем
		die('Run from CLI only!');
	}
}

Теперь, при открытии в браузере адреса http://localsite/cli.php вы увидите примерно это:
Форма для ввода параметров
В поле APP_ID нужно ввести ваше приложение, например “shop”, а в поле class – название класса, в моём случае это GoodsSyncronizerCron.
В поле Parameters нужно ввести все параметры, которые нужно передать в вашу функцию run() разделяя их пробелами. При вызове run() массив этих параметров будет передан в параметре $params.

P.S. Конечно, ориентация на имя локального веб-сервера – не самое лучшее решение, и идеально было бы использовать системную настройку debug (т.е., если включена отладка, то даём запустить скрипт в браузере, а если нет – то нет), но изменение скрипта специально сделано “на поверхности” без влезания в дальнейшую логику, которая собственно загружает основной скрипт приложения, и из него-то уже и можно узнать, идёт ли отладка (с помощью вызова isDebug()).


So, what do you think ?