isSMTP(); $mail->Host = $_ENV['SMTP_HOST'] ?? 'localhost'; $mail->Port = $_ENV['SMTP_PORT'] ?? 25; $mail->SMTPAuth = filter_var($_ENV['SMTP_AUTH'] ?? false, FILTER_VALIDATE_BOOLEAN); $mail->Username = $_ENV['SMTP_USER'] ?? ''; $mail->Password = $_ENV['SMTP_PASS'] ?? ''; $mail->SMTPAutoTLS = filter_var($_ENV['SMTP_AUTO_TLS'] ?? true, FILTER_VALIDATE_BOOLEAN); $encryption = strtolower(trim($_ENV['SMTP_ENCRYPTION'] ?? '')); if ($encryption === 'ssl') { $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; } elseif ($encryption === 'tls') { $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; } else { $mail->SMTPSecure = ''; } $fromEmail = $_ENV['SMTP_FROM_EMAIL'] ?? 'no-reply@localhost'; $fromName = $_ENV['SMTP_FROM_NAME'] ?? 'Wizdom Mailer'; $mail->setFrom($fromEmail, $fromName); $mail->SMTPOptions = [ 'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true ] ]; } /** * Parses a comma-separated list of emails and returns an array of valid addresses. * * @param string $rawList * @return array */ private static function parseRecipients(string $rawList): array { $emails = explode(',', $rawList); $validEmails = []; foreach ($emails as $email) { $email = trim($email); if (filter_var($email, FILTER_VALIDATE_EMAIL)) { $validEmails[] = $email; } } return $validEmails; } /** * Sends a basic HTML email. * * @param string $to * @param string $subject * @param string $body * @return bool */ public static function send(string $to, string $subject, string $body): bool { try { $mail = new PHPMailer(true); self::configureMailer($mail); $mail->addAddress($to); $mail->Subject = $subject; $mail->Body = $body; $mail->isHTML(true); $mail->send(); Logger::info("Email sent successfully to $to with subject: $subject"); return true; } catch (\Throwable $e) { Logger::error("Email send failed to $to: " . $e->getMessage()); return false; } } /** * Sends a system-level alert email to admins using an HTML template. * * @param string $context * @param string $errorMessage * @param array|string $data * @return void */ public static function alertAdmins(string $context, string $errorMessage, $data = []): void { $recipients = self::parseRecipients($_ENV['ADMIN_EMAILS'] ?? ''); if (empty($recipients)) { Logger::error("EmailHelper: No valid ADMIN_EMAILS configured."); return; } $htmlBody = self::renderTemplate('system_alert', [ 'context' => $context, 'errorMessage' => $errorMessage, 'data' => $data ]); foreach ($recipients as $email) { self::send($email, "[System Alert] Error in {$context}", $htmlBody); } } /** * Renders an email template with dynamic variables. * * @param string $templateName * @param array $vars * @return string */ public static function renderTemplate(string $templateName, array $vars = []): string { try { $templatePath = __DIR__ . '/../../resources/views/emails/' . $templateName . '.php'; if (!file_exists($templatePath)) { throw new \Exception("Template not found: $templateName"); } extract($vars); ob_start(); include $templatePath; return ob_get_clean(); } catch (\Throwable $e) { Logger::error("Failed to render email template: $templateName - " . $e->getMessage()); ErrorHandler::exception($e); return ''; } } /** * Generate a secure unsubscribe link for a subscriber email. * * @param string $email * @return string */ public static function buildUnsubscribeLink(string $email): string { $ts = time(); $tokenService = new TokenService(); $unsubscribeTokenService = new UnsubscribeTokenService($tokenService); $sig = $unsubscribeTokenService->generate($email, $ts); return $_ENV['APP_URL'] . "/unsubscribe?email=" . urlencode($email) . "&ts=$ts&sig=$sig"; } }