2008-10-24 18 views
11

Was ist der beste Weg, um einen Crontab-Eintrag mit PHP zu validieren? Soll ich eine Regex oder eine externe Bibliothek verwenden? Ich habe ein PHP-Skript, das Einträge aus einer Crontab-Datei hinzufügt/löscht, aber eine Möglichkeit haben möchte, zu überprüfen, ob der Teil des Zeitintervalls in einem gültigen Format ist.Validieren von Crontab-Einträgen mit PHP

Antwort

7

Hmmm, interessantes Problem.

Wenn Sie es wirklich validieren wollen, wird Regex nicht genug sein, Sie müssen den Eintrag tatsächlich analysieren und jedes der Planungsbits validieren. Das liegt daran, dass jedes Bit eine Zahl, ein Monat/Wochentag, ein Bereich (2-7), ein Satz (3, 4, Samstag), eine Vixie-Cron-Verknüpfung (60/5) oder eine beliebige Kombination sein kann der oben genannten - jeder einzelne Regex-Ansatz wird sehr haarig, schnell werden.

Nur das crontab Programm von Vixie Cron zu validieren ist nicht ausreichend, weil es tatsächlich nicht vollständig validiert! Ich kann crontab bekommen, um alle Arten von illegalen Dingen zu akzeptieren.

Dave Taylors Wicked Cool Shell Scripts (Google books link) hat ein sh-Skript, das teilweise Validierung tut, fand ich die Diskussion interessant. Sie können den Code auch verwenden oder anpassen.

drehte ich mich auch Links zu zwei PHP-Klassen auf, die tun, was Sie sagen, (deren Qualität ich nicht ausgewertet):

Einen anderen Ansatz (je nach Was Ihre App tun muss, könnte sein, dass PHP den crontab-Eintrag programmatisch erstellt und einfügt, damit Sie wissen, dass er immer gültig ist, anstatt zu versuchen, eine nicht vertrauenswürdige Zeichenfolge zu validieren. Dann müssten Sie nur eine "erstellen Crontab-Eintrag" UI erstellen, die einfach sein könnte, wenn Sie nicht wirklich komplizierte Zeitplan Kombinationen benötigen.

1

Sie sollten in der Lage sein, dies relativ einfach mit Regex zu tun. In der Tat würde ich nicht überrascht sein, wenn Sie eine existierende Regex für genau das auf Google finden könnten. Dies ist ungetestet, aber vielleicht so etwas wie:

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

ist dies nicht mit '@ reboot',' @ daily', '2- 39/4' und mehrere andere Vixie-Erweiterungen – Jasen

21

Wer sagt, dass reguläre Ausdrücke das nicht können?

Mit freundlicher Genehmigung meines Arbeitgebers Salir.com, hier ist ein PHPUnit-Test, der eine solche Validierung durchführt. Fühlen Sie sich frei zu modifizieren & zu verteilen. Ich werde es zu schätzen wissen, wenn Sie den @author-Hinweis & Link zur Website behalten.

<?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". 
      ')'; 
    } 
} 
+0

Das ist großartig! In meinem Fall möchte ich einen vollen Crontab-Inhalt Zeile für Zeile validieren. Also füge ich einfach einige Überprüfungen hinzu, bevor ich die obige Lösung löse, um auch leere Zeilen zu validieren, Zeilen beginnend mit Kommentar # und Zeilen mit Variablenzuweisungen "ABC = DEF" – bksunday

3

Dank Jordi Salvat ich Alabart, der große Lösung bekannt gegeben.

Ich habe nur bestehende Lösung von Jordi Salvat i Alabart geändert. Es funktionierte gut für mich, aber ich wollte bestimmte Teile extrahieren, indem ich Gruppen einfange. Ich habe nicht erfassbare Klammern hinzugefügt, um bestimmte Teile des Crontab-Datensatzes extrahieren zu können.Es ist leicht, die Capture-Gruppe zu verwenden, um zu sehen, wenn Sie die Ausgabe regex Test an: 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+' . 
      '(.*)$'; 
} 

Dieser Code regex erzeugt:

^\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+(.*)$ 

oder Java-Alternative diese regex zu erzeugen (ohne @X Sachen):

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(); 
} 
+0

Ich weiß nicht, generierter Regex passt nicht einmal einfach '* * * * * '. Was mache ich falsch? https://regex101.com/r/rO2wW9/2 – bentinata

0

Dank Jordi Salvat ich Alabart und ph4r05.

Ich habe kleine modifizierte bestehende Lösung auf PHP gepostet. Perl Alternative zu erzeugen regex:

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

Verwenden Sie das Muster: /^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/

In 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

Es gibt eine schöne PHP-Bibliothek, die für Cron Ausdruck Validierung verwendet werden können:

Um diese Bibliothek über Komponisten zu installieren:

composer require mtdowling/cron-expression 

Um zu überprüfen, ob Cron Ausdruck gültig

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