我想编写一个脚本,每天下午2点向用户的手机发送推送通知。但是有来自世界各地的用户,所以我需要根据特定用户的时区发送该通知,我将其保存在DB中,如“GMT + 4”。
我需要通过Cron工作来做到这一点。我从来没有使用它,所以我不知道如何编写该脚本并使其在PHP中工作。有人可以帮帮我吗?
答案 0 :(得分:5)
一种非常简单的方法是每1分钟让一个cron-job调用一个PHP脚本,并让它检查数据库中是否需要完成。如果需要完成 now(),那就去做吧。
在 crontab 中,你可以使用类似的东西,
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//other init code here...
mGestureMask = GestureHandler.GESTURE_ALL;
mGestureHandler = new GestureHandlerAndroid(metaioSDK, mGestureMask);
}
script.php 可以像这样
* * * * * /usr/bin/php /path/to/my/script.php
注意: 使用一些锁定机制来同时阻止多个进程。
答案 1 :(得分:0)
简介
如果您只对实际答案/解决方案感兴趣,请跳到下面的“解决方案摘要”部分。
我意识到这是一个老问题,有一个公认的答案和长期不活动(坦率地说,在撰写本文时没有那么多观点),但我认为这值得一个更好的答案,因为:
前面的一些事情
我稍微重新表述了这个问题以扩大它:
如何让 PHP 脚本(cron 作业)在设定的用户本地时间做某事,只给定用户的时区 (id)?
“仅给定用户的时区 ID”部分是必不可少的。例如,在用户输入计划任务的网络应用程序中,您需要使用时间戳。 但是,例如,如果用户只看到一个选项,例如“每天向我发送通知”,我们就没有该信息。所以我们可能只有用户的时区 (id),无论他们是否启用了“每天做某事”设置。
这个问题的原始海报提到让用户“时区”以“GMT + 4”等格式存储,无论是否考虑夏令时都没有提到(或存储? ) - 我建议避免这种情况。 我建议改为存储用户的 PHP timezone (named) id(例如“欧洲/阿姆斯特丹”)。这些 ID 可用于所有 PHP's date & time functions。
处理时区时的另一个一般建议是从 UTC 计算所有内容,因为夏令时不会影响它。 因此,如果您确实有一个用户在特定日期/时间输入任务的 Web 应用程序,请存储 UTC 日期/时间(或从 time() 返回的 PHP 的 UNIX 时间戳,它也总是在UTC,即使时区设置为“date_default_timezone_set()”,例如)。
为了涵盖所有不同的时区,cron 作业必须至少每 15 分钟运行一次 php 脚本。例如。在撰写本文时,时区“Pacific/Chatham”是 UTC +12:45。创建 Cron 作业本身在许多现有文章中都有介绍,应该很容易搜索和设置。
解决方案总结
为了让 cron 作业在用户本地时间的特定时间做某事,我们首先必须知道当前在哪个时区。
如果(就像在原始问题中一样)我们想在本地用户时间每天下午 2 点做某事,我们可以遍历所有可能的时区(我们可以用 DateTimeZone::listIdentifiers() 检索),然后检查它的时间是针对当前的每个时区,如果它与给定的时间(下午 2 点,或本例中的“14:00”)匹配,我们知道设置了该特定时区的用户(以及启用了“每天做某事”设置)是我们需要从数据库中选择的那些。为了查找当前给定时间所在的时区,我创建了一个自定义函数 get_timezones_where_time_is()。
从上面遵循的要求是,cron 作业需要在每个可能的时区偏移中运行,以查找所有可能的匹配时区。 IE。至少每 15 分钟一次,并且至少在指定的时间(在本例中为下午 2 点)。
这是一个边缘情况,但必须在指定时间的同一分钟内调用 get_timezones_where_time_is() 才能获得预期结果。例如,如果 cron 作业将在服务器时间 12:00 运行,但 get_timezones_where_time_is() 仅在 12:01 一分钟后执行,它当然不会在 14:00 在世界上找到任何时区时刻(因为那时已经是 14:01 了)。
解决方案
这是一个使用 cron 作业运行的示例脚本,有关更多详细信息,请参阅代码中的注释。它应该适用于 PHP 5 >= 5.2.0。
<?php
/* Just an example, since inluding files in a cron job can be counter intuitive */
set_include_path('/var/www/vhosts/example.com/');// this must be an absolute path, I recommended to the parent directory of your web/document root.
require_once 'config/config.php';// could contain the mysql login details
// Takes a time (hour and minutes) in the 24 hour clock format WITH LEADING ZERO'S, e.g. "14:00" or "04:00",
// Returns an array of timezone id's (from DateTimeZone::listIdentifiers()) where it currently is that given time, or an empty array.
// Throws exception on false $time input parameter
function get_timezones_where_time_is(string $time)
{
$time = explode(":", $time);
if(!isset($time[0]) || !isset($time[1]))
{
throw new Exception('Function get_timezones_where_time_is() expects a time paramter in 24 hour format with leading zero\'s, such as "04:00".');
}
$ret = array();
$timeHours = $time[0];
$timeMinutes = $time[1];
$timezoneIds = DateTimeZone::listIdentifiers();
foreach($timezoneIds as $key => $tzId)
{
$now = new DateTime(null, new DateTimeZone($tzId));
if($timeHours == $now->format('H') && $timeMinutes == $now->format('i'))
{
$ret[] = $tzId;
}
}
return $ret;
}
try
{
// get timezone id's where it is currently the given time of the day
$timezoneMatches = get_timezones_where_time_is('14:00');
// if no timezones found where it is currently the given time, we have nothing to do
if(!$timezoneMatches)
{
exit;
}
// we have atleast 1 found timezone where it is the given time of the day, so prepare and query the database
$mysqli = new mysqli(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_NAME);
if ($mysqli->connect_errno)
{
throw new Exception('mysqli connection error: ' . $mysqli->connect_error);
}
// just an example query, ofcourse
$query = "SELECT * FROM users WHERE timezone IN ('".implode("', '", $timezoneMatches)."') AND send_daily_notification = 1";
$selectUsers = $mysqli->query($query);
if(!$selectUsers)
{
throw new Exception("There was probably an error in the query: ".$query);
}
while($user = $selectUsers->fetch_assoc())
{
// do something for each user, such as sending a notification
}
$selectUsers->close();
}
catch (Exception $e)
{
// logs to php ini defined error log file
error_log('Cron job "send notification" failed with error message: '.$e->getMessage());
// output error here for easier debugging when running script directly (e.g. from command line)
echo 'Failed with error: '.$e->getMessage();
}
?>
后记