. */ /* * Script dedicato alla gestione delle operazioni di cron ricorrenti del gestionale. * Una volta attivato, questo script rimane attivo in background per gestire l'esecuzione delle diverse operazioni come pianificate nella tabella zz_tasks. * * Il file viene richiamato in automatico al login di un utente. * Per garantire che lo script resti attivo in ogni situazione, si consiglia di introdurre una chiamata nel relativo crontab di sistema secondo il seguente schema: */ // Schema crontab: "*/5 * * * * php /cron.php" use Carbon\Carbon; use Models\Cache; use Monolog\Handler\RotatingFileHandler; use Monolog\Logger; use Tasks\Task; // Rimozione delle limitazioni sull'esecuzione set_time_limit(0); ignore_user_abort(true); // Chiusura della richiesta alla pagina flush(); $skip_permissions = true; include_once __DIR__.'/core.php'; // Controllo su possibili aggiornamenti per bloccare il sistema $database_online = $database->isInstalled() && !Update::isUpdateAvailable(); if (!$database_online) { return; } // Disabilita della sessione session_write_close(); // Aggiunta di un logger specifico $pattern = '[%datetime%] %level_name%: %message% %context%'.PHP_EOL; $formatter = new Monolog\Formatter\LineFormatter($pattern); $logger = new Logger('Tasks'); $handler = new RotatingFileHandler(base_dir().'/logs/cron.log', 7); $handler->setFormatter($formatter); $logger->pushHandler($handler); // Lettura della cache $ultima_esecuzione = Cache::find((new Cache())->getByField('title', 'Ultima esecuzione del cron', Models\Locale::getPredefined()->id)); $data = $ultima_esecuzione->content; $in_esecuzione = Cache::find((new Cache())->getByField('title', 'Cron in esecuzione', Models\Locale::getPredefined()->id)); $cron_id = Cache::find((new Cache())->getByField('title', 'ID del cron', Models\Locale::getPredefined()->id)); $disattiva = Cache::find((new Cache())->getByField('title', 'Disabilita cron', Models\Locale::getPredefined()->id)); if ($disattiva->content || (in_array($_SERVER['HTTP_HOST'], ['localhost', '127.0.0.1']) && !$forza_cron_localhost)) { return; } // Impostazioni sugli slot di esecuzione $slot_duration = 5; // Controllo sull'ultima esecuzione $data = $data ? new Carbon($data) : null; $minimo_esecuzione = (new Carbon())->subMinutes($slot_duration * 5); if (!empty($data) && $minimo_esecuzione->lessThan($data)) { return; } // Generazione e registrazione del cron $current_id = random_string(); $cron_id->set($current_id); // Registrazione dell'esecuzione $adesso = new Carbon(); $ultima_esecuzione->set($adesso->__toString()); // Prima esecuzione immediata $slot_minimo = $adesso->copy(); // Esecuzione ricorrente $number = 1; while (true) { $disattiva->refresh(); $cron_id->refresh(); $in_esecuzione->refresh(); // Controllo su possibili aggiornamenti per bloccare il sistema $database_online = $database->isInstalled() && !Update::isUpdateAvailable(); if (!$database_online || !empty($disattiva->content) || $cron_id->content != $current_id) { return; } // Rimozione dei log piĆ¹ vecchi $database->query('DELETE FROM `zz_tasks_logs` WHERE DATE_ADD(`created_at`, INTERVAL :interval DAY) <= NOW()', [ ':interval' => 7, ]); // Risveglio programmato tramite slot $timestamp = $slot_minimo->getTimestamp(); time_sleep_until($timestamp); $in_esecuzione->set(true); // Registrazione dell'iterazione nei log $logger->info('Cron #'.$number.' iniziato', [ 'slot' => $slot_minimo->toDateTimeString(), 'slot-unix' => $timestamp, ]); // Calcolo del primo slot disponibile per l'esecuzione successiva $inizio_iterazione = $slot_minimo->copy(); $slot_minimo = $inizio_iterazione->copy()->startOfHour(); while ($inizio_iterazione->greaterThanOrEqualTo($slot_minimo)) { $slot_minimo->addMinutes($slot_duration); } // Aggiornamento dei cron disponibili $tasks = Task::all(); foreach ($tasks as $task) { $adesso = new Carbon(); // Registrazione della data per l'esecuzione se non indicata if (empty($task->next_execution_at)) { $task->registerNextExecution($inizio_iterazione); $task->save(); $logger->info($task->getTranslation('title').': data mancante', [ 'timestamp' => $task->next_execution_at->toDateTimeString(), ]); } // Esecuzione diretta solo nel caso in cui sia prevista if ($task->next_execution_at->copy()->addSeconds(20)->greaterThanOrEqualTo($inizio_iterazione) && $task->next_execution_at->lessThanOrEqualTo($adesso->copy()->addseconds(20))) { // Registrazione dell'esecuzione nei log $logger->info($task->getTranslation('title').': '.$task->expression); try { $task->execute(); } catch (Exception $e) { // Registrazione del completamento nei log $task->log('error', 'Errore di esecuzione', [ 'code' => $e->getCode(), 'message' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); $logger->error($task->getTranslation('title').': errore'); } } // Esecuzione mancata elseif ($task->next_execution_at->lessThan($inizio_iterazione)) { $logger->warning($task->getTranslation('title').': mancata', [ 'timestamp' => $task->next_execution_at->toDateTimeString(), ]); $task->registerMissedExecution($inizio_iterazione); } // Calcolo dello successivo slot if ($task->next_execution_at->lessThan($slot_minimo)) { $slot_minimo = $task->next_execution_at; } } // Registrazione dello slot successivo nei log $logger->info('Cron #'.$number.' concluso', [ 'next-slot' => $slot_minimo->toDateTimeString(), 'next-slot-unix' => $timestamp, ]); $in_esecuzione->set(false); // Registrazione dell'esecuzione $adesso = new Carbon(); $ultima_esecuzione->set($adesso->__toString()); ++$number; }