Email System Guide

⚠️ Documentation Under Review: This documentation is currently being updated and verified against the actual implementation. Some information may be incorrect or incomplete. Please verify all code examples against the actual source code before use.

Overview

The Neuron CMS provides an email system built on PHPMailer, supporting SMTP, Sendmail, and PHP mail() transports. The system features template-based email composition and test mode for development.

Architecture

Core Components

The email system consists of the following classes:

Configuration

Email configuration is defined in the email: section of config/neuron.yaml.

Basic Configuration

email:
  driver: smtp              # smtp, sendmail, or mail
  from_address: [email protected]
  from_name: Your Application Name
  test_mode: false         # Enable for development

SMTP Configuration

Gmail SMTP

email:
  driver: smtp
  host: smtp.gmail.com
  port: 587
  encryption: tls        # or ssl for port 465
  username: [email protected]
  password: your-app-password
  from_address: [email protected]
  from_name: Your Application

Note: Gmail requires an App Password when 2FA is enabled.

SendGrid SMTP

email:
  driver: smtp
  host: smtp.sendgrid.net
  port: 587
  encryption: tls
  username: apikey
  password: SG.your-sendgrid-api-key
  from_address: [email protected]
  from_name: Your Application

AWS SES SMTP

email:
  driver: smtp
  host: email-smtp.us-east-1.amazonaws.com
  port: 587
  encryption: tls
  username: your-ses-smtp-username
  password: your-ses-smtp-password
  from_address: [email protected]
  from_name: Your Application

Mailgun SMTP

email:
  driver: smtp
  host: smtp.mailgun.org
  port: 587
  encryption: tls
  username: [email protected]
  password: your-mailgun-smtp-password
  from_address: [email protected]
  from_name: Your Application

Office 365 / Outlook SMTP

email:
  driver: smtp
  host: smtp.office365.com
  port: 587
  encryption: tls
  username: [email protected]
  password: your-password
  from_address: [email protected]
  from_name: Your Name

Sendmail Configuration

email:
  driver: sendmail
  from_address: [email protected]
  from_name: Your Application

PHP Mail Configuration

email:
  driver: mail
  from_address: [email protected]
  from_name: Your Application

Note: PHP mail() is not recommended for production due to reliability and deliverability issues.

Test Mode Configuration

Enable test mode during development to log emails instead of sending:

email:
  driver: smtp
  host: smtp.gmail.com
  port: 587
  encryption: tls
  username: [email protected]
  password: your-app-password
  from_address: [email protected]
  from_name: Your Application
  test_mode: true          # Emails logged instead of sent

When test mode is enabled:

Email Templates

The CMS provides pre-built email templates located in resources/views/emails/:

resources/views/emails/
├── email-verification.php   # Email verification
├── password-reset.php        # Password reset
└── welcome.php              # Welcome email

Available Templates

Welcome Email

Template: emails/welcome

Usage Example:

use Neuron\Cms\Services\Email\Sender;

$sender = new Sender( $settingManager, $basePath );
$sender->to( '[email protected]' )
       ->subject( 'Welcome!' )
       ->template( 'emails/welcome', [
           'name' => 'John Doe',
           'username' => 'johndoe',
           'email' => '[email protected]',
           'site_name' => 'Your Application'
       ] )
       ->send();

Password Reset Email

Template: emails/password-reset

Usage Example:

$sender->to( $user->getEmail() )
       ->subject( 'Password Reset Request' )
       ->template( 'emails/password-reset', [
           'name' => $user->getName(),
           'reset_url' => "https://example.com/reset-password?token={$token}",
           'expiry_time' => '1 hour',
           'site_name' => 'Your Application'
       ])
       ->send();

Email Verification

Template: emails/email-verification

Usage Example:

$sender->to( $user->getEmail() )
       ->subject( 'Verify Your Email' )
       ->template( 'emails/email-verification', [
           'name' => $user->getName(),
           'verification_url' => "https://example.com/verify?token={$token}",
           'site_name' => 'Your Application'
       ])
       ->send();

Template Structure

Templates are PHP files that use extracted variables:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title><?= htmlspecialchars( $subject ?? 'Email' ) ?></title>
    <style>
        body { font-family: Arial, sans-serif; }
        .container { max-width: 600px; margin: 0 auto; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Welcome, <?= htmlspecialchars( $name ) ?>!</h1>
        <p>Thank you for registering.</p>
    </div>
</body>
</html>

Creating Custom Templates

  1. Create a new PHP file in resources/views/emails/
  2. Use PHP variables that will be extracted from the data array
  3. Call template via Sender service:
$sender->template( 'emails/your-template', $variables )->send();

Sending Emails

Using the Sender Service

The Sender service provides template support and PHPMailer integration:

use Neuron\Cms\Services\Email\Sender;
use Neuron\Data\Settings\SettingManager;

// Initialize sender
$sender = new Sender( $settingManager, $basePath );

// Simple email
$sender->to( '[email protected]' )
       ->subject( 'Test Email' )
       ->body( '<h1>Hello</h1><p>This is a test email.</p>' )
       ->send();

// Email with template
$sender->to( '[email protected]' )
       ->subject( 'Welcome!' )
       ->template( 'emails/welcome', [
           'name' => 'John Doe',
           'site_name' => 'Your App'
       ] )
       ->send();

Available Methods

Recipients

// Single recipient
$sender->to( '[email protected]' );
$sender->to( '[email protected]', 'User Name' );

// CC recipients
$sender->cc( '[email protected]' );
$sender->cc( '[email protected]', 'Manager Name' );

// BCC recipients
$sender->bcc( '[email protected]' );
$sender->bcc( '[email protected]', 'Admin Name' );

Note: Methods can be chained but each call adds a recipient (doesn't replace).

Reply-To Address

// Set reply-to address
$sender->replyTo( '[email protected]' );
$sender->replyTo( '[email protected]', 'Support Team' );

Content

// Set subject
$sender->subject( 'Your Order Confirmation' );

// HTML body
$sender->body( '<h1>Order Confirmed</h1><p>Thank you.</p>' );

// Plain text body
$sender->body( 'Order Confirmed\n\nThank you.', false );

// Use template
$sender->template( 'emails/welcome', ['name' => 'John'] );

Attachments

// Add file attachment
$sender->attach( '/path/to/file.pdf' );
$sender->attach( '/path/to/file.pdf', 'Invoice.pdf' );

// Chain multiple attachments
$sender->attach( '/path/to/file1.pdf', 'Document1.pdf' )
       ->attach( '/path/to/file2.pdf', 'Document2.pdf' );

Complete Example

use Neuron\Cms\Services\Email\Sender;
use Neuron\Log\Log;

$sender = new Sender( $settingManager );

try
{
    $result = $sender->to( '[email protected]' )
                     ->cc( '[email protected]' )
                     ->bcc( '[email protected]' )
                     ->subject( 'Order #12345 Confirmation' )
                     ->template( 'emails/welcome', [
                         'name' => 'John Doe',
                         'site_name' => 'Your Store'
                     ] )
                     ->attach( '/path/to/invoice.pdf', 'Invoice-12345.pdf' )
                     ->send();

    if( $result )
{
        echo "Email sent successfully";
    }
else
    {
        echo "Failed to send email";
    }
}
catch( \Exception $e )
{
    echo "Error: " . $e->getMessage();
}

Email Logging

The email system logs sent emails through the Neuron Log system.

Log Entries

Successful email sends are logged at INFO level:

[2025-11-27 10:30:15] Email sent to: [email protected]

Failed sends are logged at ERROR level:

[2025-11-27 10:30:15] Email send failed: Authentication failed

Test mode emails are logged:

[2025-11-27 10:30:15] TEST MODE - Email not sent
[2025-11-27 10:30:15]   To: [email protected]
[2025-11-27 10:30:15]   Subject: Welcome!
[2025-11-27 10:30:15]   Body: Welcome to our platform...

Test Mode

Test mode allows email development without sending actual emails.

Enabling Test Mode

email:
  test_mode: true

Test Mode Behavior

When test mode is enabled:

  1. Emails are not sent via SMTP/mail
  2. Email details are logged via Log::info()
  3. SMTP connection is bypassed
  4. send() method returns true
  5. Recipients, subject, and body preview are logged

Development Workflow

  1. Enable test mode in development environment
  2. Send test emails via application
  3. Review logged email content
  4. Verify template rendering and variables
  5. Disable test mode for staging/production

Using the Basic Email Class

The framework also provides a basic Email class with minimal functionality:

use Neuron\Util\Email;

$email = new Email();
$email->addTo( '[email protected]' );
$email->addCC( '[email protected]' );
$email->addBCC( '[email protected]' );
$email->attachFile( '/path/to/file.pdf' );
$email->setFrom( '[email protected]' );
$email->setSubject( 'Test Subject' );
$email->setBody( 'Email body content' );
$email->setType( Email::EMAIL_HTML ); // or EMAIL_TEXT

// Note: This class doesn't have a send() method
// It's a data structure for email information

Note: The Sender class is recommended over the basic Email class as it provides actual sending functionality via PHPMailer.

Security Considerations

Email Authentication (SPF, DKIM, DMARC)

Configure DNS records to improve deliverability:

SPF Record

TXT @ "v=spf1 include:_spf.google.com ~all"

DKIM

Configure through your email provider's settings.

DMARC

TXT _dmarc "v=DMARC1; p=quarantine; rua=mailto:[email protected]"

Preventing Email Injection

PHPMailer automatically sanitizes email addresses and headers to prevent injection attacks.

Sensitive Information

Never include in emails:

Instead:

SMTP Credentials

Protect SMTP credentials:

  1. Use environment variables:

    // In config/neuron.yaml
    email:
      password: ${SMTP_PASSWORD}
    
  2. Exclude from version control

  3. Rotate credentials regularly

Troubleshooting

SMTP Connection Failures

Symptom: Failed to send email with connection error

Solutions:

  1. Verify SMTP settings in config/neuron.yaml
  2. Check firewall rules for SMTP ports (25, 465, 587)
  3. Verify encryption setting matches port (TLS: 587, SSL: 465)
  4. Test connection with telnet: telnet smtp.gmail.com 587

Authentication Failures

Solutions:

  1. Verify credentials in config
  2. Gmail: Use App Password instead of account password
  3. Check if SMTP authentication is enabled for the account

Emails Going to Spam

Solutions:

  1. Configure SPF, DKIM, DMARC records
  2. Use authenticated SMTP (not PHP mail())
  3. Avoid spam trigger words
  4. Include proper unsubscribe links
  5. Maintain consistent From address

Template Not Found

Symptom: RuntimeException: Email template not found

Solutions:

  1. Verify template exists in resources/views/emails/
  2. Check template path in code (should be emails/template-name)
  3. Ensure basePath is correctly set in Sender constructor

Best Practices

Use Templates

Always use templates for consistent formatting:

// Good - uses template
$sender->template( 'emails/welcome', $variables );

// Avoid - inline HTML
$sender->body( '<html>...</html>' );

Handle Exceptions

Always wrap email sending in try-catch:

try
{
    $sender->to( $email )->template( 'emails/welcome', $vars )->send();
}
catch( \Exception $e )
{
    Log::error( "Failed to send welcome email: " . $e->getMessage() );
    // Don't block user flow due to email failure
}

Validate Email Addresses

Validate before sending:

if( !filter_var( $email, FILTER_VALIDATE_EMAIL ) )
{
    throw new \InvalidArgumentException( "Invalid email: $email" );
}

Test Email Rendering

Test templates across different email clients:

Implement Rate Limiting

Prevent abuse with rate limiting:

// Example using cache
$key = "email_reset:{$userId}";
$attempts = $cache->get( $key, 0 );

if( $attempts >= 3 )
{
    throw new \Exception( 'Too many attempts. Try again later.' );
}

$sender->send();
$cache->set( $key, $attempts + 1, 3600 ); // 1 hour

Limitations

The current email implementation has these limitations:

  1. No Queue Support: Emails are sent synchronously. There is no built-in queue() method.
  2. No Multiple Recipients Arrays: The to(), cc(), bcc() methods accept single recipients only (can be called multiple times)
  3. No Alternative Body: No separate method for plain text alternatives
  4. Limited Headers: No methods for custom headers, priority, or encoding
  5. No HTML/Text Versions: Templates are HTML-only, no automatic text version generation
  6. No Inline Attachments: Only file attachments supported
  7. Basic Logging: Simple info/error logging only

For advanced email features, consider extending the Sender class or implementing a custom email service.

Additional Resources