PHPackages                             xakki/emailer - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. xakki/emailer

ActiveLibrary[Mail &amp; Notifications](/categories/mail)

xakki/emailer
=============

Self-hosted transactional &amp; notification email service with templating, tracking, subscription management and an HTTP/console API

52PHPCI passing

Since May 22Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/Xakki/emailer)[ Packagist](https://packagist.org/packages/xakki/emailer)[ RSS](/packages/xakki-emailer/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependenciesVersions (4)Used By (0)

Emailer
=======

[](#emailer)

[![CI](https://github.com/Xakki/emailer/actions/workflows/ci.yml/badge.svg)](https://github.com/Xakki/emailer/actions/workflows/ci.yml)[![PHP](https://camo.githubusercontent.com/6882d9fcb27b9b16f02b1f1277c5c441c7f2d3c10bb87881461cf2b110683f33/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d382e34253230253743253230382e352d3737376262342e737667)](https://www.php.net/)[![License](https://camo.githubusercontent.com/1c7d7671effd64df0ad09c2aa87a73bfaf614a8ec5e72dbe870c8635b57bb1ce/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d47504c2d2d332e302d2d6f722d2d6c617465722d626c75652e737667)](LICENSE)

Self‑hosted transactional &amp; notification email service for PHP. It stores projects, campaigns, templates and recipients in a relational database, renders HTML emails from reusable template blocks, sends them over SMTP (PHPMailer), and tracks opens, clicks and (un)subscriptions through a small set of HTTP endpoints.

> The public surface is a plain PHP library (`Xakki\Emailer\Emailer`) plus an HTTP front controller and a console runner. There is no framework lock‑in — you wire it into your own app, container, or the bundled Docker stack.

Features
--------

[](#features)

- **Projects → campaigns → templates → queue** domain model on top of Doctrine DBAL 4.
- **Template engine** with `{{placeholder}}` substitution, reusable wrapper / content / block templates and per‑project parameters.
- **SMTP delivery** via PHPMailer with DKIM support and SMTP error classification (spam / quota / invalid mailbox / temporary, …).
- **Open &amp; click tracking**: tracking pixel, link rewriting, and per‑recipient statistics.
- **Subscription management**: one‑click subscribe / unsubscribe endpoints, `List-Unsubscribe` header.
- **HTTP API** (Phroute router) and a **console runner** for queue processing and migrations.
- **Redis** caching for MX lookups and auth tokens; **Doctrine Migrations** for schema.
- Tested on **PHP 8.4 and 8.5**, static‑analysed with PHPStan (level 7) and PSR‑12 (strict).

Requirements
------------

[](#requirements)

- PHP **&gt;= 8.4** with extensions: `pdo`, `pdo_mysql`, `json`, `fileinfo`, `redis`, `intl`, `mbstring`
- A MySQL / MariaDB database
- Redis
- Composer 2

Installation
------------

[](#installation)

```
composer require xakki/emailer
```

Run the database migrations (against your configured connection):

```
./console migrations migrate
```

Configuration
-------------

[](#configuration)

Configuration is a plain PHP array passed to `ConfigService`. Only `db.password`is strictly required; everything else has sane defaults (see [`src/ConfigService.php`](src/ConfigService.php)).

```
use Xakki\Emailer\ConfigService;

$config = new ConfigService([
    'db' => [
        'driver'   => 'pdo_mysql',
        'host'     => '127.0.0.1',
        'port'     => 3306,
        'user'     => 'emailer',
        'password' => 'a-strong-unique-password', // required
        'dbname'   => 'emailer',
    ],
    'redis' => ['host' => '127.0.0.1', 'port' => 6379],

    // Optional: enables the read-only GET /emailer/get/{key}/{secret} accessor.
    'secret_key' => getenv('SECRET_EMAILER_KEY') ?: '',
]);
```

KeyDefaultNotes`db``pdo_mysql` localhost setDoctrine DBAL connection params; `password` required`redis``emailer-redis:6379`Used for MX / auth caches`route`built‑in tracking routesPhroute route → `[Controller, method]` map`migration``src/Migration`Doctrine Migrations config`secret_key``''` (disabled)Guards the read‑only body accessorUsage
-----

[](#usage)

### As a library

[](#as-a-library)

```
use Monolog\Logger;
use Xakki\Emailer\Emailer;
use Xakki\Emailer\Model\Template;
use Xakki\Emailer\Transports\Smtp;

$emailer = new Emailer($config, new Logger('emailer'));

// 1. One-time setup: project, templates, transport, notify channel, campaign.
$project = $emailer->createProject('My project', [
    Template::NAME_HOST     => 'mail.example.com',
    Template::NAME_ROUTE    => '/emailer',
    Template::NAME_LANG     => 'en',
    Template::NAME_URL_LOGO => __DIR__ . '/tpl/logo.png',
]);

$wrapper = $project->createTplWrapper('Base', file_get_contents('tpl/wrapper.html'));
$content = $project->createTplContent('News', file_get_contents('tpl/content.html'));
$notify  = $project->createNotify('Newsletter');

$smtp = new Smtp($emailer);
$smtp->fromEmail = 'robot@example.com';
$smtp->fromName  = 'Robot';
$smtp->host      = 'smtp.example.com';
$smtp->port      = 587;
$project->createTransport($smtp);

$campaign = $project->createCampaign('Welcome {{name}}', $wrapper, $content, $notify, []);

// 2. Queue a message for a recipient.
$mail = $emailer->getNewMail()
    ->setEmail('user@example.com')
    ->setEmailName('Jane Doe')
    ->setData(['name' => 'Jane']);

$hashRoute = $emailer
    ->getNewSender($campaign->project_id, $campaign->id)
    ->send($mail);
```

A runnable end‑to‑end example lives in [`example/as-vendor/init.php`](example/as-vendor/init.php).

### Processing the queue (console)

[](#processing-the-queue-console)

```
./console send         # send pending messages from the queue
./console newDay       # reset per-day transport counters (run daily via cron)
./console migrations migrate
```

### HTTP tracking endpoints

[](#http-tracking-endpoints)

The front controller (`Emailer::dispatchRoute()`) exposes the tracking surface. Default routes (see `ConfigService::$route`):

Method &amp; pathPurpose`GET /emailer/home/{key}`Click‑through landing / open marker`GET /emailer/goto/{key}/{url}`Tracked outbound link redirect`GET /emailer/logoimg/{key}`Tracking pixel (logo image)`GET /emailer/unsubscribe/{key}`One‑click unsubscribe`GET /emailer/subscribe/{key}`Re‑subscribe`GET /emailer/status/{key}`Per‑message delivery status page`GET /emailer/get/{key}/{secret}`Read‑only rendered body (secret‑gated; opaque 404 when `secret_key` is unset)```
echo $emailer->dispatchRoute($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
```

The JSON management API (login / dashboard / SMTP test) is documented in [`swagger.json`](swagger.json).

Docker
------

[](#docker)

A full dev stack (PHP‑FPM, Nginx, MariaDB, Redis) is provided:

```
cp .env_dist .env   # edit passwords
make build
make up
```

See the [`Makefile`](Makefile) for `migrations-*`, `phpunit`, `phpstan`, `cs-*` targets.

Development &amp; quality
-------------------------

[](#development--quality)

The CI matrix runs the whole tool‑chain on PHP 8.4 and 8.5. To reproduce locally you only need PHP with the extensions listed above plus Composer:

```
composer install

composer test          # PHPUnit
composer test-coverage # PHPUnit + HTML/text coverage (needs Xdebug or PCOV)
composer phpstan       # PHPStan level 7 (src + tests)
composer cs-check      # PSR-12 strict (squizlabs/php_codesniffer)
composer cs-fix        # auto-fix style
```

Current line coverage is **~70%**. Tests live in [`tests/`](tests/) and split into pure unit tests (mocked DBAL connection) and integration tests that run against an in‑memory SQLite database (`tests/Support/IntegrationCase.php`).

Project layout
--------------

[](#project-layout)

```
src/
  Emailer.php          Entry point / service locator
  ConfigService.php    Typed configuration
  Mail.php             Outgoing message value object
  Sender.php           Queues a message for a campaign
  Controller/          HTTP + console + JSON API controllers
  Model/               Active-record style domain models
  Repository/          Doctrine DBAL data access
  Cqrs/                Single-purpose command/query handlers
  Transports/          SMTP transport (PHPMailer)
  Migration/           Doctrine schema migration
  Helper/, Exception/, locale/, view/
tests/                 PHPUnit unit + integration suites
example/               Runnable usage examples
docker/                Local dev & CI images

```

Contributing
------------

[](#contributing)

Contributions are welcome — please read [CONTRIBUTING.md](CONTRIBUTING.md) first.

License
-------

[](#license)

Distributed under the **GNU General Public License v3.0 or later**. See [LICENSE](LICENSE).

###  Health Score

22

—

LowBetter than 21% of packages

Maintenance58

Moderate activity, may be stable

Popularity7

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity16

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/426468?v=4)[Xakki](/maintainers/Xakki)[@Xakki](https://github.com/Xakki)

---

Top Contributors

[![Xakki](https://avatars.githubusercontent.com/u/426468?v=4)](https://github.com/Xakki "Xakki (13 commits)")

### Embed Badge

![Health badge](/badges/xakki-emailer/health.svg)

```
[![Health](https://phpackages.com/badges/xakki-emailer/health.svg)](https://phpackages.com/packages/xakki-emailer)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
