Introduction
Slenix includes a built-in mail class for sending transactional emails without any external dependencies. It supports both PHP's native mail() function and direct SMTP connections with TLS/SSL encryption — ideal for welcome emails, password resets, invoice delivery, and bulk notifications.
The class is available at Slenix\Supports\Mail\Mail and is configured automatically from your .env file.
use Slenix\Supports\Mail\Mail;
(new Mail())
->from('no-reply@example.com', 'My App')
->to('user@example.com')
->subject('Welcome!')
->message('<h1>Your account is ready.</h1>', true)
->send();Configuration
Environment File (Recommended)
Set your mail credentials in .env. The Mail class reads these values automatically on instantiation:
EMAIL_METHOD=smtp
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USERNAME=your_username
SMTP_PASSWORD=your_password
SMTP_ENCRYPTION=tls # tls | ssl | none
SMTP_AUTH=true
SMTP_TIMEOUT=30SMTP Providers
Common provider settings:
| Provider | Host | Port | Encryption |
|---|---|---|---|
| Mailtrap (dev) | sandbox.smtp.mailtrap.io | 2525 | tls |
| Gmail | smtp.gmail.com | 587 | tls |
| Outlook / Hotmail | smtp-mail.outlook.com | 587 | tls |
| SendGrid | smtp.sendgrid.net | 587 | tls |
| Mailgun | smtp.mailgun.org | 587 | tls |
Gmail: You must use an App Password instead of your normal account password. Enable 2-Step Verification first, then generate an app-specific password.
Manual Configuration
You can also configure SMTP programmatically using the smtp() method, which overrides .env values:
(new Mail())
->smtp([
'host' => 'smtp.gmail.com',
'port' => 587,
'username' => 'you@gmail.com',
'password' => 'your-app-password',
'encryption' => 'tls',
'auth' => true,
'timeout' => 30,
])
->from('you@gmail.com', 'My App')
->to('user@example.com')
->subject('Hello')
->message('Hello from Slenix.')
->send();Configuration Options
| Option | Default | Description |
|---|---|---|
host | localhost | SMTP server hostname |
port | 587 | SMTP server port |
username | '' | SMTP username |
password | '' | SMTP password |
encryption | tls | Encryption: tls, ssl, or none |
auth | true | Whether to authenticate |
timeout | 30 | Connection timeout in seconds |
Sending Methods
Slenix Mail supports two sending backends, configured via method():
// PHP's native mail() — suitable for simple local setups
(new Mail())->method('mail')->...->send();
// SMTP — recommended for production
(new Mail())->method('smtp')->...->send();When smtp() is called directly, the method is automatically switched to SMTP. For production environments, always prefer SMTP with authentication over mail().
Building and Sending Emails
Basic Email
use Slenix\Supports\Mail\Mail;
$sent = (new Mail())
->from('no-reply@example.com', 'Example App')
->to('user@example.com')
->subject('Welcome to Example App')
->message('Thank you for signing up.')
->send();
if (!$sent) {
// Handle failure
}HTML Emails
Pass true as the second argument to message() to send HTML:
$html = '<h1>Welcome, ' . $user->name . '!</h1>'
. '<p>Your account has been created successfully.</p>'
. '<p><a href="https://example.com/login">Log in now</a></p>';
(new Mail())
->from('no-reply@example.com', 'Example App')
->to($user->email)
->subject('Welcome!')
->message($html, true)
->send();Emails With Attachments
Call attach() once per file. The file must exist on the server. Slenix encodes attachments as Base64 and sends them as multipart/mixed:
(new Mail())
->from('billing@example.com', 'Example Billing')
->to('customer@example.com')
->subject('Invoice #1234')
->message('Please find your invoice attached.')
->attach(storage_path('invoices/invoice-1234.pdf'))
->attach(storage_path('invoices/receipt-1234.pdf'))
->send();Multiple Recipients
Call to() multiple times or loop over a list. Invalid email addresses are silently discarded:
$mail = (new Mail())
->from('newsletter@example.com', 'Example Newsletter')
->subject('This week in Slenix')
->message($htmlContent, true);
foreach ($subscribers as $subscriber) {
$mail->to($subscriber->email);
}
$mail->send();Using Luna Templates
Render a Luna template and pass the result to message():
(new Mail())
->from('no-reply@example.com', 'Example App')
->to($user->email)
->subject('Welcome, ' . $user->name . '!')
->message(view('emails.welcome', ['user' => $user]), true)
->send();view() returns the compiled HTML string, which is passed directly as the email body.
Practical Examples
Welcome Email (Registration)
// In AuthController or UserService
public function register(Request $request, Response $response): void
{
$user = User::create([
'name' => $request->input('name'),
'email' => $request->input('email'),
'password' => $request->input('password'),
]);
(new Mail())
->from('no-reply@example.com', 'Example App')
->to($user->email)
->subject('Welcome to Example App, ' . $user->name . '!')
->message(view('emails.welcome', ['user' => $user]), true)
->send();
$response->redirect('/dashboard');
}Password Reset
public function sendResetLink(Request $request): void
{
$user = User::firstWhere('email', $request->input('email'));
$token = bin2hex(random_bytes(32));
// Store token in DB...
$link = env('APP_BASE_URL') . '/reset-password?token=' . $token;
(new Mail())
->from('no-reply@example.com', 'Example App')
->to($user->email)
->subject('Reset your password')
->message(view('emails.password-reset', ['link' => $link, 'user' => $user]), true)
->send();
}Invoice With PDF Attachment
public function sendInvoice(Order $order): bool
{
$pdfPath = storage_path("invoices/invoice-{$order->id}.pdf");
return (new Mail())
->from('billing@example.com', 'Example Billing')
->to($order->customer->email)
->subject("Invoice #{$order->id}")
->message(view('emails.invoice', ['order' => $order]), true)
->attach($pdfPath)
->send();
}Notification With Manual SMTP Override
Useful when you need to send from a different SMTP account within the same request:
(new Mail())
->smtp([
'host' => 'smtp.sendgrid.net',
'port' => 587,
'username' => 'apikey',
'password' => env('SENDGRID_API_KEY'),
'encryption' => 'tls',
'auth' => true,
])
->from('alerts@example.com', 'Example Alerts')
->to('admin@example.com')
->subject('New order received')
->message("Order #{$order->id} has been placed by {$order->customer->name}.")
->send();Debugging
The Mail class maintains an internal SMTP conversation log — every command sent and every server response received. Use getDebugLog() to inspect it when a send fails:
$mail = (new Mail())
->from('test@local.dev')
->to('dev@example.com')
->subject('SMTP Test')
->message('Testing SMTP connection.')
->smtp([
'host' => 'smtp.mailtrap.io',
'port' => 2525,
'username' => env('SMTP_USERNAME'),
'password' => env('SMTP_PASSWORD'),
'encryption' => 'tls',
'auth' => true,
]);
if (!$mail->send()) {
// Print the full SMTP conversation
echo nl2br($mail->getDebugLog());
exit;
}The log output looks like:
>> EHLO my-server.local
<< 250-smtp.mailtrap.io Hello
>> AUTH LOGIN
<< 334 VXNlcm5hbWU6
>> [base64 username]
<< 334 UGFzc3dvcmQ6
>> [base64 password]
<< 235 2.0.0 OK Authenticated
>> MAIL FROM: <test@local.dev>
<< 250 2.1.0 OK
...Clear the log between sends when reusing an instance:
$mail->clearDebugLog();SMTP Internals
Understanding how Slenix connects helps when diagnosing delivery issues.
TLS (STARTTLS — port 587): Opens a plain connection, sends EHLO, then upgrades to TLS via STARTTLS before authenticating. This is the recommended option for most providers.
SSL (implicit TLS — port 465): Wraps the socket in SSL from the first byte using ssl:// in the hostname. Use this if your provider requires port 465.
none (port 25): Plain connection with no encryption. Only appropriate for internal mail relays on trusted networks. Never use in production over the internet.
The authentication sequence uses AUTH LOGIN, encoding credentials with Base64. This is standard for most SMTP providers including Gmail, SendGrid, and Mailgun.
Security
Always use SMTP in production. PHP's native mail() function bypasses your SMTP credentials, makes it easy for spammers to abuse shared hosting environments, and typically results in lower deliverability.
Store credentials in .env. Never hardcode passwords or API keys directly in code:
// ✅ Correct
'password' => env('SMTP_PASSWORD'),
// ❌ Wrong
'password' => 'my-secret-password',Header injection is mitigated by the fluent API — user-supplied values should be sanitised before being passed to from(), subject(), or message(). Avoid passing raw $_POST values directly:
// ✅ Sanitise first
->subject(htmlspecialchars($request->input('subject'), ENT_QUOTES, 'UTF-8'))
// ❌ Raw user input directly into subject
->subject($request->input('subject'))Email validation is enforced automatically — to() uses filter_var($email, FILTER_VALIDATE_EMAIL) and silently discards invalid addresses.
Attachments are Base64-encoded and sent as Content-Transfer-Encoding: base64, which is safe for binary files. Only files that exist on disk (file_exists()) are attached.
Method Reference
| Method | Returns | Description |
|---|---|---|
from(email, name?) | self | Set sender address and optional display name |
to(email) | self | Add a recipient (validated, can be called multiple times) |
subject(string) | self | Set email subject |
message(string, isHtml?) | self | Set email body; pass true for HTML |
attach(filePath) | self | Attach a file (must exist on disk) |
method(string) | self | Set sending method: mail or smtp |
smtp(array) | self | Configure SMTP and switch method to SMTP |
send() | bool | Send the email; returns true on success |
getDebugLog() | string | Return the full SMTP conversation log |
clearDebugLog() | void | Clear the debug log |