Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: CI

on: [pull_request]

jobs:
format:
name: Format
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run format check
run: |
docker run --rm -v $PWD:/app composer:2.7 sh -c \
"composer install --profile --ignore-platform-reqs && composer format:check"

analyze:
name: Analyze
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run static analysis
run: |
docker run --rm -v $PWD:/app composer:2.7 sh -c \
"composer install --profile --ignore-platform-reqs && composer analyze"

unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run unit tests
run: |
docker run --rm -v $PWD:/app composer:2.7 sh -c \
"composer install --profile --ignore-platform-reqs && composer test -- --testsuite unit"

e2e-tests:
name: E2E Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build image
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: smtp-dev
load: true
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Start SMTP Server
run: |
docker compose up -d
sleep 5

- name: Run E2E tests
run: docker compose exec -T smtp-server vendor/bin/phpunit --configuration phpunit.xml --testsuite e2e
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/vendor/
/.idea/
out.log
.phpunit.result.cache
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM composer:2.7 AS composer

ARG TESTING=false
ENV TESTING=$TESTING

WORKDIR /usr/local/src/

COPY composer.lock /usr/local/src/
COPY composer.json /usr/local/src/

RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist

FROM appwrite/base:0.11.3 AS final

LABEL maintainer="team@appwrite.io"

WORKDIR /usr/src/code

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

RUN echo "opcache.enable_cli=1" >> $PHP_INI_DIR/php.ini

RUN echo "memory_limit=1024M" >> $PHP_INI_DIR/php.ini

COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor

COPY ./tests /usr/src/code/tests
COPY ./src /usr/src/code/src
COPY ./phpunit.xml /usr/src/code/phpunit.xml
COPY ./phpstan.neon /usr/src/code/phpstan.neon
COPY ./pint.json /usr/src/code/pint.json

CMD [ "tail", "-f", "/dev/null" ]
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 utopia

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
119 changes: 117 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,117 @@
# smtp
Lite & fast micro PHP SMTP framework that is easy to learn.
# Utopia SMTP

[![Tests](https://github.com/utopia-php/smtp/actions/workflows/ci.yml/badge.svg)](https://github.com/utopia-php/smtp/actions/workflows/ci.yml)
[![Packagist Version](https://img.shields.io/packagist/v/utopia-php/smtp.svg)](https://packagist.org/packages/utopia-php/smtp)

Utopia SMTP is a modern PHP 8.3 toolkit for building SMTP servers and clients. It provides a fully-typed RFC 5321/5322 message encoder/decoder, pluggable handlers, transports, and telemetry hooks so you can receive and relay email with minimal effort.

Although part of the [Utopia Framework](https://github.com/utopia-php/framework) family, the library is framework-agnostic and can be used in any PHP project.

## Installation

```bash
composer require utopia-php/smtp
```

The library requires PHP 8.3+ with the `ext-sockets` extension. The Swoole adapter additionally needs the `ext-swoole` extension.

## Quick start

Create an SMTP server by wiring an adapter (TCP socket implementation) and a handler (how messages are accepted). The example below uses the native PHP socket adapter and the in-memory handler.

```php
<?php

require __DIR__ . '/vendor/autoload.php';

use Utopia\SMTP\Adapter\Native;
use Utopia\SMTP\Handler\Memory;
use Utopia\SMTP\Server;

$adapter = new Native('0.0.0.0', 2525);

$handler = new Memory(
allowedRecipients: ['inbox@example.test'],
allowedSenders: ['relay@example.test'],
);

$server = new Server($adapter, $handler, 'mail.example.test');
$server->setDebug(true);

$server->start();
```

Implement the [`Utopia\SMTP\Handler`](src/SMTP/Handler.php) interface to accept messages from databases, queues, or other stores.

## Handlers

- `Memory`: stores accepted messages in memory for testing or simple workloads
- `Proxy`: relays accepted messages to another SMTP server using the bundled client

Handlers can be combined with any adapter. Implementing the `Handler` interface allows you to plug in custom logic while reusing protocol and telemetry tooling.

## Adapters

- `Native`: pure PHP TCP server based on `ext-sockets`
- `Swoole`: non-blocking TCP server built on the Swoole runtime

Adapters are responsible only for accepting TCP connections. They call back into the server with a `Connection` so your handler logic stays isolated.

## SMTP client

The bundled client can deliver messages to any SMTP server.

```php
<?php

require __DIR__ . '/vendor/autoload.php';

use Utopia\SMTP\Client;
use Utopia\SMTP\Message;
use Utopia\SMTP\Message\Address;

$client = new Client('127.0.0.1', 2525);

$message = Message::create(
from: new Address('sender@example.test'),
to: new Address('inbox@example.test'),
subject: 'Hello from Utopia SMTP',
body: 'Plain text body',
);

$client->send($message);
```

## Transports

- `Socket`: sends messages through the bundled SMTP client (mirrors DNS resolver transports)

```php
use Utopia\SMTP\Message;
use Utopia\SMTP\Message\Address;
use Utopia\SMTP\Transport\Socket;

$transport = new Socket('127.0.0.1', 2525);
$transport->send(Message::create(
new Address('sender@example.test'),
new Address('inbox@example.test'),
'Subject',
'Body',
));
```

## Telemetry

`Server::setTelemetry()` accepts any adapter from [`utopia-php/telemetry`](https://github.com/utopia-php/telemetry). Counters (`smtp.sessions.total`, `smtp.messages.total`) and a histogram (`smtp.session.duration`) are emitted automatically.

## Development

- Install dependencies: `composer install`
- Static analysis: `composer analyze`
- Coding standards: `composer format:check` (use `composer format` to auto-fix)
- Tests: `composer test`
- Sample server for manual and E2E testing: `docker compose up`

## License

MIT
46 changes: 46 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "utopia-php/smtp",
"description": "Lite & fast micro PHP SMTP server abstraction that is **easy to use**.",
"type": "library",
"keywords": ["php", "framework", "upf", "utopia", "smtp", "email"],
"license": "MIT",
"minimum-stability": "stable",
"scripts": {
"format": "./vendor/bin/pint --config pint.json",
"format:check": "./vendor/bin/pint --test --config pint.json",
"analyze": "./vendor/bin/phpstan analyse --level max -c phpstan.neon src tests",
"test": "./vendor/bin/phpunit --configuration phpunit.xml"
},
"authors": [
{
"name": "Eldad Fux",
"email": "eldad@appwrite.io"
}
],
"autoload": {
"psr-4": {"Utopia\\SMTP\\": "src/SMTP"}
},
"autoload-dev": {
"psr-4": {
"Tests\\Unit\\Utopia\\": "tests/unit/"
}
},
"require": {
"php": ">=8.3",
"utopia-php/span": "1.1.*",
"utopia-php/telemetry": "*",
"utopia-php/validators": "0.*"
},
"require-dev": {
"swoole/ide-helper": "5.1.8",
"phpunit/phpunit": "12.5.*",
"laravel/pint": "1.29.*",
"phpstan/phpstan": "2.0.*"
},
"config": {
"allow-plugins": {
"php-http/discovery": true,
"tbachert/spi": true
}
}
}
Loading
Loading