В списке официальных модулей Kohana наконец-то прибавление. RC1 для Kohana 3.3 содержит новый модуль Minion. Он предназначен для выполнения задач через CLI (обычно это задачи для Cron‘а).
Подготовка
Для начала необходимо настроить модуль для работы.
- К сожалению, релиз RC1 содержит ссылку не на последнюю версию модуля Minion, поэтому необходимо вручную обновить модуль до последнего коммита (т.е. до HEAD). Это делается командой
cd modules/Minion && git pull origin 3.3/develop
. - Разрешаем загрузку модуля Minion в файле
bootstrap.php
(он там уже по умолчанию есть, но закомментирован). - Копируем файл
minion
изMODPATH/minion/
в нужное место (т.е. туда, откуда он будет запускаться планировщиком задач). Можно и не копировать. Затем вносим в него правки, чтобы переменные$application
,$modules
и$system
указывали в правильные места (можно скопировать их изindex.php
, если вы решили его положить вDOCROOT
).
Чтобы проверить, правильно ли все настроено, запустите из терминала команду:
./minion --help
Она должна вывести краткую информацию о модуле Minion, а также список доступных для выполнения задач.
Задачи в Minion
Самый простой способ понять этот механизм — создать простейшую задачу. По традиции реализуем вывод фразы «hello world!«:
// файл APPPATH/classes/Task/Welcome.php <?php defined('SYSPATH') or die('No direct script access.'); /** * Its a test task class * * @author biakaveron */ class Task_Welcome extends Minion_Task { /** * Test action * * @param array $params * @return void */ protected function _execute(array $params) { Minion_CLI::write('hello world!'); } } |
Вот и все. Если запустить команду ./minion --task=welcome
, то в ответ увидим строку «hello world!».
Каждая задача выполняется в виде отдельного класса-потомка Minion_Task
. За выполнение задачи отвечает метод _execute($params)
.
Передача параметров
В методе _execute()
присутствует аргумент $params
, который будет содержать все параметры, переданные в задачу через консоль. Все, кроме имени задачи (ключ --task
) и специального ключ --help
(о нем расскажу позже). Например, ./minion --task=welcome --foo=bar
означает передачу параметра ‘foo‘ со значением ‘bar‘.
Имена параметров (в том числе и
task
) регистрозависимы, поэтому./minion --Task=welcome
и./minion --tasK=welcome
приведут к ошибке. Кроме того, имена задач тоже должны быть в нужном регистре (допускается полностью нижний регистр в имени). Например,./minion --task=welcome
и./minion --task=Welcome
сработают, а./minion --task=welcomE
нет.
Если вы попробовали запустить задачу Welcome
с дополнительными параметрами, то должны были получить сообщение об ошибке Kohana. Дело в том, что необходимо для каждой задачи явно описывать допустимые параметры, а также их значения по умолчанию. Таким образом, новая версия задачи Welcome
будет выглядеть примерно так:
<?php defined('SYSPATH') or die('No direct script access.'); /** * Its a test task class * * @author biakaveron */ class Task_Welcome extends Minion_Task { protected $_options = array( // param name => default value 'foo' => 'beautiful', ); /** * Test action * * @param array $params * @return void */ protected function _execute(array $params) { Minion_CLI::write('hello '.$params['foo'].' world!'); } } |
Теперь ./minion --task=Welcome
выведет «hello beautiful world» (использовано значение по умолчанию), а ./minion --task=Welcome --foo=great
напишет «hello great world«.
Ввод и вывод данных
Класс Minion_CLI
содержит необходимые методы для работы с потоками ввода и вывода. Как правило, действия направлены на запрос у пользователя данных и вывод их обратно.
Чтение из потока
Используйте метод Minion_CLI::read($text = '', array $options = NULL)
:
// просто прочитать 1 строку Minion_CLI::read(); // вывести строку 'Введите имя' и считать ответ Minion_CLI::read('Введите имя'); // вывести строку 'Ваш пол [ м, ж ]' и считать ответ. Если это не 'м' или 'ж', то // Minion будет выдавать сообщение 'This is not a valid option. Please try again.' до получения корректного результата. // Буквы в фильтре регистрозависимы, т.е. значение 'М' считается некорректным! Minion_CLI::read('Ваш пол', array('м', 'ж')); |
Запись в поток
Метод Minion_CLI::write($text = '')
позволяет отправить сообщение. Переменная $text
может быть массивом, в таком случае каждый элемент будет выведен с новой строки.
// выведет всем известную фразу Minion_CLI::write('Hello world!'); // покажет строчки с известным стихотворением Minion_CLI::write(array('Жили у бабуси', 'Два веселых гуся')); // перейдет на следующую строчку Minion_CLI::write(); |
Еще есть метод Minion_CLI::write_replace($text = '', $end_line = FALSE)
, который затирает предыдущую строку новым сообщением. Это может быть полезно, скажем, для имитации загрузки. Флаг $end_line
стоит использовать для «фиксации» строки (будет добавлен конец строки):
Minion_CLI::write_replace('h'); Minion_CLI::wait(1); Minion_CLI::write_replace('he'); Minion_CLI::wait(1); Minion_CLI::write_replace('hel'); Minion_CLI::wait(1); Minion_CLI::write_replace('hell'); Minion_CLI::wait(1); Minion_CLI::write_replace('hello', TRUE); |
Данный пример выведет слово ‘hello‘ постепенно, как бы с эффектом печатающей машинки. Minion_CLI::wait(1)
, как можно догадаться, означает паузу в 1 секунду.
Валидация
По умолчанию все переданные параметры проверяются только на их наличие в списке разрешенных. Такое поведение можно легко изменить с помощью метода build_validation(Validation $validation)
. Он подготавливает объект Validation
для дальнейшей проверки параметров. Вот так он выглядит в классе Minion_Task
:
public function build_validation(Validation $validation) { // Add a rule to each key making sure it's in the task foreach ($validation->as_array() as $key => $value) { $validation->rule($key, array($this, 'valid_option'), array(':validation', ':field')); } return $validation; } |
Каждый переданный параметр проверяется с помощью метода valid_option()
, который просто ищет имя параметра в списке разрешенных. Если мы захотим ограничить поле foo
длиной в 10 символов, то необходимо будет создать в задаче Welcome
метод build_validation()
:
public function build_validation(Validation $validation) { return parent::build_validation($validation) ->rule('foo', 'max_length', array(10)); } |
Не забывайте вернуть объект Validation!
А вот пример для задачи без ограничений по параметрам:
public function build_validation(Validation $validation) { return $validation; } |
Я просто не стал добавлять никаких правил (в том числе и определенных в классе-родителе). Теперь явно указывать параметры задачи в свойстве $_options
необязательно.
Справка по задачам
Модуль имеет зарезервированный ключ --help
, по которому он отображает справку по задаче (если указана в параметре --task
) или по модулю в целом (мы это видели после команды ./minion
). Так, вызов ./minion --task=Welcome --help
для нашей задачи Welcome
вернет такой текст:
Usage ======= php minion.php --task=welcome [--option1=value1] [--option2=value2] Details ======= Author: biakaveron Description =========== Its a test task class
Легко понять откуда взялась такая информация — из PHPDOC к классу Task_Welcome
. Таким вот образом разработчики Kohana облегчают работу, позволяя разом убивать двух зайцев — документировать исходники и писать man для задач.
Альтернативный запуск задач
Запускать задачи можно и без использования скрипта minion
. Любые CLI-команды будут перенаправлены к модулю Minion, поэтому вместо ./minion --task=Welcome
можно использовать php index.php --task=Welcome
.
Заключение
Очень полезный модуль. Однако он, как и вся ветка 3.3, еще находится в стадии доработки, пока что имеются недочеты. Используйте его с осторожностью.
отличный модуль, как раз недавно использовал его.
Спасибо за обзор, было интересно узнать о таком модуле.
Вообще, как по мне, немного не хватает нормальных обзоров хороших модулей для различных задач.
kohana-modules — какая-то помойка. Ни описания толкового, ничего кроме ссылки на гитхаб.
А вот так вот, чтоб с чувством, с толком, с расстановкой.. Этого не хватает
Спасибо вам
Здравствуйте автор поста. Скажите, данный модуль можно прикрутить к Kohana 3.1? Или он будет работать только с 3.3
@Илья
Можно.
1. Переименовываем все файлы директории
classes
в нижний регистр2. Если хотим, чтобы работал help, то правим в файле
classes/task/help.php
Kohana::list_files('classes/Task')
наKohana::list_files('classes/task')
3. Не забываем добавить модуль в
Kohana::modules()
4. Проверяем, вызов
./minion --task=help
должен работать.Cheers I would suggest you replace the `echo` in the examples with Minion_CLI::write() so people are made aware of the proper way to output text in CLI tools
Article has been updated. Thank you, Zeelot!
Шикарно!
Вы как всегда — на высоте. Так держать
cd modules/Minion && git pull origin 3.3/develop.
У меня Кохана не с гита. Как скачать последнюю версию?
А вот отсюда: https://github.com/kohana/kohana/tree/3.3/develop
з.ы. у вас openid не работает. меня не пускает с livejournal.
Всегда можно пользоваться либо текущей разрабатываемой (ветка develop), либо стабильной (ветка master и созданные тэги) версией. На Гитхабе это найти достаточно просто, там удобный интерфейс. Другое дело, что без Гита придется пройтись по всем модулям и скачать их вручную.
Он там вынесен, можно clone https://github.com/kohana/minion.git
Я вот пробую к 3.2 прикрутить. Запускаю в консоли ./minion —help или любой таск, получаю в консоль html главной страницы о_О
> Он там вынесен, можно clone https://github.com/kohana/minion.git
Как и любой другой модуль Kohana. Я про использование web-GUI от Гитхаба.
> Запускаю в консоли ./minion —help или любой таск, получаю в консоль html главной страницы о_О
Честно говоря, времени пока не было даже чтобы релизнутую 3.3 покрутить, а уж про портирование на 3.2 даже не задумывался ))
В этой теме есть версия для 3.2, но и там не все гладко
http://forum.kohanaframework.org/discussion/10947/cli-detection-and-base_url-in-bootstrap
задал там вопрос
Я таки прикрутил
http://seyferseed.ru/php/modul-minion-s-kohana-3-3-na-kohana-3-2.html
В 3.2 я запускаю задачи просто
index.php --uri=controller/action/param
. Вроде все прекрасно работает.Скажите, что мне даст использование дополнительно модуля Minion?
1. Не придется в коде смешивать стандартные контроллеры и CRON-контроллеры с проверками типа if (Kohana::$is_cli). Суп отдельно, мухи отдельно.
2. Задачи в Minion позволяют поддерживать большое количество параметров, которые сложно предусмотреть в роутинге. Передавать через GET можно, но не так удобно.
3. Зачем тратить время на HTTP-шные вещи типа заголовков, запрос/ответ и тд? Таски в Minion «заточены» под выполнение конкретных задач.
4. Поддержка —help позволяет не ковырять исходники Вам или коллегам, которые будут сопровождать проект. Мелочь, а приятно.
И тд.
Спасибо, за подробный ответ.
Для себя понял, что дополнительный модуль мне пока не нужен )
Иван, спасибо за обзор.
Единственное, что я не понял — это для чего убрали Kohana::$is_cli. Хотел сделать подключение модуля в зависимости от окружения, и не нашел свойства is_cli. Будем по-старинке PHP_SAPI проверять =)
Необходимый модуль можно подключать прямо в таске. Возможно, это будет даже удобнее, чтобы не подключать его для всех тасков
>> Необходимый модуль можно подключать прямо в таске
Это понятно, я имел в виду подключение самого Minion’а
А, ну тогда только PHP_SAPI, ага
С этим то понятно. Не понятно как, например, вызвать метод из модели.
protected function _execute(array $params)
{
Model::factory('City')->get_regions();
}
Вызывает ошибку:
Call to undefined function mysql_connect();
Запускаю задачу так:
%php% -f C:\OpenServer\domains\mycohanasite\index.php — —task=welcome
Давно не работал с php под Windows, но вероятно проблема аналогична http://stackoverflow.com/questions/181222/php-mysql-connect-wont-work-via-command-line
Добавил метод валидации к задаче, теперь не запускается:
php minion --task=welcome --foo=good
Выдает ошибку:
ErrorException [ 2 ]: Missing argument 2 for Kohana_Valid::max_length() ~ SYSPATH\classes\Kohana\Valid.php [ 61 ]
Очевидно, что правило max_length описано не полностью. Там должно быть 2 параметра.
@biakaveron, да, вот такое правильно нормально все выводит:
->rule('foo', 'max_length', array(':value',10));
А как необходимый модуль подключать прямо в таске? Что то я нигде не нашел…
Как и при обычном запросе — вызвать Kohana::modules(Kohana::modules() + array(‘foo’ => MODPATH . ‘path/to/new/module’))