2008-10-24 36 views
11

Cách tốt nhất để xác thực mục nhập crontab bằng PHP là gì? Tôi có nên sử dụng regex hay thư viện bên ngoài không? Tôi đã có một kịch bản PHP cho biết thêm/loại bỏ các mục từ một tập tin crontab, nhưng muốn có một số cách để xác minh rằng phần khoảng thời gian là ở định dạng hợp lệ.Xác thực các mục nhập Crontab bằng PHP

Trả lời

7

Hmmm, vấn đề thú vị.

Nếu bạn định thực sự xác thực, regex sẽ không đủ, bạn sẽ phải phân tích cú pháp mục nhập và xác thực từng bit lập lịch biểu. Đó là bởi vì mỗi bit có thể là một số, một tháng/ngày của chuỗi tuần, một phạm vi (2-7), một tập hợp (3, 4, Thứ Bảy), lối tắt theo kiểu cron kiểu Vixie (60/5) hoặc bất kỳ kết hợp nào ở trên - bất kỳ phương pháp regex đơn lẻ nào cũng sẽ rất nhanh, rất nhanh.

Chỉ cần sử dụng chương trình crontab của Vixie cron để xác thực không đủ, bởi vì nó thực sự không xác thực hoàn toàn! Tôi có thể nhận được crontab để chấp nhận tất cả những thứ bất hợp pháp.

Kịch bản Wicked Cool Shell của Dave Taylor (Google books link) có kịch bản lệnh sh thực hiện xác thực một phần, tôi đã tìm thấy cuộc thảo luận thú vị. Bạn cũng có thể sử dụng hoặc điều chỉnh mã.

Tôi cũng bật lên những liên kết tới hai lớp PHP mà những gì bạn nói (mà chất lượng Tôi đã không được đánh giá):

cách tiếp cận khác (tùy thuộc vào những gì ứng dụng của bạn cần làm) có thể là để PHP xây dựng mục nhập crontab một cách có lập trình và chèn nó, vì vậy bạn biết nó luôn hợp lệ, thay vì cố gắng xác thực một chuỗi không đáng tin cậy. Sau đó, bạn chỉ cần tạo một giao diện người dùng "xây dựng một mục nhập crontab", điều này có thể đơn giản nếu bạn không cần các kết hợp lập lịch biểu phức tạp.

1

Bạn có thể làm điều đó khá dễ dàng với regex. Trong thực tế, tôi sẽ không ngạc nhiên nếu bạn có thể tìm thấy một regex hiện có cho chỉ trên Google. Điều này chưa được kiểm tra, nhưng có lẽ một cái gì đó như:

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

này không phù hợp với '@ reboot',' @ daily', '2- 39/4' và một số phần mở rộng khác của Vixie – Jasen

21

Ai nói cụm từ thông dụng không thể làm điều đó?

Được phép của chủ nhân của tôi, Salir.com, đây là bài kiểm tra PHPUnit có xác thực như vậy. Vui lòng sửa đổi & phân phối. Tôi sẽ đánh giá cao nếu bạn giữ thông báo @author & liên kết tới trang web.

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

Điều này thật tuyệt! Trong trường hợp của tôi, tôi muốn xác nhận một dòng nội dung crontab đầy đủ theo dòng. Vì vậy, tôi chỉ cần thêm vài kiểm tra trước khi chạy giải pháp trên để xác thực dòng trống, dòng bắt đầu bằng chú thích # và dòng có gán biến "ABC = DEF" – bksunday

3

Nhờ Jordi Salvat i Alabart đã đăng giải pháp tuyệt vời.

Tôi chỉ sửa đổi giải pháp hiện có được đăng bởi Jordi Salvat i Alabart. Nó làm việc tốt cho tôi, nhưng tôi muốn trích xuất các phần cụ thể bằng cách thu thập các nhóm. Tôi đã thêm dấu ngoặc đơn không bắt để có thể trích xuất các phần cụ thể của bản ghi crontab.Nó rất dễ dàng để xem cái nào chụp nhóm để sử dụng khi bạn kiểm tra đầu ra regex tại địa chỉ: 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+' . 
      '(.*)$'; 
} 

Mã này tạo ra 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+(.*)$ 

Hoặc Java thay thế để tạo ra regex này (mà không @X thứ):

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

Tôi không biết, regex được tạo sẽ không khớp ngay cả đơn giản '* * * * * '. Tôi làm gì sai? https://regex101.com/r/rO2wW9/2 – bentinata

0

Nhờ Jordi Salvat i Alabart và ph4r05.

Tôi có giải pháp sửa đổi nhỏ hiện có được đăng trên php. Perl thay thế để tạo ra 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

Sử dụng các mô hình: /^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$/

Trong 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

Có một PHP thư viện đẹp mà có thể được sử dụng cho Cron xác nhận biểu:

Để cài đặt thư viện này qua nhà soạn nhạc:

composer require mtdowling/cron-expression 

Để kiểm tra xem biểu Cron là hợp lệ

$isValid = Cron\CronExpression::isValidExpression($expression); 
Các vấn đề liên quan