使用 PHP 验证 crontab 条目的最佳方法是什么?我应该使用正则表达式还是外部库?我有一个 PHP 脚本,可以在 crontab 文件中添加/删除条目,但希望有某种方法来验证时间间隔部分的格式是否有效。

有帮助吗?

解决方案

嗯,有趣的问题。

如果您要真正验证它,正则表达式还不够,您必须实际解析该条目并验证每个调度位。这是因为每个位都可以是数字、月份/星期几字符串、范围 (2-7)、集合(3、4、星期六)、Vixie cron 式快捷方式 (60/5) 或任意组合上面的内容——任何单一的正则表达式方法都会变得非常复杂、快速。

只需使用 crontab Vixie cron 程序来验证是不够的,因为它实际上没有完全验证!我可以得到 crontab 接受各种非法的事物。

Dave Taylor 的 Wicked Cool Shell 脚本 (谷歌图书链接)有一个执行部分验证的 sh 脚本,我发现讨论很有趣。您还可以使用或改编该代码。

我还找到了两个 PHP 类的链接,它们按照你的说法执行(我还没有评估其质量):

另一种方法(取决于您的应用程序需要执行的操作)可能是让 PHP 以编程方式构造 crontab 条目并插入它,这样您就知道它始终有效,而不是尝试验证不受信任的字符串。然后,您只需要制作一个“构建 crontab 条目”UI,如果您不需要真正复杂的调度组合,这可能很简单。

其他提示

谁说正则表达式不能这样做?

由我的雇主提供, Salir.com ,这是一个进行此类验证的PHPUnit测试。随意修改&分发。如果你保留@author通知&链接到网站。

<?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发布了很好的解决方案。

我只修改了Jordi Salvat i Alabart发布的现有解决方案。它对我有用,但我想通过捕获组来提取特定部分。我添加了非捕获括号,以便能够提取crontab记录的特定部分。在以下位置测试输出正则表达式时,很容易看出要使用哪个捕获组: http://www.regexplanet.com/advanced/java/index.html

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

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-6]'
    );

    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*(' .
            '

此代码生成正则表达式:

^\s*($|#|\w+\s*=|(\*(?:\/\d+)?|(?:[0-5]?\d)(?:-(?:[0-5]?\d)(?:\/\d+)?)?(?:,(?:[0-5]?\d)(?:-(?:[0-5]?\d)(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:[01]?\d|2[0-3])(?:-(?:[01]?\d|2[0-3])(?:\/\d+)?)?(?:,(?:[01]?\d|2[0-3])(?:-(?:[01]?\d|2[0-3])(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:0?[1-9]|[12]\d|3[01])(?:-(?:0?[1-9]|[12]\d|3[01])(?:\/\d+)?)?(?:,(?:0?[1-9]|[12]\d|3[01])(?:-(?:0?[1-9]|[12]\d|3[01])(?:\/\d+)?)?)*)\s+(\*(?:\/\d+)?|(?:[1-9]|1[012])(?:-(?:[1-9]|1[012])(?:\/\d+)?)?(?:,(?:[1-9]|1[012])(?:-(?:[1-9]|1[012])(?:\/\d+)?)?)*|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s+(\*(?:\/\d+)?|(?:[0-6])(?:-(?:[0-6])(?:\/\d+)?)?(?:,(?:[0-6])(?:-(?:[0-6])(?:\/\d+)?)?)*|mon|tue|wed|thu|fri|sat|sun)\s+|(@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly)\s+)([^\s]+)\s+(.*)$

或Java替代生成此正则表达式(没有@X的东西):

public static String buildRegex(){
    // numbers intervals and regex
    Map<String, String> numbers = new HashMap<String, String>();
    numbers.put("min", "[0-5]?\\d");
    numbers.put("hour", "[01]?\\d|2[0-3]");
    numbers.put("day", "0?[1-9]|[12]\\d|3[01]");
    numbers.put("month", "[1-9]|1[012]");
    numbers.put("dow", "[0-6]");

    Map<String, String> field_re = new HashMap<String, String>();

    // expand regex to contain different time specifiers
    for(String field : numbers.keySet()){
        String number = numbers.get(field);
        String range = "(?:"+number+")(?:-(?:"+number+")(?:\\/\\d+)?)?";
        field_re.put(field, "\\*(?:\\/\\d+)?|"+range+"(?:,"+range+")*");
    }

    // add string specifiers
    String monthRE = field_re.get("month");
    monthRE = monthRE + "|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec";
    field_re.put("month", monthRE);

    String dowRE = field_re.get("dow");
    dowRE = dowRE + "|mon|tue|wed|thu|fri|sat|sun";
    field_re.put("dow", dowRE);

    StringBuilder fieldsReSB = new StringBuilder();
    fieldsReSB.append("^\\s*(")
            .append("<*>quot;)
            .append("|#")
            .append("|\\w+\\s*=")
            .append("|");        
            .append("(")
            .append(field_re.get("min")).append(")\\s+(")
            .append(field_re.get("hour")).append(")\\s+(")
            .append(field_re.get("day")).append(")\\s+(")
            .append(field_re.get("month")).append(")\\s+(")
            .append(field_re.get("dow"))
            .append(")")
            .append("\\s+)")
            .append("([^\\s]+)\\s+")
            .append("(.*)<*>quot;);

    return fieldsReSB.toString();
}
. '|#' . '|\w+\s*=' . "|$fields_re\s+" . "|($replacements)\s+" . ')' . '([^\\s]+)\\s+' . '(.*)

此代码生成正则表达式:

<*>

或Java替代生成此正则表达式(没有@X的东西):

<*>; }

此代码生成正则表达式:

<*>

或Java替代生成此正则表达式(没有@X的东西):

<*>

有一个很好的 PHP 库可用于 Cron 表达式验证:

要通过 Composer 安装此库:

composer require mtdowling/cron-expression

检查 Cron 表达式是否有效

$isValid = Cron\CronExpression::isValidExpression($expression);

你应该可以使用正则表达式轻松地做到这一点。事实上,如果您能在Google上找到现有的正则表达式,我不会感到惊讶。这是未经测试的,但可能类似于:

/^((\*)|(\d+((-\d+)|(,\d+)+))\s+){5}/

感谢Jordi Salvat i Alabart和ph4r05。

我在php上发布了一个小的修改现有解决方案。 Perl替代生成正则表达式:

sub _BuildRegex {
    my $number = {
            '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-6]'
    };

    my $field_re = {};
    foreach my $nmb ( qw/min hour day month dow/ ) {
            my $range = "(?:$number->{$nmb})(?:-(?:$number->{$nmb})(?:\\/\\d+)?)?";
            $field_re->{$nmb} = "\\*(?:\\/\\d+)?|$range(?:,$range)*";
    }

    $field_re->{'month'} .='|[jJ]an|[fF]eb|[mM]ar|[aA]pr|[mM]ay|[jJ]un|[jJ]ul|[aA]ug|[sS]ep|[oO]ct|[nN]ov|[dD]ec';
    $field_re->{'dow'} .= '|[mM]on|[tT]ue|[wW]ed|[tT]hu|[fF]ri|[sS]at|[sS]un';

    my $ff = [];
    push @$ff, $field_re->{

感谢Jordi Salvat i Alabart和ph4r05。

我在php上发布了一个小的修改现有解决方案。 Perl替代生成正则表达式:

<*>} foreach ( qw/min hour day month dow/ ); my $fields_req = '(' . join(')\s+(', @$ff) . ')'; my $replacements = '@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly'; return '^\s*(' . ' . '|#' . '|\w+\s*=' . "|$fields_req\\s+" . "|($replacements)\\s+" . ')' . '([^\\s]+)\\s+' . '(.*); }

使用模式: <代码> / ^((:[1-9] \ d | \ *)\ S *(:(:\ / - ] [1-9] \ d)|(:??????,[ 1-9]?\ d)+)?\ S *){5} $ /

在PHP中:

<?php 
$cron = "*/5 1-2 3 3,4,5 *"; 
$result = preg_match( "/^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/", $cron, $matches); 
print_r($matches);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top