2008-10-24 12 views
11

Jaki jest najlepszy sposób sprawdzania poprawności wpisu crontab w PHP? Czy powinienem używać wyrażeń regularnych lub biblioteki zewnętrznej? Mam skrypt PHP, który dodaje/usuwa wpisy z pliku crontab, ale chce mieć jakiś sposób sprawdzenia, czy przedział czasu jest w ważnym formacie.Sprawdzanie poprawności wpisów Crontab za pomocą PHP

Odpowiedz

7

Hmmm, interesujący problem.

Jeśli naprawdę chcesz to sprawdzić, wyrażenie regularne nie będzie wystarczające, musisz przeanalizować wpis i sprawdzić poprawność każdego z bitów planowania. To dlatego, że każdy bit może być liczbą, łańcuchem miesiąca/dnia tygodnia, przedziałem (2-7), zestawem (3, 4, sobota), skrótem Vixie w stylu cron (60/5) lub dowolną kombinacją z powyższego - każde pojedyncze podejście do wyrażenia regularnego będzie bardzo owłosione, szybkie.

Używanie programu Vixie cron do sprawdzania poprawności jest niewystarczające, ponieważ w rzeczywistości nie sprawdza się w pełni! Mogę uzyskać crontab, aby zaakceptować wszystkie rodzaje nielegalnych rzeczy.

Dave Taylor's Wicked Cool Shell Scripts (Google books link) ma skrypt sh, który przeprowadza częściową walidację, uznałem dyskusję za interesującą. Możesz również użyć lub dostosować kod.

Ja również okazało się linki do dwóch klas PHP, że nie to, co mówisz (których jakość Nie oceniany):

Innym podejściem (w zależności od to, co twoja aplikacja musi zrobić) może polegać na tym, że PHP skonstruuje program crontab programowo i wstawi go, więc wiesz, że jest zawsze ważny, zamiast próbować sprawdzać niezaufany ciąg. Następnie wystarczy utworzyć interfejs użytkownika "buduj wpis crontab", co może być proste, jeśli nie potrzebujesz naprawdę skomplikowanych kombinacji planowania.

1

Powinieneś być w stanie to zrobić dość łatwo z regex. W rzeczywistości nie byłbym zaskoczony, gdyby można było znaleźć istniejące wyrażenie w tym właśnie w Google. To jest niesprawdzone, ale może coś takiego:

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

to nie pasuje '@ reboot',' @ daily', '2- 39/4' i kilka innych rozszerzeń Vixie – Jasen

21

Kto powiedział, że wyrazy regularne nie mogą tego zrobić?

Dzięki uprzejmości mojego pracodawcy, Salir.com, oto test PHPUnit, który dokonuje takiego sprawdzenia. Możesz modyfikować dystrybucję &. Będę wdzięczny, jeśli będziesz utrzymywał link do @authora & link do strony internetowej.

<?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

To jest świetne! W moim przypadku chcę zweryfikować pełną treść linii crontab po linii. Tak więc po prostu dodaję kilka sprawdzeń przed uruchomieniem powyższego rozwiązania, aby również zweryfikować puste wiersze, linie zaczynające się od komentarza #, i linie z przypisaniami zmiennych "ABC = DEF" – bksunday

3

Podziękowania dla Jordi Salvat i Alabart, którzy napisali świetne rozwiązanie.

Mam tylko zmodyfikowane istniejące rozwiązanie opublikowane przez Jordi Salvat i Alabart. Udało mi się to dobrze, ale chciałem wydobyć poszczególne części, przechwytując grupy. Dodałem nieprzechwytujące nawiasy, aby móc wyodrębnić poszczególne części rekordu crontab.Łatwo jest sprawdzić, które przechwytywania grupa używać podczas testowania wyjścia regex w: 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+' . 
      '(.*)$'; 
} 

Ten kod generuje regex:

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

lub alternatywne Java do wygenerowania tego regex (bez @x rzeczy):

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

Nie wiem, wygenerowane wyrażenie regularne nie będzie pasować nawet prostym '* * * * * '. Co mam zrobić źle? https://regex101.com/r/rO2wW9/2 – bentinata

0

Podziękowania dla Jordi Salvat i Alabart oraz ph4r05.

Mam małe zmodyfikowane istniejące rozwiązanie opublikowane na php. Perl alternatywa do generowania wyrażenia regularnego:

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

pomocą wzoru: /^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/

w 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

Jest miły biblioteki PHP, które mogą być wykorzystywane do sprawdzania poprawności wypowiedzi Cron:

Aby zainstalować tę bibliotekę poprzez kompozytor:

composer require mtdowling/cron-expression 

Aby sprawdzić, czy wyraz Cron jest ważny

$isValid = Cron\CronExpression::isValidExpression($expression); 
Powiązane problemy