Как запланировать динамическую функцию с помощью 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 для запуска вашей конкретной функции, когда вы действительно хотите, чтобы она выполнялась.
Для вашего конкретного случая здесь ниже, что я хотел бы предложить:
- Создайте скрипт-оболочку и запланируйте его запуск в Cron каждую секунду.
- Этот скрипт-обертка будет общаться с MySQL и извлекать время, в которое должна запускаться определенная функция.
- Затем он динамически создаст Cron Entry для запуска этой функции в определенную полученную метку времени. Вы можете динамически добавлять и удалять Cron Entry, используя скрипт оболочки. Пожалуйста, смотрите ссылки ниже для деталей.
- Как только ваша функция будет завершена, должна появиться некоторая индикация, например, статус, сохраненный где-то, возможно, в вашей БД, или какой-то файл, чтобы обертка могла получить / узнать статус и удалить соответствующую запись 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";
}