سؤال

ما هي أفضل طريقة للتحقق من صحة إدخال crontab باستخدام PHP؟هل يجب أن أستخدم regex أم مكتبة خارجية؟لدي برنامج نصي PHP يضيف/يزيل الإدخالات من ملف crontab، ولكنني أريد طريقة ما للتحقق من أن جزء الفاصل الزمني بتنسيق صالح.

هل كانت مفيدة؟

المحلول

هممم، مشكلة مثيرة للاهتمام.

إذا كنت تريد التحقق من صحة ذلك بالفعل، فلن يكون التعبير العادي كافيًا، وسيتعين عليك تحليل الإدخال والتحقق من صحة كل بت من أجزاء الجدولة.ذلك لأن كل بت يمكن أن يكون رقمًا، أو سلسلة شهر/يوم من الأسبوع، أو نطاقًا (2-7)، أو مجموعة (3، 4، السبت)، أو اختصارًا بنمط Vixie cron (60/5) أو أي مجموعة مما سبق - أي نهج regex واحد سيصبح مشعرًا جدًا وسريعًا.

مجرد استخدام crontab برنامج Vixie cron للتحقق ليس كافيًا، لأنه في الواقع لا يتم التحقق بشكل كامل!أستطيع الحصول على crontab لقبول جميع أنواع الأشياء غير القانونية.

نصوص ديف تايلور الشريرة الرائعة (رابط كتب جوجل) يحتوي على نص sh يقوم بالتحقق الجزئي، لقد وجدت المناقشة مثيرة للاهتمام.يمكنك أيضًا استخدام الكود أو تعديله.

لقد قمت أيضًا بعرض روابط لفئتين PHP تفعلان ما تقوله (لم أقم بتقييم جودتهما):

هناك طريقة أخرى (اعتمادًا على ما يحتاج تطبيقك إلى القيام به) وهي جعل PHP تقوم بإنشاء إدخال crontab برمجيًا وإدراجه، حتى تعرف أنه صالح دائمًا، بدلاً من محاولة التحقق من صحة سلسلة غير موثوق بها.بعد ذلك، ستحتاج فقط إلى إنشاء واجهة مستخدم "إنشاء إدخال crontab"، والتي قد تكون بسيطة إذا لم تكن بحاجة إلى مجموعات جدولة معقدة حقًا.

نصائح أخرى

والذي قال التعابير العادية لا تستطيع أن تفعل ذلك؟

وبإذن من صاحب العمل، 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".
            ')';
    }
}

وبفضل جوردي Salvat ط Alabart الذي نشر حلا رائعا.

ولقد تعديل سوى حل والقائمة المرسلة بواسطة جوردي Salvat ط Alabart. لأنها عملت معي بشكل جيد، ولكن أردت أن استخراج أجزاء معينة من قبل جماعات اسر. واضاف لقد قوسين عدم التقاط لتكون قادرة على استخراج أجزاء معينة من سجل كرونتاب. فمن السهل أن نرى أي جماعة التقاط لاستخدامه عند اختبار الانتاج التعابير المنطقية في: 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+(.*)$

وأو بديل لتوليد هذه التعابير المنطقية (بدون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();
}

توجد مكتبة PHP رائعة يمكن استخدامها للتحقق من صحة تعبير Cron:

لتثبيت هذه المكتبة عبر الملحن:

composer require mtdowling/cron-expression

للتحقق مما إذا كان تعبير Cron صالحًا

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

ويجب أن تكون قادرة على القيام بذلك بسهولة إلى حد ما مع التعابير المنطقية. في الواقع، أنا لن يفاجأ إذا كنت يتمكن من العثور على التعابير المنطقية القائمة لذلك فقط على جوجل. هذا لم تختبر، ولكن ربما شيء من هذا القبيل:

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

وبفضل جوردي Salvat ط Alabart وph4r05.

ولقد صغير المعدلة حل موجود نشرت على بي. بيرل بديل لتوليد التعابير المنطقية:

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+' .
         '(.*)$';
}

استخدم نمط: /^((?:[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