PHP function that receive a cron string and return the next run timestamp
Asked Answered
H

7

14

I need to develop a task system that should be able to work on servers that doesn't support crontab.

I'm asking if there is any existing code that can take a cron string (e.g. '0 0,12 1 */2 *' and return the timestamp of the next scheduled run.

If such a code couldn't be found then how should I start with that?

Hobbs answered 3/1, 2011 at 19:5 Comment(1)
#321994Bandog
R
12

You could use this class PHP-Parse-cron-strings-and-compute-schedules

It'll also compute the last scheduled run

Ramberg answered 3/1, 2011 at 19:12 Comment(3)
also see #321994Ramberg
I use the PHP-Parse cron class to run sub-crons on two servers and I have not had a single problem with itHeliocentric
>> You need to be a registered user and login to get this file. <<Teeny
O
10

Use this function:

function parse_crontab($time, $crontab)
         {$time=explode(' ', date('i G j n w', strtotime($time)));
          $crontab=explode(' ', $crontab);
          foreach ($crontab as $k=>&$v)
                  {$time[$k]=intval($time[$k]);
                   $v=explode(',', $v);
                   foreach ($v as &$v1)
                           {$v1=preg_replace(array('/^\*$/', '/^\d+$/', '/^(\d+)\-(\d+)$/', '/^\*\/(\d+)$/'),
                                             array('true', $time[$k].'===\0', '(\1<='.$time[$k].' and '.$time[$k].'<=\2)', $time[$k].'%\1===0'),
                                             $v1
                                            );
                           }
                   $v='('.implode(' or ', $v).')';
                  }
          $crontab=implode(' and ', $crontab);
          return eval('return '.$crontab.';');
         }
var_export(parse_crontab('2011-05-04 02:08:03', '*/2,3-5,9 2 3-5 */2 *'));
var_export(parse_crontab('2011-05-04 02:08:03', '*/8 */2 */4 */5 *'));
Organography answered 20/4, 2011 at 8:9 Comment(5)
Awesome function. Here it is with a more standard code style. gist.github.com/goosehub/7deff7928be04ec99b4292be10b4b7b0Bigwig
I found a bug in your code. See my answer for explanation and feel free to apply it to your answer.Bigwig
@Goose, I think that "$time[$k]=intval($time[$k]);" is ok.Organography
echo intval(09); gives 0, so unfortunately you've got to use $time[$k] = preg_replace('/^0+(?=\d)/', '', $time[$k]);Bigwig
this doesn't answer the OP question, it doesn't return the next runtime!Consistency
C
3

You can try this: http://mtdowling.com/blog/2012/06/03/cron-expressions-in-php/ which use PHP Cron-Expression parser library, a php class https://github.com/mtdowling/cron-expression

Complacent answered 14/1, 2013 at 13:36 Comment(0)
B
2

I found diyism had a great answer, but found a crucial bug.

If you enter a cron time such as 0 * * * *, it'll run at 0 minute, 8th, minute and 9th minute. The code gives a conditional 08===0, which returns true, because PHP interprets numbers starting with 0 as octal, and 08 and 09 are not valid octal numbers so they're interpreted as 0. More information here.

How to prevent PHP from doing octal math in conditionals? (why does 08 === 0)

Here's a fixed and well commented version of diyism's code.

// Parse CRON frequency
function parse_crontab($time, $crontab) {
    // Get current minute, hour, day, month, weekday
    $time = explode(' ', date('i G j n w', strtotime($time)));
    // Split crontab by space
    $crontab = explode(' ', $crontab);
    // Foreach part of crontab
    foreach ($crontab as $k => &$v) {
        // Remove leading zeros to prevent octal comparison, but not if number is already 1 digit
        $time[$k] = preg_replace('/^0+(?=\d)/', '', $time[$k]);
        // 5,10,15 each treated as seperate parts
        $v = explode(',', $v);
        // Foreach part we now have
        foreach ($v as &$v1) {
            // Do preg_replace with regular expression to create evaluations from crontab
            $v1 = preg_replace(
                // Regex
                array(
                    // *
                    '/^\*$/',
                    // 5
                    '/^\d+$/',
                    // 5-10
                    '/^(\d+)\-(\d+)$/',
                    // */5
                    '/^\*\/(\d+)$/'
                ),
                // Evaluations
                // trim leading 0 to prevent octal comparison
                array(
                    // * is always true
                    'true',
                    // Check if it is currently that time, 
                    $time[$k] . '===\0',
                    // Find if more than or equal lowest and lower or equal than highest
                    '(\1<=' . $time[$k] . ' and ' . $time[$k] . '<=\2)',
                    // Use modulus to find if true
                    $time[$k] . '%\1===0'
                ),
                // Subject we are working with
                $v1
            );
        }
        // Join 5,10,15 with `or` conditional
        $v = '(' . implode(' or ', $v) . ')';
    }
    // Require each part is true with `and` conditional
    $crontab = implode(' and ', $crontab);
    // Evaluate total condition to find if true
    return eval('return ' . $crontab . ';');
}
Bigwig answered 29/12, 2016 at 22:25 Comment(4)
I think that "$time[$k]=intval($time[$k]);" is ok, i've update my answer, thanks.Organography
@Organography echo intval(09); gives 0, so unfortunately you've got to use $time[$k] = preg_replace('/^0+(?=\d)/', '', $time[$k]);Bigwig
This should be marked as the right answer ... really precise and compact solution!Jaime
preg_replace('/^0+(?=\d)/', '', $time[$k]); can be replaced by ltrim($time[$k], '0');. Test: 3v4l.org/HU1bgDuala
P
1

I wrote a very powerful PHP class called CalendarEvent a long time ago:

https://github.com/cubiclesoft/php-misc/

It supports two different pattern syntaxes. Normal cron cannot handle certain complex patterns whereas the default CalendarEvent syntax handles any scheduling pattern you could ever need. The cron pattern syntax support is actually a fallback (prefix cron lines with cron and a space).

CalendarEvent was written mostly as a calendar event calculation class and just happened to support cron style "next trigger" mechanisms. It is actually designed to calculate an entire schedule for multiple months for the purpose of displaying a calendar to a user (hence the class name). I've also used the class as an intermediate solution to translate events between calendar platforms. That's the rarer scenario - I've more frequently utilized it for AddSchedule()/NextTrigger() cron-like solutions.

Philbert answered 7/4, 2017 at 15:9 Comment(0)
R
1

You can use the popular package PHP Cron Expression Parser: https://github.com/dragonmantank/cron-expression

This also is the built-in part of the Laravel Framework)

Rugger answered 10/8, 2019 at 12:32 Comment(0)
M
0

In the parse_crontab function:

Replace $time[$k] with intval($time[$k]) inside the preg_replace line
to compare two base10 numbers correctly.

Masuria answered 12/12, 2014 at 22:54 Comment(1)
Can you please rephrase your answer? It's difficult to understand. Is there a parse_crontab function available? Where? How? And what's that about replacing code? Are you suggesting adapting the parse_crontab function?Bergquist

© 2022 - 2024 — McMap. All rights reserved.