Pregunta

¿Cuál es la mejor manera de validar una entrada crontab con PHP? ¿Debo usar una expresión regular, o una biblioteca externa? Tengo un script PHP que agrega / elimina entradas de un archivo crontab, pero quiero tener alguna forma de verificar que la parte del intervalo de tiempo esté en un formato válido.

¿Fue útil?

Solución

Hmmm, problema interesante.

Si realmente va a validarlo, la expresión regular no será suficiente, tendrá que analizar la entrada y validar cada uno de los bits de programación. Esto se debe a que cada bit puede ser un número, una cadena de mes / día de la semana, un rango (2-7), un conjunto (3, 4, sábado), un atajo de estilo cron de Vixie (60/5) o cualquier combinación De lo anterior, cualquier enfoque de expresiones regulares se volverá muy velludo, rápido.

Simplemente usar el programa crontab de Vixie cron para validar no es suficiente, ¡porque en realidad no se valida por completo! Puedo obtener crontab para aceptar todo tipo de cosas ilegales.

Wicked Cool Shell Scripts de Dave Taylor ( enlace de libros de Google ) tiene un script sh que hace una validación parcial, encontré la discusión interesante. También puede usar o adaptar el código.

También encontré enlaces a dos clases de PHP que hacen lo que dices (cuya calidad no he evaluado):

Otro enfoque (dependiendo de lo que su aplicación necesite hacer) podría ser que PHP construya la entrada crontab programáticamente e la inserte, para que sepa que siempre es válida, en lugar de intentar validar una cadena no confiable. Entonces solo deberías hacer un " compilar una entrada crontab " IU, que podría ser simple si no necesita combinaciones de programación realmente complicadas.

Otros consejos

¿Quién dijo que las expresiones regulares no pueden hacer eso?

Cortesía de mi empleador, Salir.com , aquí hay una prueba PHPUnit que realiza dicha validación. Siéntase libre de modificar & amp; distribuir. Te agradecería si mantienes el aviso de autor @ & amp; enlace al sitio 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".
            ')';
    }
}

Gracias a Jordi Salvat i Alabart que publicó una gran solución.

Solo he modificado la solución existente publicada por Jordi Salvat i Alabart. Me funcionó bien, pero quería extraer partes particulares capturando grupos. He agregado paréntesis que no capturan para poder extraer partes particulares del registro de crontab. Es fácil ver qué grupo de captura usar cuando prueba la expresión regular de salida en: 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*(' .
            '

Este código genera expresiones regulares:

^\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 la alternativa de Java para generar esta expresión regular (sin cosas de @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+' . '(.*)

Este código genera expresiones regulares:

<*>

O la alternativa de Java para generar esta expresión regular (sin cosas de @X):

<*>; }

Este código genera expresiones regulares:

<*>

O la alternativa de Java para generar esta expresión regular (sin cosas de @X):

<*>

Hay una biblioteca de PHP agradable que se puede usar para la validación de la expresión Cron:

Para instalar esta biblioteca a través del compositor:

composer require mtdowling/cron-expression

Para verificar si la expresión de Cron es válida

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

Debería poder hacerlo con bastante facilidad con regex. De hecho, no me sorprendería si pudiera encontrar una expresión regular existente para eso en Google. Esto no se ha probado, pero quizás algo así como:

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

Gracias a Jordi Salvat i Alabart y ph4r05.

Tengo una pequeña solución existente modificada publicada en php. Perl alternativa para generar expresiones regulares:

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

Gracias a Jordi Salvat i Alabart y ph4r05.

Tengo una pequeña solución existente modificada publicada en php. Perl alternativa para generar expresiones regulares:

<*>} 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 el patrón: / ^ ((?: [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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top