[],'bcc'=>[],'attachments'=>[],'headers'=>[],'from'=>[email,name],'reply_to'=>[email,name]] * @return array [bool success, ?string error] */ function uslugi_mail(string $to, string $subject, string $html, ?string $text = null, array $opts = []): array { global $MAILCFG; $mail = new PHPMailer(true); try { // SMTP $mail->isSMTP(); $mail->Host = $MAILCFG['smtp']['host']; $mail->Port = $MAILCFG['smtp']['port']; $mail->SMTPSecure = $MAILCFG['smtp']['secure']; $mail->SMTPAuth = $MAILCFG['smtp']['auth']; $mail->Username = $MAILCFG['smtp']['username']; $mail->Password = $MAILCFG['smtp']['password']; // Base $mail->CharSet = 'UTF-8'; $mail->Encoding = 'base64'; $mail->XMailer = ''; // pas d'X-Mailer bavard // Expéditeur / Reply-To $from = $opts['from'] ?? $MAILCFG['from']; $rpto = $opts['reply_to'] ?? $MAILCFG['reply_to']; $mail->setFrom($from[0], $from[1] ?? ''); if (!empty($rpto[0])) $mail->addReplyTo($rpto[0], $rpto[1] ?? ''); // Return-Path (enveloppe) → SPF/DMARC alignés if (!empty($MAILCFG['bounce'])) $mail->Sender = $MAILCFG['bounce']; // Destinataires $mail->addAddress($to, $opts['to_name'] ?? ''); foreach (($opts['cc'] ?? []) as $cc) { $mail->addCC($cc); } foreach (($opts['bcc'] ?? []) as $bcc) { $mail->addBCC($bcc); } // Pièces jointes foreach (($opts['attachments'] ?? []) as $k => $v) { if (is_int($k)) $mail->addAttachment($v); else $mail->addAttachment($k, $v); } // Entêtes optionnelles foreach (($opts['headers'] ?? []) as $k => $v) { $mail->addCustomHeader($k, $v); } if (!empty($MAILCFG['suppress_auto'])) { $mail->addCustomHeader('Auto-Submitted', 'auto-generated'); $mail->addCustomHeader('X-Auto-Response-Suppress', 'All'); } // Contenu $mail->isHTML(true); $mail->Subject = $subject; // PHPMailer encode proprement $mail->Body = $html; $mail->AltBody = $text ?? strip_tags($html); $mail->send(); return [true, null]; } catch (Exception $e) { error_log('PHPMailer error: '.$mail->ErrorInfo); return [false, $mail->ErrorInfo]; } }