Skip to content
Merged
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
7 changes: 5 additions & 2 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@ on:
jobs:
Build:
runs-on: 'ubuntu-latest'
container: 'byjg/php:${{ matrix.php-version }}-cli'
container:
image: 'byjg/php:${{ matrix.php-version }}-cli'
options: --user root --privileged
strategy:
matrix:
php-version:
- "8.4"
- "8.3"
- "8.2"
- "8.1"

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- run: composer install
- run: ./vendor/bin/phpunit

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ composer.lock
vendor
.idea
.phpunit.result.cache
phpunit.coverage.xml
phpunit.report.xml
*.bak
4 changes: 4 additions & 0 deletions .run/PHPUnit.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
<TestRunner configuration_file="$PROJECT_DIR$/phpunit.xml.dist" scope="XML" use_alternative_configuration_file="true" />
<method v="2" />
</configuration>
<configuration default="false" name="PHPUnit" type="PHPUnitRunConfigurationType" factoryName="PHPUnit">
<TestRunner configuration_file="$PROJECT_DIR$/phpunit.xml.dist" directory="$PROJECT_DIR$" scope="XML" use_alternative_configuration_file="true" />
<method v="2" />
</configuration>
</component>
8 changes: 8 additions & 0 deletions .run/psalm.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="psalm" type="ComposerRunConfigurationType" factoryName="Composer Script">
<option name="commandLineParameters" value="" />
<option name="pathToComposerJson" value="$PROJECT_DIR$/composer.json" />
<option name="script" value="psalm" />
<method v="2" />
</configuration>
</component>
243 changes: 47 additions & 196 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,223 +6,77 @@
[![GitHub license](https://img.shields.io/github/license/byjg/php-mailwrapper.svg)](https://opensource.byjg.com/opensource/licensing.html)
[![GitHub release](https://img.shields.io/github/release/byjg/php-mailwrapper.svg)](https://github.com/byjg/php-mailwrapper/releases/)

A lightweight wrapper for send mail. The interface is tottaly decoupled from the sender. The motivation is
create a single interface for sending mail doesn't matter the sender. There are three options available:
A lightweight wrapper for sending email. The interface is totally decoupled from the sender, providing a single interface for sending mail regardless of the underlying mail service.

- SMTP (with SSL/TLS)
- AWS SES (using API directly)
- Mailgun (using API directly)
## Available Wrappers

## How to use
- **SMTP** - SMTP with SSL/TLS support
- **AWS SES** - Amazon Simple Email Service (using API directly)
- **Mailgun** - Mailgun API (using API directly)
- **SendMail** - PHP's built-in mail() function
- **FakeSender** - For testing (does nothing)

The MailWrapper has your classes totally decoupled in three parts:
## Install

```shell
composer require "byjg/mailwrapper"
```

- The Envelope: the mail envelope. Defines the mail sender, recipients, body, subject, etc;
- The Mailer: the responsible to deal with the process of send the envelope
- The Register: Will register the available Mailers in the system.
## Documentation

### Envelope Class
- **[Getting Started](docs/getting-started.md)** - Installation, quick start, and architecture overview
- **[Envelope](docs/envelope.md)** - Creating and configuring email messages
- **[Connection Strings](docs/connection-strings.md)** - URI patterns for different mail services (SMTP, Mailgun, SES, etc.)
- **[Mailer Factory](docs/mailer-factory.md)** - Registering and creating mailers
- **[Attachments](docs/attachments.md)** - Sending attachments and embedded images
- **[Custom Wrappers](docs/custom-wrappers.md)** - Implementing your own mail wrapper
- **[Exceptions](docs/exceptions.md)** - Error handling and exception types

MailWrapper provides a envelope class with all the basic necessary attributes to create an email.
As this Envelope class are totally decoupled from the Mailer engine, you can use it also as DTO.
See an example below: (do not forget `require "vendor/autoload.php"`)
## Quick Start

```php
<?php
require "vendor/autoload.php";

// Create the email envelope
$envelope = new ByJG\Mail\Envelope();
$envelope = new \ByJG\Mail\Envelope();
$envelope->setFrom('johndoe@example.com', 'John Doe');
$envelope->addTo('jane@example.com');
$envelope->setSubject('Email Subject');
$envelope->setBody('html text body');
```

### Sending the email
$envelope->setBody('<h1>Hello World</h1>');

Once you have created the envelope you can send the email. Basically you have to register in the fabric all mailer
you intend to use and then create the mailer:

```php
<?php
// Register the available class
// Register available mailers
\ByJG\Mail\MailerFactory::registerMailer(\ByJG\Mail\Wrapper\PHPMailerWrapper::class);
\ByJG\Mail\MailerFactory::registerMailer(\ByJG\Mail\Wrapper\MailgunApiWrapper::class);

// Create the proper mailer based on the scheme
// In the example below will find the "mailgun" scheme
$mailer = \ByJG\Mail\MailerFactory::create('mailgun://api:YOUR_API_KEY@YOUR_DOMAIN');

// Send the email:
$mailer->send($envelope);
```

You can create the mailer directly without the factory:

```php
<?php
$mailer = new \ByJG\Mail\Wrapper\MailgunApiWrapper(
new \ByJG\Util\Uri(
'mailgun://your_api_key@YOUR_DOMAIN'
)
);

// Send the email:
$mailer->send($envelope);
```

### Sending attachment
// Create mailer from connection string
$mailer = \ByJG\Mail\MailerFactory::create('smtp://username:password@smtp.example.com:587');

```php
<?php
$envelope = new \ByJG\Mail\Envelope('from@email.com', 'to@email.com', 'Subject', 'Body');
$envelope->addAttachment('name_of_attachement', '/path/to/file', 'mime/type');
$mailer->send($envelope);
// Send the email
$result = $mailer->send($envelope);
```

### Adding attachment as Embed Image
## Architecture

Adding an image as a inline attachment (or Embed) your mail reader will not show as download but you can
use it as an local image in your email.
MailWrapper is organized into three main components:

See the example:
- **The Envelope**: The mail message. Defines the sender, recipients, body, subject, attachments, etc.
- **The Mailer**: Responsible for the process of sending the envelope
- **The Factory**: Registers and creates the available Mailers in the system

```php
<?php
$envelope = new \ByJG\Mail\Envelope('from@email.com', 'to@email.com', 'Subject');
$envelope->addEmbedImage('mycontentname', '/path/to/image', 'mime/type');
$envelope->setBody('<img src="cid:mycontentname" />');
$mailer->send($envelope);
```

## The connection url

To create a new sender you have to define a URL like that:

```text
scheme://username:password/smtpserver:port
```

The options are:

| Part | Description |
|:-----------|:--------------------------------------------------------------------------------------------------------|
| scheme | The email scheme: smtp, ssl, tls, mandrill and ses. Note that mandrill and ses use your own private api |
| username | The username |
| password | The password |
| smtpserver | The SMTP Host |
| port | The SMTP Port |

The protocols available are:

| Scheme | Description | URI Pattern | Mailer Object |
|:-----------|:-----------------------------------|:-----------------------------------------|:-------------------|
| smtp | SMTP over insecure connection | `smtp://username:password@host:25` | PHPMailerWrapper |
| tls | SMTP over secure TLS connection | `tls://username:password@host:587` | PHPMailerWrapper |
| ssl | SMTP over secure SSL connection | `ssl://username:password@host:587` | PHPMailerWrapper |
| sendmail | Sending Email using PHP mail() | `sendmail://localhost` | SendMailWrapper |
| mailgun | Sending Email using Mailgun API | `mailgun://YOUR_API_KEY@YOUR_DOMAIN` | MailgunApiWrapper |
| ses | Sending Email using Amazon AWS API | `ses://ACCESS_KEY_ID:SECRET_KEY@REGION` | AmazonSesWrapper |
| fakesender | Do nothing | `fakesender://anything` | FakeSenderWrapper |

### Gmail specifics

From December 2014, Google started imposing an authentication mechanism called
XOAUTH2 based on OAuth2 for access to their apps, including Gmail.
This change can break both SMTP and IMAP access to gmail, and you may receive
authentication failures (often "5.7.14 Please log in via your web browser")
from many email clients, including PHPMailer, Apple Mail, Outlook, Thunderbird and others.
The error output may include a link to
[https://support.google.com/mail/bin/answer.py?answer=78754](https://support.google.com/mail/bin/answer.py?answer=78754), which
gives a list of possible remedies.

There are two main solutions:

#### Sending through SMTP

You have to enable the option "Allow less secure apps".
It does not really make your app significantly less secure.
Reportedly, changing this setting may take an hour or more to take effect,
so don't expect an immediate fix. You can start changing
[here](https://www.google.com/settings/security/lesssecureapps)

The connection string for sending emails using SMTP through GMAIL is:

```text
tls://YOUREMAIL@gmail.com:YOURPASSWORD@smtp.gmail.com:587
```
## Connection URL Schemes

#### Sending Through XOAuth2
| Scheme | Description | URI Pattern |
|:-----------|:-----------------------------------|:-----------------------------------------|
| smtp | SMTP over insecure connection | `smtp://username:password@host:25` |
| tls | SMTP over secure TLS connection | `tls://username:password@host:587` |
| ssl | SMTP over secure SSL connection | `ssl://username:password@host:465` |
| sendmail | PHP's built-in mail() function | `sendmail://localhost` |
| mailgun | Mailgun API | `mailgun://YOUR_API_KEY@YOUR_DOMAIN` |
| ses | Amazon SES API | `ses://ACCESS_KEY_ID:SECRET_KEY@REGION` |
| fakesender | Testing (does nothing) | `fakesender://localhost` |

This option is currently unsupported.

Further information and documentation on how to set up can be found on this
[wiki](https://github.com/PHPMailer/PHPMailer/wiki/Using-Gmail-with-XOAUTH2) page.

### Amazon SES API specifics

The connection url for the AWS SES api is:

```text
ses://ACCESS_KEY_ID:SECRET_KEY@REGION
```

The access_key_id and secret_key are created at AWS Control Panel. The region can be us-east-1, etc.

### Mailgun API specifics

The connection url for the Mailgun api is:

```text
mailgun://YOUR_API_KEY@YOUR_DOMAIN
```

The YOUR_API_KEY and YOUR_DOMAIN are defined at Mailgun Control Panel.

The Region of the endpoint can be configured by query parameter "region" (example: mailgun://api-key@mg.domain.cz?region=eu)

Valid values are: us and eu.

### Sendmail Specifics

The connection url for the Sendmail is:

```text
sendmail://localhost
```

You need to setup in the `php.ini` the email relay.

## Implementing your Own Wrappers

To implement your own wrapper you have to create a class inherited from: `ByJG\Mail\Wrapper\BaseWrapper` and implement
how to send in the method: `public function send(Envelope $envelope);`

```php
class MyWrapper extends \ByJG\Mail\Wrapper\BaseWrapper
{
public static function schema()
{
return ['mywrapper'];
}

public function send(Envelope $envelope): \ByJG\Mail\SendResult
{
// Do how to send the email using your library
}

// You can create your own validation methods.
public function validate(Envelope $envelope)
{
parent::validate($envelope);
}
}
```

## Install

```shell
composer require "byjg/mailwrapper"
```
See [Connection Strings](docs/connection-strings.md) for detailed configuration examples.

## Running Tests

Expand All @@ -234,12 +88,9 @@ composer require "byjg/mailwrapper"

```mermaid
flowchart TD
byjg/mailwrapper --> ext-curl
byjg/mailwrapper --> byjg/convert
byjg/mailwrapper --> byjg/webrequest
byjg/mailwrapper --> aws/aws-sdk-php
byjg/mailwrapper --> phpmailer/phpmailer
```

----
[Open source ByJG](http://opensource.byjg.com)
[Open source ByJG](http://opensource.byjg.com)
14 changes: 9 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@
"prefer-stable": true,
"minimum-stability": "dev",
"require": {
"php": ">=8.1 <8.4",
"php": ">=8.1 <8.5",
"ext-curl": "*",
"byjg/convert": "^5.0",
"byjg/webrequest": "^5.0",
"byjg/convert": "^6.0",
"byjg/webrequest": "^6.0",
"aws/aws-sdk-php": "~3.20",
"phpmailer/phpmailer": ">=6.4.1"
},
"require-dev": {
"phpunit/phpunit": "^9.6",
"vimeo/psalm": "^5.9"
"phpunit/phpunit": "^10|^11",
"vimeo/psalm": "^5.9|^6.12"
},
"scripts": {
"test": "vendor/bin/phpunit",
"psalm": "vendor/bin/psalm"
},
"license": "MIT"
}
Loading