<?php
// Worker PHP - appelé via HTTP par fire_worker()
// Ferme la connexion HTTP immédiatement et continue en arrière-plan
error_reporting(0);
ini_set('display_errors', 0);
set_time_limit(0);
ignore_user_abort(true);
// Fermer la session immédiatement
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
$campaign_id = $_GET['campaign_id'] ?? ($argv[1] ?? '');
if (!$campaign_id) {
http_response_code(400);
die('No campaign ID');
}
$sessions_dir = sys_get_temp_dir() . '/mailer_sessions';
$campaign_file = "$sessions_dir/$campaign_id.json";
$lock_file = "$sessions_dir/$campaign_id.lock";
if (!file_exists($campaign_file)) {
http_response_code(404);
die('Campaign not found');
}
// Empêcher les doublons - vérifier si un autre worker tourne déjà
if (file_exists($lock_file)) {
$lock_data = @json_decode(file_get_contents($lock_file), true);
if ($lock_data && (time() - $lock_data['time']) < 15) {
header('Content-Type: text/plain');
echo 'already_running';
exit;
}
}
// === FERMER LA CONNEXION HTTP ===
// Envoyer la réponse et libérer le client
header('Content-Type: text/plain');
header('Connection: close');
// Buffer la réponse
ob_start();
echo 'started';
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
// Flush tous les buffers
if (ob_get_level() > 0) ob_end_flush();
@ob_flush();
flush();
// FastCGI : terminer la requête client
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
// === LE CLIENT EST LIBÉRÉ - ON CONTINUE EN ARRIÈRE-PLAN ===
function send_email_worker($to, $subject, $text, $html, $sender_name, $sender_email) {
$boundary = md5(uniqid(time()));
$headers = "";
$headers .= "From: $sender_name <$sender_email>\r\n";
$headers .= "Reply-To: $sender_email\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/alternative; boundary=\"$boundary\"\r\n";
$body = "--$boundary\r\n";
$body .= "Content-Type: text/plain; charset=UTF-8\r\n\r\n";
$body .= ($text ?: strip_tags($html)) . "\r\n\r\n";
$body .= "--$boundary\r\n";
$body .= "Content-Type: text/html; charset=UTF-8\r\n\r\n";
$body .= $html . "\r\n\r\n";
$body .= "--$boundary--";
return @mail($to, $subject, $body, $headers, "-f$sender_email");
}
// Boucle principale d'envoi
while (true) {
// Heartbeat - prouver qu'on est vivant
file_put_contents($lock_file, json_encode(['pid' => getmypid(), 'time' => time()]));
clearstatcache();
$raw = file_get_contents($campaign_file);
$campaign = @json_decode($raw, true);
if (!$campaign || $campaign['status'] !== 'running') {
break;
}
if ($campaign['current_index'] >= $campaign['total']) {
$campaign['status'] = 'completed';
file_put_contents($campaign_file, json_encode($campaign));
break;
}
// Envoyer un batch de 10 emails
$batch_size = 10;
$batch_count = 0;
while ($batch_count < $batch_size && $campaign['current_index'] < $campaign['total']) {
$email = $campaign['emails'][$campaign['current_index']];
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
if (send_email_worker(
$email,
$campaign['subject'],
$campaign['text_template'],
$campaign['html_template'],
$campaign['sender_name'],
$campaign['sender_email']
)) {
$campaign['sent']++;
} else {
$campaign['errors']++;
}
} else {
$campaign['errors']++;
}
$campaign['current_index']++;
$batch_count++;
usleep(30000); // 30ms entre chaque email
}
$campaign['last_update'] = time();
if ($campaign['current_index'] >= $campaign['total']) {
$campaign['status'] = 'completed';
}
file_put_contents($campaign_file, json_encode($campaign));
}
// Nettoyage
@unlink($lock_file);