Проверка записей Crontab с помощью PHP - PullRequest
12 голосов
/ 25 октября 2008

Каков наилучший способ проверки записи в crontab с помощью PHP? Должен ли я использовать регулярное выражение или внешнюю библиотеку? У меня есть PHP-скрипт, который добавляет / удаляет записи из файла crontab, но я хочу иметь какой-то способ проверить, что часть временного интервала имеет допустимый формат.

Ответы [ 7 ]

23 голосов
/ 10 апреля 2010

Кто сказал, что регулярные выражения не могут этого сделать?

Предоставлено моим работодателем, 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".
            ')';
    }
}
8 голосов
/ 25 октября 2008

Хммм, интересная проблема.

Если вы собираетесь действительно проверить его, регулярных выражений будет недостаточно, вам придется фактически проанализировать запись и проверить каждый из битов планирования. Это связано с тем, что каждый бит может быть числом, строкой месяца / дня недели, диапазоном (2-7), набором (3, 4, суббота), ярлыком в стиле cron в стиле Vixie (60/5) или любой комбинацией из вышесказанного - любой подход к регулярному выражению будет очень волосатым, быстрым.

Простого использования crontab программы Vixie cron для проверки недостаточно, потому что на самом деле проверка выполняется не полностью! Я могу заставить crontab принимать всевозможные нелегальные вещи.

У Злых крутых сценариев Дейва Тейлора ( ссылка на книги Google ) есть сценарий sh, который выполняет частичную проверку, мне показалось интересным обсуждение. Вы также можете использовать или адаптировать код.

Я также нашел ссылки на два класса PHP, которые делают то, что вы говорите (качество которых я не оценивал):

Другой подход (в зависимости от того, что должно делать ваше приложение) может состоять в том, чтобы PHP конструировал запись crontab программно и вставлял ее, чтобы вы знали, что она всегда действительна, вместо того, чтобы пытаться проверить ненадежную строку. Тогда вам просто нужно будет создать пользовательский интерфейс «build crontab entry», который может быть простым, если вам не нужны действительно сложные комбинации расписаний.

3 голосов
/ 06 мая 2012

Спасибо 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*(' .
            '$' .
            '|#' .
            '|\w+\s*=' .
            "|$fields_re\s+" .
            "|($replacements)\s+" .
            ')' .
            '([^\\s]+)\\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("$")
            .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("(.*)$");

    return fieldsReSB.toString();
}
2 голосов
/ 07 ноября 2016

Есть хорошая библиотека PHP, которую можно использовать для проверки выражения Cron:

Чтобы установить эту библиотеку через композитор:

composer require mtdowling/cron-expression

Чтобы проверить правильность выражения Cron

$isValid = Cron\CronExpression::isValidExpression($expression);
0 голосов
/ 18 февраля 2016

Используйте шаблон: /^((?:[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);
0 голосов
/ 11 декабря 2014

Спасибо Джорди Сальват и Алабарт и 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->{$_} 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+' .
         '(.*)$';
}
0 голосов
/ 25 октября 2008

Вы можете легко сделать это с помощью регулярных выражений. На самом деле, я не удивлюсь, если вы найдете в Google существующее регулярное выражение. Это не проверено, но, возможно, что-то вроде:

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