views:

2014

answers:

3

What is the best way to validate a crontab entry with PHP? Should I be using a regex, or an external library? I've got a PHP script that adds/removes entries from a crontab file, but want to have some way to verify that the time interval portion is in a valid format.

+1  A: 

You should be able to do that fairly easily with regex. In fact, I wouldn't be surprised if you could find an existing regex for just that on Google. This is untested, but perhaps something like:

/^((\*)|(\d+((-\d+)|(,\d+)+))\s+){5}/
Lucas Oman
+4  A: 

Hmmm, interesting problem.

If you're going to really validate it, regex isn't going to be enough, you'll have to actually parse the entry and validate each of the scheduling bits. That's because each bit can be a number, a month/day of the week string, a range (2-7), a set (3, 4, Saturday), a Vixie cron-style shortcut (60/5) or any combination of the above -- any single regex approach is going to get very hairy, fast.

Just using the crontab program of Vixie cron to validate isn't sufficient, because it actually doesn't validate completely! I can get crontab to accept all sorts of illegal things.

Dave Taylor's Wicked Cool Shell Scripts (Google books link) has a sh script that does partial validation, I found the discussion interesting. You might also use or adapt the code.

I also turned up links to two PHP classes that do what you say (whose quality I haven't evaluated):

Another approach (depending on what your app needs to do) might be to have PHP construct the crontab entry programatically and insert it, so you know it's always valid, rather than try to validate an untrusted string. Then you would just need to make a "build a crontab entry" UI, which could be simple if you don't need really complicated scheduling combinations.

joelhardi
A: 

Who said regular expressions can't do that?

Courtesy of my employer, Salir.com, here's a PHPUnit test which does such validation. Feel free to modify & distribute. I'll appreciate if you keep the @author notice & link to web site.

<?php
/**
 * @author Jordi Salvat i Alabart - with thanks to <a href="www.salir.com">Salir.com</a>.
 */

abstract class CrontabChecker extends PHPUnit_Framework_TestCase {
    protected function assertFileIsValidUserCrontab($file) {
        $f= @fopen($file, 'r', 1);
        $this->assertTrue($f !== false, 'Crontab file must exist');
        while (($line= fgets($f)) !== false) {
            $this->assertLineIsValid($line);
        }
    }

    protected function assertLineIsValid($line) {
        $regexp= $this->buildRegexp();
        $this->assertTrue(preg_match("/$regexp/", $line) !== 0);
    }

    private function buildRegexp() {
        $numbers= array(
            'min'=>'[0-5]?\d',
            'hour'=>'[01]?\d|2[0-3]',
            'day'=>'0?[1-9]|[12]\d|3[01]',
            'month'=>'[1-9]|1[012]',
            'dow'=>'[0-7]'
        );

        foreach($numbers as $field=>$number) {
            $range= "($number)(-($number)(\/\d+)?)?";
            $field_re[$field]= "\*(\/\d+)?|$range(,$range)*";
        }

        $field_re['month'].='|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec';
        $field_re['dow'].='|mon|tue|wed|thu|fri|sat|sun';

        $fields_re= '('.join(')\s+(', $field_re).')';

        $replacements= '@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly';

        return '^\s*('.
                '$'.
                '|#'.
                '|\w+\s*='.
                "|$fields_re\s+\S".
                "|($replacements)\s+\S".
            ')';
    }
}
Jordi Salvat i Alabart