Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- namespace App\Services;
- use Exception;
- use PHPMailer\PHPMailer\PHPMailer;
- use PHPMailer\PHPMailer\Exception as PHPMailerException;
- use PHPMailer\PHPMailer\SMTP;
- use App\Services\Email\EmailTemplateRenderer;
- use App\Services\Email\HtmlValidator;
- use App\Services\Email\EmailRateLimiter;
- use App\Services\Email\EmailSecurityManager;
- /**
- * Email Service
- *
- * Core email service that handles basic email sending functionality.
- *
- * @package App\Services
- */
- class EmailService
- {
- /**
- * @var PHPMailer PHPMailer instance
- */
- private PHPMailer $mailer;
- /**
- * @var Logger Logger service
- */
- private Logger $logger;
- /**
- * @var SecurityService Security service
- */
- private SecurityService $securityService;
- /**
- * @var array Default email configuration
- */
- private array $config;
- /**
- * @var EmailTemplateRenderer Template renderer
- */
- private EmailTemplateRenderer $templateRenderer;
- /**
- * @var HtmlValidator HTML validator
- */
- private HtmlValidator $htmlValidator;
- /**
- * @var EmailRateLimiter Rate limiter
- */
- private EmailRateLimiter $rateLimiter;
- /**
- * @var EmailSecurityManager Security manager
- */
- private EmailSecurityManager $securityManager;
- /**
- * EmailService constructor
- *
- * @param array $config Custom email configuration
- * @param int $maxEmailsPerHour Maximum emails per hour for rate limiting
- * @throws PHPMailerException
- */
- public function __construct(array $config = [], int $maxEmailsPerHour = 10)
- {
- $this->logger = new Logger();
- $this->securityService = new SecurityService();
- $this->templateRenderer = new EmailTemplateRenderer();
- $this->htmlValidator = new HtmlValidator();
- $this->rateLimiter = new EmailRateLimiter($maxEmailsPerHour);
- $this->securityManager = new EmailSecurityManager();
- // Default configuration from constants
- $this->config = [
- 'host' => MAIL_HOST,
- 'port' => MAIL_PORT,
- 'username' => MAIL_USERNAME,
- 'password' => MAIL_PASSWORD,
- 'encryption' => MAIL_ENCRYPTION,
- 'from_address' => MAIL_FROM_ADDRESS,
- 'from_name' => MAIL_FROM_NAME,
- 'debug' => APP_DEBUG ? 1 : 0,
- ];
- // Override with custom config if provided
- if (!empty($config)) {
- $this->config = array_merge($this->config, $config);
- }
- $this->initializeMailer();
- }
- /**
- * Initialize PHPMailer with config settings
- *
- * @return void
- * @throws PHPMailerException
- */
- private function initializeMailer(): void
- {
- $this->mailer = new PHPMailer(true);
- // Server settings
- $this->mailer->isSMTP();
- $this->mailer->Host = $this->config['host'];
- $this->mailer->SMTPAuth = true;
- $this->mailer->Username = $this->config['username'];
- $this->mailer->Password = $this->config['password'];
- $this->mailer->SMTPSecure = $this->config['encryption'];
- $this->mailer->Port = $this->config['port'];
- $this->mailer->CharSet = 'UTF-8';
- // Debug settings
- if (APP_ENV === 'development' || $this->config['debug'] > 0) {
- // Ensure log directory exists
- $logDir = ROOT_DIR . '/logs/track-mail';
- if (!is_dir($logDir)) {
- mkdir($logDir, 0755, true);
- }
- // Generate log file name with timestamp
- $timestamp = date('Y-m-d_H-i-s');
- $logFile = $logDir . '/mail_' . $timestamp . '.log';
- // Set debug level
- $this->mailer->SMTPDebug = SMTP::DEBUG_SERVER;
- // Custom debug output handler that writes to log file
- $this->mailer->Debugoutput = function ($str, $level) use ($logFile) {
- // Append to log file
- file_put_contents($logFile, $str . PHP_EOL, FILE_APPEND);
- };
- } else {
- $this->mailer->SMTPDebug = 0; // Turn off debugging in production
- }
- // Default sender
- $this->mailer->setFrom(
- $this->config['from_address'],
- $this->config['from_name']
- );
- // Set default reply-to
- $this->mailer->addReplyTo(
- $this->config['from_address'],
- $this->config['from_name']
- );
- }
- /**
- * Send an email
- *
- * @param string|array $to Recipient(s)
- * @param string $subject Email subject
- * @param string $body Email body (HTML)
- * @param string|null $plainText Plain text alternative
- * @param array $attachments Attachments [path => name]
- * @param array $cc CC recipients [email => name]
- * @param array $bcc BCC recipients [email => name]
- * @return bool True if email was sent successfully
- * @throws Exception If email sending fails
- */
- public function send(
- string|array $to,
- string $subject,
- string $body,
- ?string $plainText = null,
- array $attachments = [],
- array $cc = [],
- array $bcc = []
- ): bool
- {
- try {
- // Check if we're being rate limited
- if (!$this->rateLimiter->checkRateLimit()) {
- $this->logger->warning('Email rate limit exceeded', [
- 'to' => $to,
- 'subject' => $subject
- ]);
- throw new Exception('Email sending rate limit exceeded. Please try again later.');
- }
- // Log email attempt
- $this->rateLimiter->logEmailAttempt($to, $subject);
- // Reset recipients
- $this->mailer->clearAllRecipients();
- $this->mailer->clearAttachments();
- // Add recipient(s)
- if (is_array($to)) {
- foreach ($to as $email => $name) {
- if (is_numeric($email)) {
- $this->mailer->addAddress($name); // If just email without name
- } else {
- $this->mailer->addAddress($email, $name);
- }
- }
- } else {
- $this->mailer->addAddress($to);
- }
- // Add CC recipients
- foreach ($cc as $email => $name) {
- if (is_numeric($email)) {
- $this->mailer->addCC($name); // If just email without name
- } else {
- $this->mailer->addCC($email, $name);
- }
- }
- // Add BCC recipients
- foreach ($bcc as $email => $name) {
- if (is_numeric($email)) {
- $this->mailer->addBCC($name); // If just email without name
- } else {
- $this->mailer->addBCC($email, $name);
- }
- }
- // Add attachments
- foreach ($attachments as $path => $name) {
- $this->mailer->addAttachment($path, $name);
- }
- // Set email content
- $this->mailer->isHTML(true);
- $this->mailer->Subject = $subject;
- $body = $this->htmlValidator->validate($body);
- $this->mailer->Body = $body;
- // Set plain text alternative if provided
- if ($plainText !== null) {
- $this->mailer->AltBody = $plainText;
- } else {
- // Generate plain text from HTML
- $this->mailer->AltBody = $this->templateRenderer->htmlToPlainText($body);
- }
- // Add security headers to prevent email spoofing
- $this->securityManager->addSecurityHeaders($this->mailer);
- // Send the email
- $result = $this->mailer->send();
- // Log successful sending
- $this->logger->info('Email sent successfully', [
- 'to' => $to,
- 'subject' => $subject
- ]);
- return $result;
- } catch (PHPMailerException $e) {
- // Log the error
- $this->logger->error('Email sending failed', [
- 'error' => $e->getMessage(),
- 'to' => $to,
- 'subject' => $subject
- ]);
- // Log security event
- $this->securityService->logSecurityEvent(
- 'email_error',
- [
- 'error' => $e->getMessage(),
- 'to' => is_array($to) ? implode(', ', array_keys($to)) : $to,
- 'subject' => $subject
- ],
- 'warning'
- );
- throw new Exception('Failed to send email: ' . $e->getMessage());
- }
- }
- /**
- * Reset mailer instance with fresh configuration
- *
- * @return void
- * @throws PHPMailerException
- */
- public function resetMailer(): void
- {
- $this->initializeMailer();
- }
- /**
- * Get the PHPMailer instance for advanced configuration
- *
- * @return PHPMailer PHPMailer instance
- */
- public function getMailer(): PHPMailer
- {
- return $this->mailer;
- }
- /**
- * Set a custom reply-to address
- *
- * @param string $email Reply-to email
- * @param string $name Reply-to name
- * @return void
- * @throws PHPMailerException
- */
- public function setReplyTo(string $email, string $name = ''): void
- {
- $this->mailer->clearReplyTos();
- $this->mailer->addReplyTo($email, $name);
- }
- /**
- * Get the template renderer
- *
- * @return EmailTemplateRenderer
- */
- public function getTemplateRenderer(): EmailTemplateRenderer
- {
- return $this->templateRenderer;
- }
- /**
- * Get the HTML validator
- *
- * @return HtmlValidator
- */
- public function getHtmlValidator(): HtmlValidator
- {
- return $this->htmlValidator;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement