Menu

Mutualisé OVH et Laravel scheduler

Publié en Novembre 2016 - Développement web

cron-laravel-ovh

Le scheduler de Laravel est un outil très puissant qui vous permet de planifier des tâches en ne gérant qu'un seul cron. Vous pouvez exécuter des commandes Artisan ou toutes autres commandes, envoyer des mails et tout ça, dans une syntaxe d'écriture très simple !

Par exemple :

$schedule->command('emails:send --force')->daily();

Le scheduler exécutera la commande Artisan 'emails:send' tous les jours à 00:00.

Parlons maintenant des tâches cron sur les mutualisés d'OVH. Nous avons la possibilité de préciser un script à exécuter (sans pouvoir lui passer d'options) et la fréquence à laquelle le script sera appelé, qui est au minimum toutes les heures. Il n'est donc pas possible de configurer le cron d'OVH pour exécuter la commande '/path/to/artisan schedule:run' comme l'exige Laravel.

 

Comment exécuter une commande Artisan avec le cron d'OVH ?

Il faut d'abord comprendre le fonctionnement d'Artisan, pour cela, regardons l'intérieur du fichier artisan à la racine de votre projet Laravel.

// Autoload des dépendances avec Composer
require __DIR__.'/bootstrap/autoload.php';

// L'application Laravel est activée
$app = require_once __DIR__.'/bootstrap/app.php';

// Une fois l'application lancée, Artisan va exécuter la commande
// passée en paramètre avec ses arguments grâce à la classe
// Symfony\Component\Console\Input\ArgvInput 
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);

// La commande finie ensuite de s'exécuter en retournant le code d'erreur
$kernel->terminate($input, $status);
exit($status);

La classe ArgvInput de Symfony va simplement récupérer les paramètres passés lors de l'exécution du script.

Dans notre cas, la commande à exécuter est '/path/to/artisan schedule:run', si nous regardons le contenu de la variable super global $_SERVER à l'indice argv, nous aurons

[
    '/path/to/artisan',
    'schedule:run'
]

Nous pouvons donc simuler un appel à cette commande Artisan très simplement en remplaçant le contenu de l'indice argv.

Pour cela, créez un fichier schedule-run.php à la racine de votre application et remplissez-le avec le code suivant

<?php
// On simule l'appel à `artisan schedule:run`
$_SERVER['argv'] = [
    'artisan',
    'schedule:run',
];

// On lance artisan
require __DIR__.'/artisan';

On tombe ensuite dans l'exécution du fichier précédemment décrit, l'application Laravel va démarrer et artisan va s'exécuter avec les paramètres que nous avons simulés !

La première étape est maintenant terminée, nous pouvons maintenant configurer notre cron d'OVH pour exécuter le fichier schedule-run.php.

Viens maintenant la configuration de la récurence. Il n'est pas possible avec les mutualisés d'OVH d'avoir un cron où la répétition est inférieure à 1h.

Nous allons donc mettre le plus rapide, soit toutes les heures.

 

Autoriser le scheduler à fonctionner toutes les heures

Si l'on s'arrête ici, un problème persiste. Lorsque vous configurez un cron qui s'exécute toutes les heures, OVH va vous attribuer aléatoirement les minutes auxquelles le cron va se lancer.

Par exemple  la valeur "14 * * * *". Autrement dit, le cron va se lancer à 00:14, 01:14, 02:14, ... Ainsi de suite. Si l'on reprend le schedule de l'exemple, on veut qu'il se lance tous les jours à 00:00, il ne s'exécutera donc jamais.

Le dernier hack consiste à déverrouiller les events afin de ne pas les exécuter à une minute précise. Dans le fichier app/Console/Kernel.php ajoutez la méthode suivante

protected function scheduleRunsHourly(Schedule $schedule)
{
    foreach ($schedule->events() as $event) {
        $event->expression = substr_replace($event->expression, '*', 0, 1);
    }
}

Puis dans la méthode schedule

// Vos schedules
$schedule->command('emails:send --force')->daily();

// À rajouter à la fin de la méthode après la déclaration de vos schedules
$this->scheduleRunsHourly($schedule);

Cette pratique va vous permettre d'utiliser les schedules de Laravel de la même manière qu'avec un cron toutes les minutes avec seulement quelques limitations, vous ne pouvez pas régler les minutes. La méthode $schedule‑>everyMinute() va s'exécuter toutes les heures, la méthode $schedule‑>at('12:45') va lancer la commande tous les jours lorsque l'heure sera 12h mais les minutes vont dépendre de ce qu'OVH vous a attribué.

Malgré ces limitations la solution reste tout de même largement viable pour la plupart des projets, tant qu'ils n'ont pas besoin d'un contrôle à la minute.