gatovel / mail
Sistema modular e independente de envio de e-mails para aplicações PHP.
Requires
- php: ^8.3
- phpmailer/phpmailer: ^7.0
Requires (Dev)
- vlucas/phpdotenv: ^5.7
Suggests
- gatovel/queue: Optional integration for asynchronous email delivery.
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-13 15:10:28 UTC
README
A modular and independent PHP library for sending emails through SMTP, with support for HTML templates, built-in themes, attachments, asynchronous delivery, and optional queue integration.
Designed for Gatovel Framework, but compatible with any PHP 8.3+ application.
Features
- SMTP email delivery powered by PHPMailer
- Plain-text and HTML email messages
- File attachments
- HTML templates with variable substitution
- Automatic HTML escaping for template variables
- Two built-in email themes
- Gatovel theme enabled by default
- Array transport for testing
- Transport abstraction through interfaces
- Dependency injection
- Dedicated mail exception handling
- Optional Gatovel Queue integration
- Asynchronous email delivery
- Delayed email jobs
- Queue retry support
- Optional idempotency for queued emails
- Independent and modular architecture
Requirements
- PHP 8.3 or higher
- Composer
- SMTP server access for real email delivery
- Gatovel Queue (optional, for asynchronous delivery)
Installation
Once published on Packagist, install Gatovel Mail using Composer:
composer require gatovel/mail
For asynchronous email delivery, install the optional Queue package:
composer require gatovel/mail gatovel/queue
Gatovel Mail works independently without Gatovel Queue for synchronous email delivery.
Quick Start
Create an SMTP transport and send your first email.
<?php require 'vendor/autoload.php'; use Gatovel\Mail\MailManager; use Gatovel\Mail\messages\Message; use Gatovel\Mail\transports\SmtpTransport; $transport = new SmtpTransport( host: 'smtp.example.com', port: 587, username: 'your-username', password: 'your-password', encryption: 'tls' ); $mail = new MailManager($transport); $message = (new Message()) ->from('sender@example.com') ->to('recipient@example.com') ->subject('Welcome to Gatovel') ->text('Hello from Gatovel Mail!'); $mail->send($message);
SMTP Configuration
The SMTP transport supports the following settings:
| Parameter | Description |
|---|---|
host |
SMTP server hostname |
port |
SMTP server port |
username |
SMTP authentication username |
password |
SMTP authentication password |
encryption |
tls, ssl, or none |
For secure SMTP connections, use the encryption mode supported by your provider.
Never commit real SMTP credentials to version control.
HTML Emails
Gatovel Mail supports both HTML content and plain-text alternatives.
$message = (new Message()) ->from('sender@example.com') ->to('recipient@example.com') ->subject('Welcome') ->text('Welcome to our application!') ->html('<h1>Welcome!</h1><p>Thanks for joining us.</p>'); $mail->send($message);
HTML Templates
Gatovel Mail includes a simple template renderer for reusable email content.
Creating a Template
Create the file:
templates/welcome.html
<h2>Hello, {{ name }}!</h2> <p>Welcome to {{ app }}.</p> <p>Thank you for joining us!</p>
Rendering a Template
use Gatovel\Mail\templates\TemplateRenderer; $renderer = new TemplateRenderer( __DIR__ . '/templates' ); $html = $renderer->render('welcome.html', [ 'name' => 'Carlos', 'app' => 'Gatovel Framework', ]);
Template variables are automatically HTML-escaped to help prevent HTML injection.
The template renderer also applies a built-in email theme.
Sending a Rendered Template
$message = (new Message()) ->from('sender@example.com') ->to('recipient@example.com') ->subject('Welcome to Gatovel') ->text('Welcome to Gatovel Framework!') ->html($html); $mail->send($message);
Built-in Themes
Gatovel Mail includes two email themes.
Gatovel Theme
The Gatovel theme is the default theme.
It features a dark-blue background with green accents.
$renderer = new TemplateRenderer( __DIR__ . '/templates' );
You can also select it explicitly:
$renderer = new TemplateRenderer( __DIR__ . '/templates', theme: 'gatovel' );
Default Theme
The alternative theme provides a clean, light appearance with a white background and dark text.
$renderer = new TemplateRenderer( __DIR__ . '/templates', theme: 'default' );
Both themes are built into the package.
File Attachments
Attach files to outgoing messages.
$message = (new Message()) ->from('sender@example.com') ->to('recipient@example.com') ->subject('Your Documents') ->text('Please find the attached documents.') ->attach(__DIR__ . '/documents/invoice.pdf') ->attach( __DIR__ . '/documents/report.pdf', 'monthly-report.pdf' ); $mail->send($message);
Attachment files must exist and be readable.
For queued emails, attachment files must remain accessible to the worker until the job is processed.
Array Transport
The Array Transport stores messages in memory instead of sending them through SMTP.
This is useful for testing and development.
use Gatovel\Mail\MailManager; use Gatovel\Mail\messages\Message; use Gatovel\Mail\transports\ArrayTransport; $transport = new ArrayTransport(); $mail = new MailManager($transport); $message = (new Message()) ->from('sender@example.com') ->to('recipient@example.com') ->subject('Test Email') ->text('This is a test message.'); $mail->send($message); // Number of stored messages echo $transport->count(); // Retrieve stored messages $messages = $transport->messages(); // Clear stored messages $transport->clear();
No real email is sent when using ArrayTransport.
Optional Queue Integration
Gatovel Mail supports asynchronous email delivery through the optional gatovel/queue package.
The core SMTP functionality does not require Gatovel Queue.
Installing Queue Support
composer require gatovel/mail gatovel/queue
Dispatching an Email
<?php require 'vendor/autoload.php'; use Gatovel\Mail\messages\Message; use Gatovel\Mail\queue\MailDispatcher; use Gatovel\Queue\drivers\DatabaseDriver; $pdo = new PDO( 'sqlite:' . __DIR__ . '/queue.sqlite' ); $queue = new DatabaseDriver($pdo); $queue->createTable(); $dispatcher = new MailDispatcher($queue); $message = (new Message()) ->from('store@example.com') ->to('customer@example.com') ->subject('Order Confirmation') ->text('Your order has been confirmed.'); $jobId = $dispatcher->dispatch($message);
Dispatching stores the email job in the queue. It does not send the email immediately.
Delayed Emails
Schedule an email for later processing by specifying a delay in seconds.
$dispatcher->dispatch( message: $message, delay: 30 );
The job becomes available for processing after the configured delay.
Processing Queued Emails
A Queue worker processes pending jobs and delivers messages through a configured mail transport.
<?php require 'vendor/autoload.php'; use Gatovel\Mail\MailManager; use Gatovel\Mail\jobs\SendMailJob; use Gatovel\Mail\transports\SmtpTransport; use Gatovel\Queue\drivers\DatabaseDriver; use Gatovel\Queue\workers\Worker; $pdo = new PDO( 'sqlite:' . __DIR__ . '/queue.sqlite' ); $queue = new DatabaseDriver($pdo); $queue->createTable(); $transport = new SmtpTransport( host: 'smtp.example.com', port: 587, username: 'your-username', password: 'your-password', encryption: 'tls' ); $mailer = new MailManager($transport); $worker = new Worker( queue: $queue, jobFactory: static function ( string $className ) use ($mailer): object { if ($className !== SendMailJob::class) { throw new RuntimeException( 'Unsupported job class.' ); } return new SendMailJob($mailer); } ); $worker->runOnce();
The worker should run in a separate process in production.
Queue Retry Support
Gatovel Queue supports retrying jobs after temporary failures.
If an SMTP delivery attempt fails, the Queue worker can release the job for another attempt.
The default RetryPolicy supports:
- Maximum of 3 attempts
- Initial retry delay of 5 seconds
- Exponential backoff
- Maximum retry delay of 300 seconds
Retry behavior is controlled by Gatovel Queue.
A successful SMTP delivery followed by a worker failure may result in the same email being delivered again.
Message Serialization
MessageSerializer converts email messages into queue-compatible arrays.
The worker reconstructs the original message before sending it.
Supported message information includes:
- Sender
- Recipient
- Subject
- Plain-text content
- HTML content
- Attachment paths and names
Attachments are serialized by file path, not by file contents.
The application must ensure that attachment files remain available until the worker processes the job.
Optional Idempotency
Gatovel Mail supports optional idempotency keys for queued email operations.
Idempotency helps suppress repeated deliveries when a previous successful delivery has already been recorded.
Dispatching with an Idempotency Key
$dispatcher->dispatch( message: $message, idempotencyKey: 'order-1001-confirmation' );
The idempotency key should uniquely identify a logical email operation.
For example:
order-1001-confirmationorder-1001-shippinguser-500-welcome
Different operations should use different keys.
Idempotency Contract
Gatovel Mail defines the following interface:
namespace Gatovel\Mail\contracts; interface MailIdempotencyInterface { public function isCompleted(string $key): bool; public function markCompleted(string $key): bool; }
Applications may implement this interface using SQLite, Redis, or another persistent storage mechanism.
Integrating with Gatovel Queue
Gatovel Queue includes an IdempotencyStore implementation backed by SQLite.
An adapter can connect it to Gatovel Mail.
use Gatovel\Mail\contracts\MailIdempotencyInterface; use Gatovel\Queue\idempotency\IdempotencyStore; final class QueueIdempotencyAdapter implements MailIdempotencyInterface { public function __construct( private readonly IdempotencyStore $store ) { } public function isCompleted(string $key): bool { return $this->store->isCompleted($key); } public function markCompleted(string $key): bool { return $this->store->markCompleted($key); } }
Create the storage and adapter:
$store = new IdempotencyStore($pdo); $store->createTable(); $adapter = new QueueIdempotencyAdapter($store);
Then inject the adapter into the email job:
$job = new SendMailJob( mailer: $mailer, idempotency: $adapter );
When using a Queue worker, provide this configured job through its job factory.
Idempotency Behavior
When an idempotency key is provided and an idempotency implementation is configured:
- The job checks whether the key is already completed.
- If completed, the email is not sent again.
- If not completed, the email is sent.
- After successful delivery, the key is registered as completed.
Without an idempotency implementation, queued emails work normally without deduplication.
Delivery Guarantees and Limitations
Idempotency reduces duplicate email deliveries, but it does not guarantee exactly-once delivery.
Important limitations:
- A worker may crash after SMTP acceptance but before recording completion.
- A retry after that crash may send the email again.
- Concurrent workers may check the same key before either records completion.
- SMTP delivery and SQLite transactions cannot be committed atomically.
Applications requiring stronger guarantees should consider provider-specific idempotency features, delivery tracking, and additional coordination mechanisms.
Error Handling
SMTP delivery errors are wrapped in a dedicated MailException.
use Gatovel\Mail\exceptions\MailException; try { $mail->send($message); } catch (MailException $exception) { echo $exception->getMessage(); // Retrieve the original exception $previous = $exception->getPrevious(); }
Invalid arguments or configuration values may throw InvalidArgumentException.
Queue processing errors and retry behavior are handled by Gatovel Queue.
Architecture
Gatovel Mail follows a modular architecture.
src/
├── MailManager.php
├── attachments/
│ └── Attachment.php
├── contracts/
│ ├── MailIdempotencyInterface.php
│ └── MailInterface.php
├── exceptions/
│ └── MailException.php
├── jobs/
│ └── SendMailJob.php
├── messages/
│ └── Message.php
├── queue/
│ └── MailDispatcher.php
├── serialization/
│ └── MessageSerializer.php
├── templates/
│ └── TemplateRenderer.php
├── themes/
│ ├── DefaultTheme.php
│ └── GatovelTheme.php
└── transports/
├── ArrayTransport.php
└── SmtpTransport.php
Core Components
MailManager
Coordinates email delivery through a transport implementing MailInterface.
Message
Represents an outgoing email, including sender, recipient, subject, content, and attachments.
MailInterface
Defines the contract for email transports.
SmtpTransport
Delivers emails using PHPMailer.
ArrayTransport
Stores outgoing messages in memory for testing.
TemplateRenderer
Renders HTML templates with escaped variables and applies a built-in theme.
Attachment
Represents and validates email attachments.
MailDispatcher
Dispatches serialized email messages to Gatovel Queue.
SendMailJob
Processes queued email jobs and sends reconstructed messages through MailInterface.
MessageSerializer
Converts email messages into queue-compatible payloads and reconstructs them during processing.
MailIdempotencyInterface
Defines the optional contract for tracking completed email operations.
Testing
Gatovel Mail has been tested with automated checks covering:
- Message creation and validation
- Mail manager behavior
- SMTP transport configuration
- Array transport behavior
- File attachments
- HTML template rendering
- Built-in themes
- Exception handling
- Queue integration
- SQLite job persistence
- Worker processing
- Retry after temporary failures
- Sequential idempotency
- Duplicate delivery risk scenarios
SMTP integration has also been tested with Mailtrap.
Queue Integration Tests
The local development test suite includes:
tests/
├── MailQueueIntegrationTest.php
├── MailQueueDatabaseTest.php
├── MailQueueRetryTest.php
├── MailQueueIdempotencyTest.php
└── MailQueueDuplicateTest.php
These tests validate asynchronous processing and queue integration.
The duplicate delivery test intentionally reproduces a scenario in which the same message is delivered more than once.
The tests/ directory is excluded from the published Git repository in the current development configuration.
Roadmap
Current capabilities include SMTP delivery, HTML templates, built-in themes, attachments, optional Queue integration, asynchronous delivery, retry support, and optional idempotency.
Potential future improvements include:
- Additional email transports
- Delivery metrics and monitoring
- Provider-specific delivery deduplication
- Improved concurrent processing safeguards
- Additional extensibility options
Queue integration will remain optional to preserve the package's independence.
Contributing
Contributions, suggestions, and bug reports are welcome.
Please open an issue or submit a pull request through the GitHub repository:
https://github.com/ttrikingG/gatovel-mail
License
Gatovel Mail is open-source software licensed under the MIT License.
Copyright (c) 2026 Tom Garcia.
Developed as part of the Gatovel ecosystem.