Работа с CRON и с консолью - energy-coresky/air GitHub Wiki

Задачи cron обычно находятся в main/cron.php, файл запускается раз в минуту, но время выполнения каждой задачи указывается непосредственно перед лямбда-функцией с помощью cron-синтаксиса. Задачи выполняются в отдельных потоках, возможно параллельно несколько сразу (обеспечивается с помощью функции PHP popen(..)), поэтому даже фатальные ошибки отдельной задачи не нарушают работу остальных задач и регистрируются в системе регистрации ошибок. Функционал реализован в классе Schedule см. файл main/w2/schedule.php. Пример файла cron.php:

<?php

define('START_TS', microtime(true));
#'cli' == PHP_SAPI or exit;
$argv[0] = __FILE__;
require __DIR__ . '/../bootstrap.php';

$sky  = new SKY;
$cron = new Schedule;
$cron
->at('0 2,3', fn() => $cron->visitors()) # run task at 2:00 and 3:00 every night
->at('30 5', fn() => $cron->mail_error())
->at('3', false, fn() => new This_task_dont_load_database) # second param is false
->turn(fn() => false)
->at('+', function() {
    # this task never run due to "turn/false"
})
->turn(fn() => true)
->at('+', function() {
    # this task run every times
})
->at('named', fn() => new Task)

# .. other tasks
?>

В консольных скриптах для создания центрального объекта, всегда используется класс SKY, в отличие от веб скриптов, где всегда используется класс HEAVEN, расширяющий класс SKY.

В задачах вместо глобальной функции sql(), можно использовать $cron->sql(), используя тот-же синтаксис, но вторая еще логирует свое исполнение в Cron-логе, например:

2018-05-01 00:02:01 [0] 0.001 sec <= 2423 <= delete from visitors where ...

Также, чтобы записать произвольную строку в лог, можно использовать $cron->write(..) или echo. Вне cron-скриптов, запись в Cron Log, всегда можно выполнить так: SKY::log('log some info').

С помощью функции $cron->mail_error() можно организовать задачу отсылки письма, если на продакшн, появились новые ошибки и они не просмотрены в логе ошибок.

Первым параметром Schedule::at(..), вместо Cron-расписания, можно указать имя расписания, которое должно начинаться с латинской буквы, смотрите крайнюю задачу выше. В этом случае, расписание будет располагаться в файле main/cron.times в формате "Bang", который можно редактировать из Root-Admin раздела, т.е. изменять расписание на продакшн из веб-интерфейса. Если имя расписания в этом файле не найдено, первоначально будет назначено '-' - отключенная задача. Для именованных задач, в Cron-логе, автоматически указывается имя, кроме номера задачи в цепочке лямбда функций.

Если задача нормально выполнилась и не сделала запись в лог, также как и не сделала echo, она не будет отмечена ни в каких логах. Хотя любая крайняя задача, которая нормально выполнилась всегда отображается на вкладке Main/Overview в строке "Cron layer last tick". Это не касается только задач, которые выполнились не создавая соединение с базой данных. Если нужно, чтобы выполнение Cron-задачи отмечалось в логе, сделайте в теле задачи хотя-бы echo '-';.

Запуск CRON-задач

Стандартная строка запуска в crontab:

* * * * * php /path-to-project/main/cron.php @ 2>&1

Здесь амперсанд перенаправляет весь вывод из stdout в крон лог. Во время разработки, задачи можно запускать так: php cron.php, в этом случае, весь вывод в stdout и stderror будет в консоле.

Для того, чтобы задача гарантированно запустилась при любом вызове php cron.php, необходимо первым символом расписания, указать + (плюс). Чтобы полностью отключить задачу (но при этом не затереть ее расписание), можно первым символом указать - (минус).

Для имитации использования Unix утилиты flock, можно первым символом расписания указать ! (восклицательный знак). Такие запущенные задачи, записывают файл с временем запуска var/cron/task_N (N - цифра, номер задачи в цепочке лямбда функций) и после окончания выполнения удаляют его.

Cron для Windows

Можно настроить планировщик задач Windows, чтобы он работал подобно программе Cron Unix систем. Чтобы запуск происходил в фоновом режиме, настройте планировщик Windows для запуска файла Visual Basic:

Dim WinScriptHost
Set WinScriptHost = CreateObject("WScript.Shell")
WinScriptHost.Run Chr(34) & "C:\path-to-file\cron.bat" & Chr(34), 0
Set WinScriptHost = Nothing

При этом файл cron.bat примерно такой:

php -f C:\path-to-sky-project\cron.php @ 2>&1

Выполнение задач в едином потоке

Иногда функция PHP popen() может быть отключена по соображениям безопасности у некоторых хостинг провайдеров. В этом случае выполнение задач будет происходить последовательно в одном процессе PHP (для задач, запуск который совпал на одно и то-же время).

Задать выполнение задач в одном процессе, можно также в конструкторе класса Schedule, хотя это не очень хорошая идея - предпочтительно использовать возможности popen().

Организовать выполнение задач в одном потоке можно также с помощью HTTP запросов:

* * * * * curl http://example.net/proxy.php

Где файл proxy.php выглядит примерно так:

<?php

'ip_addr' == $_SERVER["REMOTE_ADDR"] or exit; # где ip_addr - ip адрес сервера

require '../main/cron.php';

Методы для организации цепочки задач

Единственный метод для определения задач - ->at(..). Первый параметр - расписание. Второй параметр, если false - доступ к БД не инициируется, если true или второй параметр Closure - доступ к БД инициируется и задача может использовать главное соединение к БД. Ежеминутный запуск файла cron.php, сам по себе, также не инициирует доступ к базам данных. Внутри последнего параметра, Closure, описывается код задачи. Можно в параметре лямбда функции указать переменную, которая будет содержать номер задачи в цепочке (нумерация начинается с нуля). Расписание задачи может содержать имя расписания, читайте об этом выше.

Второй метод, который может участвовать в построении цепочки задач - ->turn(). Если в этом методе (его лямбда функции) вернуть false, то все задачи после него в цепочке не будут выполняться. Однако можно указать второй ->turn(), который разрешит (если вернет true) выполнение задач в дальнейшей цепочке.

Конфигурационные переменные класса Schedule

Класс Schedule имеет собственный реестр переменных, который организован с помощью ghost SQL. Значения сохраняются в memory.9 (стандартная таблица memory, ряд с ID равный 9). Переменные можно посмотреть в консоли sky m 9 или изменить: sky eval "SKY::n('clear_nc', null);". Для доступа к переменным, нужно использовать префикс n_, объект - $cron или $sky:

$cron->n_some_var = 'test'; # write var to database
echo $sky->n_some_var; # read var from database

Класс Console и скрипт sky

В CORESKY имеется консольный скрипт sky и "батник" для Windows sky.bat. В классе Console имплементировано для него несколько базовых команд:

>sky
Usage: sky command [param ...]
Commands are:
  a              Show top-view actions (routes)
  app _test      Test without DB
  app test       This is just test
  c              Show controllers
  d              List dirs (from current dir)
  drop           Drop all cache
  e              Search for errors using all possible methods
  eval           Eval PHP code, example: sky eval "echo $sky->s_online;"
  fr             Write "first run" into index.php
  g              Check globals
  gate           Write default gate.php
  m              Read tmemo cell from $_memory
  master         Push ware `venus` to remote origin master
  php            Lint PHP files (from current dir)
  rewrite        Write default rewrite.php
  s              Run PHP web-server
  sql            Execute SQL, example: sky sql "+select 1+1" [con-name] [ware]
  v              Show Coresky version
  venus base     Work with database
  venus caret    Friend classes
  venus im       Debug Maxwell indexes
  venus iv       Debug Vesper index
  venus parse    Parse css
  venus s        Search (list) Vesper css classes
  venus t        Show Venus tables
  venus test     Test Maat parser
  venus tw       Generate Vesper(Tailwind) classes
  w              List installed wares
  warm           Warm all cache
Coresky app: βῆτα.0.788.AB.SKY. (ab.sky)
Coresky ware: venus
Repository: https://github.com/energy-coresky/venus.git

SKY-приложения могут расширить список этих команд в классе с фиксированным именем App, main/w3/app.php. Пример класса App:

<?php

class App extends Console
{
    /** This is just test */
    function a_test() {
        var_dump(SKY::$dd);
    }

    /** Test without DB */
    function a__test() {
        var_dump(SKY::$dd);
    }
}

Методы для команд начинаются с "a_", а в DOC-comment блоке можно описать назначение команд. Если описание сделано, такие команды будут представлены во время вывода "USAGE". Командам приложений предшествует "app", например: sky app test. Если команда начинается с подчеркивания, автоматическое подключение к базе данных не будет выполнено: sky app _test.

Продукты также могут содержать расширения для скрипта sky, аналогичные классам App приложений. Для этого нужно создать аналогичный файл в папке w3 и использовать namespace соответствующий имени продукта:

<?php

namespace venus;
use Plan, Maat, Vesper, Maxwell;

class app extends \Console
{
    function __construct($argv = [], $found = []) {
        Plan::set('venus', fn() => parent::__construct($argv, $found));
    }

    /** Parse css */
    function a_parse() { # "sky venus parse"
        new Maat;
    }
}

Если в переменную окружения PATH внести путь к скрипту sky, то его можно будет запускать с любого места в файловой системе. При этом, скрипт может находить SKY-приложения, SKY-продукты и просто репозитории git, двигаясь вверх по иерархии папок. Для SKY-приложений, список команд наиболее широкий. При установке продуктов, система записывает файл .coresky, содержащий путь к приложению. Таким образом, консольные команды приложений, можно выполнять в том числе из директорий продуктов.

Работа с PHP web-сервером

SKY-приложения могут работать со встроенным в PHP development-сервером. Для этого необходимо выполнить: sky s.

Типы консольных скриптов

Итак, для консольных скриптов, активирующихся планировщиком CRON имеется специальная схема и класс Schedule. Главное преимущество: простое обслуживание и возможность запускать несколько скриптов параллельно, в отдельных процессах PHP, запуск которых совпал на одно и тоже время. Кроме того класс Schedule, содержит весь другой типичный функционал, который может потребоваться для таких задач.

Также, имеется класс Console и специальная схема для консольного скрипта sky. Скрипт "инициатор", можно запускать из любого места в файловой системе, если прописать к нему путь в PATH. Эта схема, также, инкапсулирует все типичные консольные задачи, которые могут потребоваться во время работы с Coresky. Класс Console имеет удобную систему описания назначения скриптов (в DOC-комментариях), которая используется во время вывода "Usage". Консольные скрипты, также, можно организовать "натурально":

<?php

define('START_TS', microtime(true));
require __DIR__ . '/../bootstrap.php';
new SKY;

echo 'test';

Скрипт First-Run page

Для тестирования окружения на новых инсталляциях SKY-приложений, Coresky умеет автоматически генерировать скрипт "First-Run" page, вместо public/index.php.

Для генерации этого скрипта с целью тестирования, можно запустить в консоле sky fr.

При запуске sky master (только для SKY-приложений, но не wares), перед коммитом и пушем в удаленный репозиторий, эта команда вызывает common_c::make_h(true); и также common_c::make_h(false); в конце обработки. Если этот метод трейта HOOK_C не переопределен, то будет происходить стандартная обработка: вызывается метод Install::make($forward = true, Array $plus = []). Когда $forward = true - public/index.php заменяется скриптом "First-Run" page. А когда $forward = false - код public/index.php восстанавливается.

Стандартная обработка включает проверку версии PHP и перечень необходимых PHP-extensions. Эти настройки устанавливаются в "Open SkyProject". А список необходимых расширений PHP, можно найти автоматически с помощью "Global Reports".

Метод common_c::make_h($forward); можно переопределить:

<?php

class common_c extends Controller
{
    use HOOK_C;
. . .

    static function make_h($forward) {
        sqlf('delete from $_visitors');
        sqlf('vacuum');
        // + other: prepare for new instance
        Install::make($forward, ['common_c::trivial']);
    }

    static function trivial() {
        return ['This is a trivial test. Hit "Run app anyway" if all other OK', false];
    }
}

В примере выше, в параметре $plus, в массиве, передаётся дополнительный тест, код которого будет включён в "First-Run" код. Можно указать несколько дополнительных тестов (или код для начальной инициализации приложения).

Если все тесты "First-Run" прошли без ошибок, то public/index.php принимает свой обычный код, который содержится в конце файла "First-Run", после halt_compiler();.

При создании *.sky файла с помощью "Open SkyProject", файл public/index.php также как и при sky master, может замениться "First-Run" скриптом с помощью вызова common_c::make_h(true);.