tasksDirectory = $tasksDirectory; $this->logger = $logger; $this->loadTasks(); } private function loadTasks(): void { if (!is_dir($this->tasksDirectory)) { $this->logger->error("Tasks directory not found: {$this->tasksDirectory}"); return; } $files = scandir($this->tasksDirectory); foreach ($files as $file) { if ($file === '.' || $file === '..') { continue; } $filePath = $this->tasksDirectory . '/' . $file; if (!is_file($filePath)) { continue; } // Parse interval from filename (e.g., "task.60.php" -> 60 seconds) if (preg_match('/^(.+)\.(\d+)\.php$/', $file, $matches)) { $taskName = $matches[1]; $interval = (int) $matches[2]; $this->tasks[] = [ 'name' => $taskName, 'file' => $filePath, 'interval' => $interval, 'last_run' => null, ]; $this->logger->info("Loaded task: {$taskName} with interval {$interval} seconds"); } } } public function run(): void { $this->logger->info('Scheduler started'); // Install signal handlers for graceful shutdown pcntl_async_signals(true); pcntl_signal(SIGTERM, [$this, 'handleSignal']); pcntl_signal(SIGINT, [$this, 'handleSignal']); while ($this->running) { $this->executeDueTasks(); sleep(1); // Check every second } $this->logger->info('Scheduler stopped'); } private function executeDueTasks(): void { $currentTime = time(); foreach ($this->tasks as &$task) { if ($task['last_run'] === null || ($currentTime - $task['last_run']) >= $task['interval']) { $this->executeTask($task); $task['last_run'] = $currentTime; } } } private function executeTask(array $task): void { $this->logger->info("Executing task: {$task['name']}"); $command = sprintf('php %s > /dev/null 2>&1 &', escapeshellarg($task['file'])); exec($command, $output, $exitCode); if ($exitCode === 0) { $this->logger->info("Task {$task['name']} executed successfully"); } else { $this->logger->error("Task {$task['name']} failed with exit code: {$exitCode}"); } } public function handleSignal(int $signal): void { $this->logger->info("Received signal: {$signal}. Shutting down gracefully..."); $this->running = false; } } // Main execution try { echo "Scheduler started\n"; $diContainer = new DiContainer(); $logger = $diContainer->get(LoggerInterface::class); $tasksDirectory = __DIR__ . '/scheduler-tasks'; $scheduler = new Scheduler($tasksDirectory, $logger); $scheduler->run(); exit(0); } catch (\Exception $e) { $logger = $logger ?? new \Monolog\Logger('scheduler'); $logger->error('Scheduler failed: ' . $e->getMessage()); exit(1); }