Domanda

Qual è il modo migliore per convalidare una voce crontab con PHP? Dovrei usare una regex o una libreria esterna? Ho uno script PHP che aggiunge / rimuove voci da un file crontab, ma voglio avere un modo per verificare che la parte dell'intervallo di tempo sia in un formato valido.

È stato utile?

Soluzione

Hmmm, problema interessante.

Se lo validerai davvero, regex non sarà abbastanza, dovrai effettivamente analizzare la voce e validare ciascuno dei bit di schedulazione. Questo perché ogni bit può essere un numero, una stringa del mese / giorno della settimana, un intervallo (2-7), un set (3, 4, sabato), un collegamento in stile cron Vixie (60/5) o qualsiasi combinazione di quanto sopra - ogni singolo approccio regex diventerà molto peloso, veloce.

Il solo utilizzo del programma crontab di Vixie cron per convalidare non è sufficiente, perché in realtà non si convalida completamente! Posso ottenere crontab per accettare ogni sorta di cose illegali.

Dave Taylor's Wicked Cool Shell Scripts ( link a Google books ) ha uno script sh che fa una validazione parziale, ho trovato interessante la discussione. Puoi anche utilizzare o adattare il codice.

Ho anche trovato collegamenti a due classi PHP che fanno ciò che dici (la cui qualità non ho valutato):

Un altro approccio (a seconda di ciò che la tua app deve fare) potrebbe essere quello di fare in modo che PHP costruisca programmaticamente la voce crontab e la inserisca, quindi sai che è sempre valida, piuttosto che provare a convalidare una stringa non attendibile. Quindi dovrai solo creare un "quot entry crontab" UI, che potrebbe essere semplice se non hai bisogno di combinazioni di pianificazione davvero complicate.

Altri suggerimenti

Chi ha detto che le espressioni regolari non possono farlo?

Per gentile concessione del mio datore di lavoro, Salir.com , ecco un test PHPUnit che effettua tale convalida. Sentiti libero di modificare & amp; distribuire. Apprezzerò se mantieni l'avviso @author & amp; collegamento al sito 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".
            ')';
    }
}

Grazie a Jordi Salvat i Alabart che ha pubblicato un'ottima soluzione.

Ho modificato solo la soluzione esistente pubblicata da Jordi Salvat i Alabart. Ha funzionato bene per me, ma volevo estrarre parti particolari catturando gruppi. Ho aggiunto parentesi non catturanti per poter estrarre parti particolari del record crontab. È facile vedere quale gruppo di acquisizione utilizzare quando si testa regex di output su: 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*(' .
            '

Questo codice genera 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+(.*)$

O alternativa a Java per generare questo regex (senza roba @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("<*>quot;)
            .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("(.*)<*>quot;);

    return fieldsReSB.toString();
}
. '|#' . '|\w+\s*=' . "|$fields_re\s+" . "|($replacements)\s+" . ')' . '([^\\s]+)\\s+' . '(.*)

Questo codice genera regex:

<*>

O alternativa a Java per generare questo regex (senza roba @X):

<*>; }

Questo codice genera regex:

<*>

O alternativa a Java per generare questo regex (senza roba @X):

<*>

C'è una bella libreria PHP che può essere usata per la validazione delle espressioni Cron:

Per installare questa libreria tramite il compositore:

composer require mtdowling/cron-expression

Per verificare se l'espressione Cron è valida

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

Dovresti essere in grado di farlo abbastanza facilmente con regex. In effetti, non sarei sorpreso se tu potessi trovare una regex esistente proprio su Google. Questo non è testato, ma forse qualcosa del genere:

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

Grazie a Jordi Salvat i Alabart e ph4r05.

Ho una piccola soluzione esistente modificata pubblicata su php. Alternativa al Perl per generare 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->{

Grazie a Jordi Salvat i Alabart e ph4r05.

Ho una piccola soluzione esistente modificata pubblicata su php. Alternativa al Perl per generare regex:

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

Usa il modello: / ^ ((: [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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top