logger = $logger; } /** * Register the cron hook and schedule. */ public function register(): void { add_action( self::CRON_HOOK, [ $this, 'process' ] ); if ( ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time(), 'webcal_five_minutes', self::CRON_HOOK ); } add_filter( 'cron_schedules', [ $this, 'add_cron_schedule' ] ); } /** * Add a 5-minute cron schedule. * * @param array> $schedules Existing schedules. * @return array> */ public function add_cron_schedule( array $schedules ): array { if ( ! isset( $schedules['webcal_five_minutes'] ) ) { $schedules['webcal_five_minutes'] = [ 'interval' => 300, 'display' => __( 'Every 5 Minutes', 'agenticdaisy-calendar' ), ]; } return $schedules; } /** * Add an email to the queue. * * @param string $to Recipient email address. * @param string $subject Email subject. * @param string $body Email body. */ public function enqueue( string $to, string $subject, string $body ): void { $queue = $this->get_queue(); $queue[] = [ 'to' => $to, 'subject' => $subject, 'body' => $body, 'queued' => time(), 'attempts' => 0, ]; update_option( self::OPTION_KEY, $queue, false ); } /** * Process all queued emails. * * Sends each email via wp_mail(). Failed emails are retried up to 3 times * before being discarded. */ public function process(): void { $queue = $this->get_queue(); if ( empty( $queue ) ) { return; } // Clear the queue immediately to prevent duplicate processing // by concurrent cron runs. update_option( self::OPTION_KEY, [], false ); $retry = []; foreach ( $queue as $email ) { $sent = wp_mail( $email['to'], $email['subject'], $email['body'] ); if ( ! $sent ) { $attempts = $email['attempts'] + 1; $this->logger->warning( sprintf( 'Failed to send queued email to %s (Attempt %d)', $email['to'], $attempts ) ); if ( $attempts < 3 ) { $email['attempts'] = $attempts; $retry[] = $email; } // After 3 attempts, silently discard. } } if ( ! empty( $retry ) ) { // Merge retries back with any new emails queued during processing. $current = $this->get_queue(); update_option( self::OPTION_KEY, array_merge( $retry, $current ), false ); } } /** * Get the current queue. * * @return array */ public function get_queue(): array { $queue = get_option( self::OPTION_KEY, [] ); /** @var array */ return is_array( $queue ) ? $queue : []; } /** * Get the number of emails in the queue. */ public function count(): int { return count( $this->get_queue() ); } }