Question

Quel est le meilleur moyen de valider une entrée de crontab avec PHP? Devrais-je utiliser un regex ou une bibliothèque externe? J'ai un script PHP qui ajoute / supprime les entrées d'un fichier crontab, mais je veux un moyen de vérifier que la partie de l'intervalle de temps est dans un format valide.

Était-ce utile?

La solution

Hmmm, problème intéressant.

Si vous voulez vraiment le valider, les regex ne suffiront pas, vous devrez en fait analyser l’entrée et valider chacun des bits de planification. En effet, chaque bit peut être un nombre, une chaîne mois / jour de la semaine, une plage (2-7), un ensemble (3, 4, samedi), un raccourci Vixie (60/5) ou toute autre combinaison. de ce qui précède - toute approche regex unique va devenir très poilue, rapidement.

Utiliser le programme crontab de Vixie cron pour valider ne suffit pas, car cela ne valide pas complètement! Je peux obtenir que crontab accepte toutes sortes de choses illégales.

Les scripts de Wicked Cool Shell de Dave Taylor (ss) # PPA147 "rel =" noreferrer "> lien Google Livres ) a un script sh qui effectue une validation partielle, j'ai trouvé la discussion intéressante. Vous pouvez également utiliser ou adapter le code.

J'ai aussi créé des liens vers deux classes PHP qui font ce que vous dites (dont je n'ai pas évalué la qualité):

Une autre approche (en fonction de ce que votre application doit faire) pourrait consister à faire en sorte que PHP construise l'entrée crontab par programme et l'insère afin que vous sachiez qu'elle est toujours valide plutôt que d'essayer de valider une chaîne non fiable. Ensuite, il vous suffira de créer un " construire une entrée de crontab " Interface utilisateur, ce qui pourrait être simple si vous n’avez pas besoin de combinaisons de planification vraiment compliquées.

Autres conseils

Qui a dit que les expressions régulières ne pouvaient pas faire ça?

Avec l'aimable autorisation de mon employeur, Salir.com , voici un test PHPUnit qui effectue cette validation. N'hésitez pas à modifier & amp; distribuer. J'apprécierai si vous gardez la notice @author & amp; lien vers le site 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".
            ')';
    }
}

Merci à Jordi Salvat i Alabart pour son excellente solution.

Je n'ai modifié que la solution existante publiée par Jordi Salvat i Alabart. Cela a bien fonctionné pour moi, mais je voulais extraire certaines parties en capturant des groupes. J'ai ajouté des parenthèses non-capturantes pour pouvoir extraire des parties particulières de l'enregistrement crontab. Il est facile de voir quel groupe de capture utiliser lorsque vous testez une expression rationnelle à la sortie: 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*(' .
            '

Ce code génère une 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+(.*)$

Ou alternative Java pour générer cette expression rationnelle (sans les éléments @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+' . '(.*)

Ce code génère une regex:

<*>

Ou alternative Java pour générer cette expression rationnelle (sans les éléments @X):

<*>; }

Ce code génère une regex:

<*>

Ou alternative Java pour générer cette expression rationnelle (sans les éléments @X):

<*>

Il existe une belle bibliothèque PHP pouvant être utilisée pour la validation de l'expression Cron:

Pour installer cette bibliothèque via composer:

composer require mtdowling/cron-expression

Pour vérifier si l'expression Cron est valide

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

Vous devriez pouvoir le faire assez facilement avec regex. En fait, je ne serais pas surpris si vous pouviez trouver une regex existante pour cela sur Google. Ceci n’a pas été testé, mais peut-être quelque chose comme:

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

Merci à Jordi Salvat i Alabart et à ph4r05.

J'ai une petite solution existante modifiée postée sur php. Alternative Perl pour générer une expression rationnelle:

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

Merci à Jordi Salvat i Alabart et à ph4r05.

J'ai une petite solution existante modifiée postée sur php. Alternative Perl pour générer une expression rationnelle:

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

Utilisez le motif: / ^ ((?: [1-9]? \ d | \ *) \ s * (?: (?: [\ / -] [1-9]? \ d) | (?:, [ 1-9]? \ D) +)? \ S *) {5} $ /

En 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);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top