Как запланировать динамическую функцию с помощью cron?

Я хочу знать, как я могу запланировать динамическую (автоматически заполняемые данные) функцию для автоматического запуска каждый день в сэкономленное время?

Допустим, у меня есть форма, которая после нажатия кнопки отправляет данные в функцию, которая публикует данные. Я просто хочу автоматизировать это, чтобы мне не нужно было нажимать кнопку.

<ul>
    <?php 
    foreach($Class->retrieveData as $data)
    {
        <form method="post" action="">
            <li>
                <input type="hidden" name="name">'.$data['name'].'<br/>
                <input type="hidden" name="description">'.$data['description'].'<br/>
                <input type="submit" name="post_data"  value="Post">
            </li>
        </form>
    }
    ?>
</ul>

Теперь форма передаст данные в функцию.

if(isset($_POST['post_data'])) // if post_data button is clicked then it runs myFunction()
{
    myFunction();
}

myFunction()
{
    $name        = $_POST['name'];
    $description = $_POST['description'];
}

Я попытался сделать следующее, но проблема в том, что Cron Job может запустить только весь файл.php, и я получаю сэкономленное время для запуска из MySQL.

foreach($Class->getTime() as $timeData)
{
    $timeHour    = $timeData['timeHour'];
    $timeMinute = $timeData['timeMinute'];

    $hourMin    = date('H:i');
    $timeData   = ''.$timeHour.':'.$timeMinute.'';

    if($hourMin == $timeData)
    {
        run myFunction.
    }
}

$hourMin текущий час: минута, которая сопоставляется с сохраненным временем для автоматического запуска из Mysql. Так что если $hourMin == $timeData тогда функция запустится.

Как я могу запустить Cron Job для автоматического запуска myFunction() если $hourMin равняется $timeData?

Так...

List 1 = is to be runned at 10am
List 2 = is to be runned at 12pm
List 3 = is to be runned at 2pm

10am, 12pm, 2pm это $timeHour а также $timeMinute это извлекается из MySQL, но на основе каждого идентификатора списка.

РЕДАКТИРОВАТЬ

@случайное зерно,

1) I can schedule cron jobs.
2) $name and $description will all be arrays, so the following is what I am trying to accomplish.

$name = array(
    'Jon',
    'Steven',
    'Carter'
);

$description = array(
    'Jon is a great person.',
    'Steven has an outgoing character.',
    'Carter is a horrible person.'
);

Я хочу разобрать первые массивы как из $name, так и из $description, если запланированное время правильное.

В базе данных у меня есть следующее

postDataTime table

+----+---------+----------+------------+--------+
| iD | timeDay | timeHour | timeMinute | postiD |
+--------------------------------------+--------+
| 1  | *       | 9        | 0          | 21     |
|----|---------|----------|------------|--------|
| 2  | *       | 10       | 30         | 22     |
|----|---------|----------|------------|--------|
| 3  | *       | 11       | 0          | 23     |
+----|---------+----------+------------+--------+

iD         = auto incremented on upload.
timeDay    = * is everyday (cron job style)
timeHour   = Hour of the day to run the script
timeMinute = minute of the hour to run script
postiD     = this is the id of the post that is located in another table (n+1 relationship)

Если трудно понять.. что такое киноа

if(time() == 10:30(time from MySQL postiD = 22))
{
    // run myFunction with the data that is retrieved for that time ex:

    $postiD = '22';
    $name   = 'Steven';
    $description = 'Steven has an outgoing character.';

    // the above is what will be in the $_POST from the form and will be
    // sent to the myFunction()
}

Я просто хочу запланировать все в соответствии со временем, которое сохраняется в MySQL, как я показал в самом верху (таблица postDataTime). (Я бы показал, что я пытался, но я искал бесчисленные часы в качестве примера того, чего я пытаюсь достичь, но я ничего не могу найти, и то, что я пытался, не работает.).

Я думал, что мог бы использовать функцию exec(), но, судя по всему, это не позволяет мне запускать функции, иначе я бы сделал следующее...

$time = '10:30';
if($time == time())
{
    exec(myFunction());
}

5 ответов

Решение

у вас есть 2 способа, хотя только один будет делать то, что вы хотите;

  • Первый способ требует, чтобы у вас был доступ и привилегии для изменения стороны сервера cron-jobs (например, через PHP или другой). В зависимости от того, какие ОС существуют учебники: Win, Nix

  • 2-й способ будет делать то, что вам нужно, но без точности минут вы будете проигрывать максимум по 2 минуты в каждом цикле. (см. объяснение ниже).

1-й путь идеальный путь

  • Как только пользователь нажмет форму, создайте для него уникальную задачу cron, используя желаемое время данных.

если у вас нет этих привилегий, вы можете воспользоваться услугой 3d part, такой как http://www.easycron.com/, они также предлагают бесплатную версию с ограниченным запросом. они также предоставляют метод REST API для управления (CRUDE) cron-задачами.

2-й путь несовершенный путь

  • добавить новый VARCHAR колонка, я назвал это today при этом мы гарантируем, что задача будет выполняться только один раз в день.

-

+----+---------+----------+------------+--------+----------+
| iD | timeDay | timeHour | timeMinute | postiD |   today  |
+--------------------------------------+--------+----------+
| 1  | *       | 9        | 0          | 21     | 30-05-04 |
|----|---------|----------|------------|--------|----------+
| 2  | *       | 10       | 30         | 22     |          |
|----|---------|----------|------------|--------|----------+
| 3  | *       | 11       | 0          | 23     |          |
+----|---------+----------+------------+--------+----------+
  • после этого создать файл php, я назвал его crontask.php мы будем звонить каждые 5 минут

  • добавьте это к вашей панели cronjob:

  • 0,5 * * * * /usr/bin/php /www/virtual/username/crontask.php > /dev/null 2>&1

  • в crontask.php файл

-

<?php
// include() Database Config file here with mysql_connect etc...
// include() the required files ex. the file where myFunction reside...

$cron_cycle = 5; // set it equal to what used in cron command-line
$today = date('Y-m-d');
if($result = mysql_query("SELECT * FROM postDataTime WHERE today != '{$today}'")){
    while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { 
        $postID = $row['postID'];
        $timeHour = (int) $row['timeHour'];
        $current_hours = (int) date('H'); // current hours
        $current_minutes = (int) date('i'); // current minutes
        $timeMinute = (int) $row['timeMinute'];
        // force to run at the closest cycle
        $timeMinute = ($timeMinute % $cycle === 0) ? $timeMinute : toCloser($timeMinute, $cron_cycle); 
        if( $current_hours === $timeHour && $current_minutes === $timeMinute ){
            // ensure that we have already runned a cron for this user...
            mysql_query("UPDATE postDataTime SET today = '{$today}' WHERE postID = '{$postID}'");
            myFunction($postID);
        }
    }
}
function toCloser($n,$x=5) {
    $j = (round($n)%$x === 0) ? round($n) : (round(($n+$x/2)/$x)*$x);
    return ($j-$n) >= round($x/2) ? ($j-$x) : $j;
}

?>

Объяснение функции:

Предполагая, что расписание cron запускается каждые 5 минут, последние говорят, что мы в 20:00, теперь cron будет работать в 20:05, 20:10, 20:15, 20:20 и так далее...

тогда предположим, что в нашей БД у нас есть время

Jonh  : 20:05, 
Mario : 20:32, 
luke  : 20:48, 
David : 20:57, 
Jimmy : 20:06, 
Eddy  : 20:16

когда скрипт сверяется с тем временем, он будет работать как показано ниже:

at 20:05 -> run 20:05 Jonh, 20:06 Jimmy
at 20:10 -> run null
at 20:15 -> run 20:16 Eddy
at 20:20 -> run null
and so on....

Как видите, в худшем случае вы теряете 2 минуты каждый раз. Я думаю, что это достаточно справедливо!;)

Задачи Cron требуют, чтобы вы предварительно установили время, в которое они выполняются, они не могут (да, вы могли бы взломать это, имея скрипт, который редактирует ваш crontab, но я бы не сказал, что это очень хорошая идея), чтобы их время для запуска определялось динамически. Это означает, что у вас есть два варианта:

1) Установите cronjob для запуска каждую минуту и ​​используйте временный файл, к которому вы прикасаетесь, чтобы сообщить, когда он в последний раз запускал одну из запланированных задач. Каждый раз, когда он запускается, он проверяет, была ли задача для выполнения между последней отметкой времени ваш временный файл и текущее время, и если есть, он запускает задачу. Это грубое, но простое решение.

2) Не используйте cron. Создайте демон, который проверяет, сколько времени нужно запускать задачи, и помещает их в очередь с приоритетами, затем он высвечивает самый ранний элемент и спит, пока не настало время запустить эту задачу. Он запускает задачу и вставляет ее на 24 часа в будущем и повторяет. Это решение намного элегантнее, но также требует больше работы.

Можно настроить задание cron, которое запускается каждую минуту, и при запуске оно проверяет, какие задания запланированы на этот момент.

В качестве простой идеи, которая может быть легко изменена, чтобы протолкнуть детали времени выполнения для конкретного скрипта, если вы хотите:-

<?php

include '/core/config.php');

// Test script to allow jobs to be set up (cron style) on a database, but with the addition that jobs can be made
// dependent on other jobs completing first.
// Currently does not support jobs being dependent on more than one parent job.
// It uses a database of 2 tables. One for servers and the other for jobs.
// The server is selected as the one that matches the value of php_uname('n') (hence this can be run on many servers accessing a single database and only executing jobs for the particular server an instance is running on)
// Time ranges are specified in the same way as on CRON jobs:-
//  *   = no restriction based on that field
//  x   = when the value of that time parameter matches x
//  /x  = every x of that field (ie, mod current of that field by x and match if result is 0)
//  x-y = when the value of that time parameter is between x and y
//  x,y = when the value of the time parameter matches x or y (or z, etc)
// The script field on the scheduling table contains the script / command to be executed. For example if a php script then it might be 'php /usr/webdata/cron_scripts/some_script.php
// Parentid is the id of a job that must have finished before the job is executed.

class scheduling extends core_class
{

    public $connections;
    private $db;

    private $year;
    private $month;
    private $day;
    private $hour;
    private $minute;
    private $second;
    private $day_of_week;
    private $background_kick_off = true;

    private $completed_jobs = array();

    function __construct($connections, $background_kick_off = true) 
    {
        parent::__construct($connections);

        $this->background_kick_off = $background_kick_off;

        $this->debug_time_start();

        $this->connections = $connections;
        $this->db = new database($connections['EO'], 'em_scheduling');
        if (!$this->db->no_error)
            $this->error('E_ERROR', $this->db->error());

        $run_date = date('Y/m/d H:i:s w');

        list($date_part, $time_part, $this->day_of_week) = explode(' ', $run_date);
        list($this->year, $this->month, $this->day) = explode('/', $date_part);
        list($this->hour, $this->minute, $this->second) = explode(':', $time_part);     

        $this->find_jobs(0);
    }

    function find_jobs($parent_id)
    {
        $sql = "SELECT a.id, a.script, a.parent_id, a.minutes, a.hours, a.day_of_month, a.months, a.day_of_week, a.script_description, COUNT(DISTINCT b.id) AS child_count
                FROM scheduling a
                ON s.id = a.server_id
                LEFT OUTER JOIN scheduling b
                ON a.id = b.parent_id
                AND b.enabled = 1
                AND (b.minutes = '*' OR FIND_IN_SET('".$this->minute."', b.minutes) OR (SUBSTR(b.minutes, 1, 1) = '/' AND (".$this->minute." % CAST(SUBSTR(b.minutes, 2) AS UNSIGNED)) = 0) OR (b.minutes LIKE '%-%' AND ".$this->minute." BETWEEN CAST(SUBSTRING_INDEX(b.minutes, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.minutes, '-', -1) AS UNSIGNED)))
                AND (b.hours = '*' OR FIND_IN_SET('".$this->hour."', b.hours) OR (SUBSTR(b.hours, 1, 1) = '/' AND (".$this->hour." % CAST(SUBSTR(b.hours, 2) AS UNSIGNED)) = 0) OR (b.hours LIKE '%-%' AND ".$this->hour." BETWEEN CAST(SUBSTRING_INDEX(b.hours, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.hours, '-', -1) AS UNSIGNED)))
                AND (b.months = '*' OR FIND_IN_SET('".$this->month."', b.months) OR (SUBSTR(b.months, 1, 1) = '/' AND (".$this->month." % CAST(SUBSTR(b.months, 2) AS UNSIGNED)) = 0) OR (b.months LIKE '%-%' AND ".$this->month." BETWEEN CAST(SUBSTRING_INDEX(b.months, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.months, '-', -1) AS UNSIGNED)))
                AND ((b.day_of_month = '*' OR FIND_IN_SET('".$this->day."', b.day_of_month) OR (SUBSTR(b.day_of_month, 1, 1) = '/' AND (".$this->day." % CAST(SUBSTR(b.day_of_month, 2) AS UNSIGNED)) = 0) OR (b.day_of_month LIKE '%-%' AND ".$this->day." BETWEEN CAST(SUBSTRING_INDEX(b.day_of_month, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.day_of_month, '-', -1) AS UNSIGNED)))
                OR (b.day_of_week = '*' OR FIND_IN_SET('".$this->day_of_week."', b.day_of_week) OR (SUBSTR(b.day_of_week, 1, 1) = '/' AND (".$this->day_of_week." % CAST(SUBSTR(b.day_of_week, 2) AS UNSIGNED)) = 0) OR (b.day_of_week LIKE '%-%' AND ".$this->day_of_week." BETWEEN CAST(SUBSTRING_INDEX(b.day_of_week, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(b.day_of_week, '-', -1) AS UNSIGNED))))
                WHERE a.parent_id = ".(int)$parent_id."
                AND a.enabled = 1
                AND (a.minutes = '*' OR FIND_IN_SET('".$this->minute."', a.minutes) OR (SUBSTR(a.minutes, 1, 1) = '/' AND (".$this->minute." % CAST(SUBSTR(a.minutes, 2) AS UNSIGNED)) = 0) OR (a.minutes LIKE '%-%' AND ".$this->minute." BETWEEN CAST(SUBSTRING_INDEX(a.minutes, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.minutes, '-', -1) AS UNSIGNED)))
                AND (a.hours = '*' OR FIND_IN_SET('".$this->hour."', a.hours) OR (SUBSTR(a.hours, 1, 1) = '/' AND (".$this->hour." % CAST(SUBSTR(a.hours, 2) AS UNSIGNED)) = 0) OR (a.hours LIKE '%-%' AND ".$this->hour." BETWEEN CAST(SUBSTRING_INDEX(a.hours, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.hours, '-', -1) AS UNSIGNED)))
                AND (a.months = '*' OR FIND_IN_SET('".$this->month."', a.months) OR (SUBSTR(a.months, 1, 1) = '/' AND (".$this->month." % CAST(SUBSTR(a.months, 2) AS UNSIGNED)) = 0) OR (a.months LIKE '%-%' AND ".$this->month." BETWEEN CAST(SUBSTRING_INDEX(a.months, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.months, '-', -1) AS UNSIGNED)))
                AND ((a.day_of_month = '*' OR FIND_IN_SET('".$this->day."', a.day_of_month) OR (SUBSTR(a.day_of_month, 1, 1) = '/' AND (".$this->day." % CAST(SUBSTR(a.day_of_month, 2) AS UNSIGNED)) = 0) OR (a.day_of_month LIKE '%-%' AND ".$this->day." BETWEEN CAST(SUBSTRING_INDEX(a.day_of_month, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.day_of_month, '-', -1) AS UNSIGNED)))
                OR (a.day_of_week = '*' OR FIND_IN_SET('".$this->day_of_week."', a.day_of_week) OR (SUBSTR(a.day_of_week, 1, 1) = '/' AND (".$this->day_of_week." % CAST(SUBSTR(a.day_of_week, 2) AS UNSIGNED)) = 0) OR (a.day_of_week LIKE '%-%' AND ".$this->day_of_week." BETWEEN CAST(SUBSTRING_INDEX(a.day_of_week, '-', 1) AS UNSIGNED) AND CAST(SUBSTRING_INDEX(a.day_of_week, '-', -1) AS UNSIGNED))))
                GROUP BY a.id, a.script, a.parent_id, a.minutes, a.hours, a.day_of_month, a.months, a.day_of_week
                ORDER BY child_count
                ";

        //echo "\r\n $sql \r\n";

        $this->db->query($sql) or die($this->db->error());

        $process_array = array();

        while ($row = $this->db->fetch_assoc())
        {
            $process_array[] = $row;
        }   

        foreach($process_array as $aProcess)
        {
            if ($this->background_kick_off and $aProcess['child_count'] == 0)
            {
                // No jobs to follow so just kick them off as a background task
                $this->launchBackgroundProcess($aProcess['script']);
                $completed_jobs[$aProcess['id']] = $aProcess['script_description'];
            }
            else
            {
                passthru($aProcess['script'].'', $return_var);
                if ($return_var == 0)
                {
                    $completed_jobs[$aProcess['id']] = $aProcess['script_description'];
                    $this->find_jobs($aProcess['id']);
                }
            }
        }
    }

    private function launchBackgroundProcess($call) 
    {

        // Windows
        if($this->is_windows())
        {
            pclose(popen('start /b '.$call, 'r'));
        }

        // Some sort of UNIX
        else 
        {
            pclose(popen($call.' /dev/null &', 'r'));
        }
        return true;
    }

    private function is_windows()
    {
        if(PHP_OS == 'WINNT' || PHP_OS == 'WIN32')
        {
            return true;
        }
        return false;
    }
}

$Scheduling = new scheduling($connections, true);

?>

Таблицы как это:-

CREATE TABLE IF NOT EXISTS `scheduling` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `enabled` tinyint(1) NOT NULL DEFAULT '1',
  `script` varchar(255) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `minutes` varchar(5) DEFAULT NULL,
  `hours` varchar(5) DEFAULT NULL,
  `day_of_month` varchar(5) DEFAULT NULL,
  `months` varchar(5) DEFAULT NULL,
  `day_of_week` varchar(5) DEFAULT NULL,
  `script_description` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `parent_id` (`server_id`,`parent_id`,`enabled`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ;

--
-- Dumping data for table `scheduling`
--

INSERT INTO `scheduling` (`id`,  `enabled`, `script`, `parent_id`, `minutes`, `hours`, `day_of_month`, `months`, `day_of_week`, `script_description`) VALUES
(1, 1, 'php download.php', 0, '*', '*', '*', '*', '*', 'Download files'),
(2, 1, 'php load_data.php', 1, '*', '*', '*', '*', '*', 'Load files to database'),
(3, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(4, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(5, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(6, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(7, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(8, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(9, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(10, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(11, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(12, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(13, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL),
(14, 1, 'php file_test.php', 1, '*', '*', '*', '*', '*', NULL);

Я предлагаю вам создавать записи Cron динамически с помощью скрипта-обертки, который настраивает запись cron для запуска вашей конкретной функции, когда вы действительно хотите, чтобы она выполнялась.

Для вашего конкретного случая здесь ниже, что я хотел бы предложить:

  1. Создайте скрипт-оболочку и запланируйте его запуск в Cron каждую секунду.
  2. Этот скрипт-обертка будет общаться с MySQL и извлекать время, в которое должна запускаться определенная функция.
  3. Затем он динамически создаст Cron Entry для запуска этой функции в определенную полученную метку времени. Вы можете динамически добавлять и удалять Cron Entry, используя скрипт оболочки. Пожалуйста, смотрите ссылки ниже для деталей.
  4. Как только ваша функция будет завершена, должна появиться некоторая индикация, например, статус, сохраненный где-то, возможно, в вашей БД, или какой-то файл, чтобы обертка могла получить / узнать статус и удалить соответствующую запись cron.

Рекомендации
1. Создайте Cron, используя Bash
crontab -l | { cat; echo "0 0 0 0 0 some entry"; } | crontab -
2. Удалить / автоматизировать Cron
crontab -l -u | grep -v <unique command> | crontab -

С nodeJS, используя node-schedule решил вопрос:

запустить пользовательское задание:

  const campaignId = "MY_CUSTOM_ID"
  let job = schedule.scheduleJob(campaignId, '* * * * * *', function () {
    console.log('running campaign: ' + campaignId)
  })

остановить пользовательское задание:

  const campaignId = "MY_CUSTOM_ID"
  let current_job = schedule.scheduledJobs[campaignId]
  current_job.cancel()

Если я правильно понимаю, я думаю, что что-то вроде этого может работать для вас, используя часы в качестве ключей для функции, которую вы хотите запустить, в cron, установленном для запуска каждые два часа:

$listArray = Array(8=>"list1_function",10=>"list2_function");//etc...
$hour = Date("G");

if(array_key_exists($hour,$listArray))
{
    $listArray[$hour]();
}

function list1_function()
{
     echo "do list 1 stuff";
}

function list2_function()
{
     echo "do list 2 stuff";
 }
Другие вопросы по тегам