2010-07-26 86 views
4

我在我自己的個人框架中設計了一個任務調度程序,並試圖避免不靈活的「每運行n分鐘/小時/天」方法, d更容易實施。我想要做的就是模仿cron調度。我已經有了一些函數來分割模式並計算當前下一個日期(月中的某個日期)的下一個值,但是如果有比我所做的更容易的事情,或者可能更好做我想做的事。在PHP中計算cron下一次運行時間

/** 
    * Takes pattern(s) for various time attributes and calculates the next time the task should run 
    * 
    * @param mixed $minute Pattern or value of minute: 0-59 or 15,45 or * or * /5 (every 5 minutes) 
    * @param mixed $hour Pattern or value of hour: 0-23 or 0,6,12 or * or * /2 (every 2 hours) 
    * @param mixed $date Pattern or value of date: 1-31 or 1,15 or * or * /5 (every 5 days) 
    * @param mixed $day Pattern or value of weekday: 0-7 or 0,1,7 or * or * /7 (every 7 days) - takes precedence over date 
    * @return string Timestamp of next run time 
    */ 
public static function calcNextRun($minute, $hour, $date, $day) 
{ 
    # Simplest first, if all * then we run every minute. Return timestamp for next whole minute 
    if ($minute == '*' && $hour == '*' && $date == '*' && $day == '*') 
    return mktime(date("H"), date("i"), 0) + 60; # Prettier than time() + 60, isn't that reason enough? 

    # Default to current values 
    $nextDate = date("d"); 
    $nextMonth = date("m"); 
    $nextYear = date("Y"); 
    $nextDay = date("N"); 
    $nextHour = date("H"); 
    $nextMinute = date("i"); 

    # Calculate month date to run on, using multiple dates in the presence of , or - 
    if(strstr($date, ',') || strstr($date, '-')) 
    { 
    # Variable to determine whether the date has been set or not 
    $dateSet = false; 

    # Determine if there's a range in thurr 
    $rangeExists = (strstr($date, '-')) ? true : false ; 

    # Set up the $dates array, exploding if multiple values is present 
    $dates = array(); 
    if (strstr($date, ',')) 
    $dates = explode(',', $date); 
    else 
    $dates[] = $date; 

    # If we have a range(s) present then we expand them into full stuffs 
    foreach ($dates as $key => $val) 
    if (strstr($val, '-')) 
    $dates = array_merge($dates, self::expandRange($val)); # Merge the expanded range into the $dates array 

    # Loop through the $dates array and remove any lingering ranges 
    foreach ($dates as $key => $val) 
    if (strstr($val, '-')) 
    unset($dates[ $key ]); 

    # Sort the array 
    sort($dates); 

    # Determine the next lowest value 
    foreach($dates as $val) 
    { 
    # If the value is higher than the maximum number of dates this month, lower it to that 
    if ($val > date("t")) 
    $val = date("t"); 

    # If $val is higher than today's date, we use that 
    if ($val > date("d")) 
    { 
    $nextDate = $val; 
    $dateSet = true; 
    break; # We're done, we have our value 
    } 
    } 

    # If the date has not been set, add one to the month and use the lowest value in the array 
    if (!$dateSet) 
    { 
    # Increment the month. Maybe the year. Hurr hurr 
    if ($nextMonth == 12) 
    { 
    $nextMonth = 1; 
    $nextYear++; 
    } 
    else 
    $nextMonth++; 

    # Set the next day to the lowest value in the array 
    $nextDate = $dates[0]; 
    } 
    } 
    elseif (strstr($date, '/')) # Every n days 
    { 
    $parts = explode('/', $date); 
    $numDays = array_pop($parts); 

    # Calculate the timestamp of n days from now 
    $nDayTime = time() + ($numDays * 86400); # 86400 seconds in a day 

    # Update values of $nextVars 
    $nextDate = date("d", $nDayTime); 
    $nextMonth = date("m", $nDayTime); 
    $nextYear = date("Y", $nDayTime); 
    $nextDay = date("N", $nDayTime); 
    } 
    elseif ($date == (int)$date) 
    { 
    if ($date < date("j")) 
    { 
    # Determine if the month pushes into the next year 
    if ($nextMonth == 12) 
    { 
    $nextMonth = 1; 
    $nextYear++; 
    } 
    else 
    $nextMonth++; 
    } 

    $nextDate = $date; 
    } 

    # Return the new timestamp! 
    return mktime($nextHour, $nextMinute, 0, $nextMonth, $nextDate, $nextYear); 
} 

/** 
    * Takes a range and returns an array with all values belonging to that range 
    * 
    * @param string $range Two values split by a hyphen, ie: 1-5, 0-9, etc. 
    * @return array Array of values between the two parts of the range 
    */ 
private static function expandRange($range) 
{ 
    # Get the parts of the range 
    $range = explode('-', $range); 

    # Sort just in case the range is handed to us backwards. <_< 
    sort($range); 

    # Set up our return array 
    $returnArray = array(); 

    # Populate the return array with all values between min and max 
    for($i=$range[0];$i<=$range[1];$i++) 
    $returnArray[] = $i; 

    return $returnArray; 
} 

我不介意使用cron的使用參數的所有五人,但無論哪種方式,我希望能夠很容易地計算出所提供的模式匹配的下一個時間戳。

有沒有人有任何建議來完成此?我正在考慮創建一個函數,它將採用一個模式(1-7,10,15或*/5或*或其他)和當前的val(當前分鐘,一天中的某天),並返回下一個值從那個匹配或高於當前值的模式開始。

回答

8

我爲PHP創建了一個CRON解析器,可以處理您的調度需求。它支持包括範圍增量(3-59/12,*/2),範圍(3-5),散列(3#2),月份/月的最後一個工作日(5L,L),最近工作日到本月的某一天(15W)以及可選年份字段。

https://github.com/mtdowling/cron-expression

用法:

<?php 

// Works with predefined scheduling definitions 
$cron = Cron\CronExpression::factory('@daily'); 
$cron->isDue(); 
$cron->getNextRunDate(); 
$cron->getPreviousRunDate(); 

// Works with complex expressions 
$cron = new Cron\CronExpression::factory('15 2,6-12 */15 1 *'); 
$cron->getNextRunDate(); 

Calculate when a cron job will be executed then next time