PHPackages                             cbowofrivia/dmarc-record-builder - 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. cbowofrivia/dmarc-record-builder

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

cbowofrivia/dmarc-record-builder
================================

A lean package that makes creating DMARC records user friendly

v4.0.0(1w ago)6119.7k↓86.8%1[2 PRs](https://github.com/cbowofrivia/dmarc-record-builder/pulls)MITPHPPHP ^8.3CI passing

Since May 6Pushed 1w ago1 watchersCompare

[ Source](https://github.com/cbowofrivia/dmarc-record-builder)[ Packagist](https://packagist.org/packages/cbowofrivia/dmarc-record-builder)[ Docs](https://github.com/cbowofrivia/dmarc-record-builder)[ RSS](/packages/cbowofrivia-dmarc-record-builder/feed)WikiDiscussions main Synced yesterday

READMEChangelog (9)Dependencies (15)Versions (24)Used By (0)

DMARC Record Builder
====================

[](#dmarc-record-builder)

[![Latest Version on Packagist](https://camo.githubusercontent.com/d4895a765c91f68d2887cd88aa35b744a65a5c2189922920f8dc82674c820a02/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f63626f776f6672697669612f646d6172632d7265636f72642d6275696c6465722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/cbowofrivia/dmarc-record-builder)[![Tests](https://github.com/cbowofrivia/dmarc-record-builder/actions/workflows/run-tests.yml/badge.svg?branch=main)](https://github.com/cbowofrivia/dmarc-record-builder/actions/workflows/run-tests.yml)[![codecov](https://camo.githubusercontent.com/d5dc1615c130edbf0dac1720d5bbdc105190fb3702d391294e639a42deb13de4/68747470733a2f2f636f6465636f762e696f2f67682f63626f776f6672697669612f646d6172632d7265636f72642d6275696c6465722f67726170682f62616467652e737667)](https://codecov.io/gh/cbowofrivia/dmarc-record-builder)[![Total Downloads](https://camo.githubusercontent.com/70f3bc8e675f4a40ca2d31d064385828b5b3019bd8719aa571177c53143284d4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f63626f776f6672697669612f646d6172632d7265636f72642d6275696c6465722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/cbowofrivia/dmarc-record-builder)

A PHP package for building and parsing DMARC DNS records with a fluent, human-friendly API. Accepts readable values (`'relaxed'`, `'strict'`, `'reject'`) and outputs correctly formatted DMARC strings.

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

[](#requirements)

- PHP 8.3 or higher

This package has no runtime dependencies.

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

[](#installation)

```
composer require cbowofrivia/dmarc-record-builder
```

Quick Start
-----------

[](#quick-start)

```
use CbowOfRivia\DmarcRecordBuilder\DmarcRecord;

$record = (string) (new DmarcRecord())
    ->policy('reject')
    ->rua('mailto:dmarc@example.com');

// v=DMARC1; p=reject; rua=mailto:dmarc@example.com
```

Building a Record
-----------------

[](#building-a-record)

### Fluent API

[](#fluent-api)

All setter methods return `$this`, so calls can be chained:

```
$record = new DmarcRecord();

$record->policy('reject')
    ->subdomainPolicy('quarantine')
    ->rua('mailto:dmarc@example.com')
    ->ruf('mailto:dmarc-forensic@example.com')
    ->adkim('relaxed')
    ->aspf('strict')
    ->reporting(['dkim', 'spf'])
    ->nonExistentSubdomainPolicy('reject')
    ->publicSuffixDomainPolicy('y')
    ->testingMode('n');

echo $record;
// v=DMARC1; p=reject; sp=quarantine; rua=mailto:dmarc@example.com; ruf=mailto:dmarc-forensic@example.com; adkim=r; aspf=s; fo=d:s; np=reject; psd=y; t=n
```

### Constructor

[](#constructor)

All parameters are optional. The constructor accepts the same values as the fluent setters:

```
$record = new DmarcRecord(
    version: 'DMARC1',
    policy: 'reject',
    subdomain_policy: 'quarantine',
    rua: 'mailto:dmarc@example.com',
    ruf: 'mailto:dmarc-forensic@example.com',
    adkim: 'relaxed',
    aspf: 'strict',
    reporting: ['dkim', 'spf'],
    np: 'reject',
    psd: 'y',
    t: 'n',
);
```

### Static Factory

[](#static-factory)

`DmarcRecord::create()` is a convenience wrapper around the constructor, useful when you want to build and cast in one expression:

```
$record = (string) DmarcRecord::create(
    policy: 'quarantine',
    rua: 'mailto:dmarc@example.com',
);

// v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com
```

Parsing an Existing Record
--------------------------

[](#parsing-an-existing-record)

Pass a raw DMARC TXT record string to `DmarcRecord::parse()` to get a populated `DmarcRecord` object. This is useful for reading and modifying existing records.

```
$record = DmarcRecord::parse('v=DMARC1; p=quarantine; adkim=r; aspf=s; fo=d:s');

$record->policy;      // 'quarantine'
$record->adkim;       // 'relaxed'  (translated from 'r')
$record->aspf;        // 'strict'   (translated from 's')
$record->reporting;   // ['dkim', 'spf']  (translated from 'fo=d:s')
```

After parsing, you can modify the record and re-cast it to a string:

```
$record = DmarcRecord::parse('v=DMARC1; p=none; rua=mailto:dmarc@example.com');
$record->policy('reject');

echo $record;
// v=DMARC1; p=reject; rua=mailto:dmarc@example.com
```

`parse()` requires both `v` and `p` tags to be present and will throw an `InvalidDmarcRecordException` if either is missing. Unknown tags are silently ignored.

Tag Reference
-------------

[](#tag-reference)

MethodDMARC TagAccepted ValuesDefault`version()``v``'DMARC1'``'DMARC1'``policy()``p``'none'`, `'quarantine'`, `'reject'``'none'``subdomainPolicy()``sp``'none'`, `'quarantine'`, `'reject'``null``rua()``rua``'mailto:...'``null``ruf()``ruf``'mailto:...'``null``adkim()``adkim``'relaxed'`, `'strict'``null``aspf()``aspf``'relaxed'`, `'strict'``null``reporting()``fo``'all'`, `'any'`, `'dkim'`, `'spf'``[]``nonExistentSubdomainPolicy()``np``'none'`, `'quarantine'`, `'reject'``null``publicSuffixDomainPolicy()``psd``'y'`, `'n'`, `'u'``null``testingMode()``t``'y'`, `'n'``null`Tags with a `null` value are omitted from the output string. Only `v` and `p` are always emitted.

> **Note:** RFC 9989 (DMARCbis) removed the `pct` and `ri` tags, so this package no longer supports them (removed in `4.0.0`). Records that still contain those tags parse without error — the tags are simply ignored.

### Tag Details

[](#tag-details)

#### `policy()` / `subdomainPolicy()` / `nonExistentSubdomainPolicy()`

[](#policy--subdomainpolicy--nonexistentsubdomainpolicy)

Controls how the receiving mail server handles messages that fail DMARC checks.

- `'none'` — take no action; useful during monitoring
- `'quarantine'` — send to spam/junk
- `'reject'` — reject the message outright

`subdomainPolicy()` (`sp`) overrides `policy()` for subdomains. If omitted, subdomains inherit the main policy.

`nonExistentSubdomainPolicy()` (`np`) applies to non-existent subdomains (RFC 9091 / DMARCbis). Takes precedence over both `policy()` and `subdomainPolicy()` for those domains.

#### `rua()` / `ruf()`

[](#rua--ruf)

URIs for receiving DMARC reports. Must be prefixed with `mailto:`.

- `rua` — aggregate reports (daily summaries from receivers)
- `ruf` — forensic/failure reports (per-message failure details; not all receivers send these)

```
->rua('mailto:dmarc@example.com')
->ruf('mailto:dmarc-forensic@example.com')
```

Both tags also accept an array of addresses (RFC 9989 — comma-separated list):

```
->rua(['mailto:dmarc@example.com', 'mailto:backup@example.com'])
// rua=mailto:dmarc@example.com,mailto:backup@example.com
```

#### `adkim()` / `aspf()`

[](#adkim--aspf)

Alignment mode for DKIM and SPF respectively.

- `'relaxed'` — the organisational domain must match (e.g. `mail.example.com` aligns with `example.com`)
- `'strict'` — the domains must match exactly

Omitting either defaults to relaxed alignment per the RFC.

#### `reporting()`

[](#reporting)

Specifies which failure conditions trigger a forensic report. Accepts a string or an array of options:

Value`fo` tagMeaning`'all'``fo=0`Report if all mechanisms fail`'any'``fo=1`Report if any mechanism fails`'dkim'``fo=d`Report if DKIM fails`'spf'``fo=s`Report if SPF failsMultiple values produce a colon-separated `fo` tag:

```
->reporting(['dkim', 'spf'])
// fo=d:s

->reporting(['any'])
// fo=1
```

Duplicate values are silently deduplicated. `'all'` and `'any'` are mutually exclusive — passing both throws an `InvalidDmarcRecordException`.

#### `publicSuffixDomainPolicy()`

[](#publicsuffixdomainpolicy)

Controls DMARC policy application at public suffix domains (DMARCbis extension).

- `'y'` — apply DMARC policy to this public suffix domain
- `'n'` — do not apply policy to this public suffix domain
- `'u'` — undefined / unknown

#### `testingMode()`

[](#testingmode)

When set to `'y'`, signals that DMARC is in testing mode. Receivers should not apply policy actions but may still send reports.

Validation
----------

[](#validation)

The package validates inputs on each setter call. Passing an invalid value throws a `CbowOfRivia\DmarcRecordBuilder\Exceptions\InvalidDmarcRecordException`. It extends the native `\InvalidArgumentException`, so existing `catch (\InvalidArgumentException)` handlers continue to work.

```
// Throws: Expected one of: "none", "quarantine", "reject", null. Got: "monitor"
$record->policy('monitor');

// Throws: rua mailto address should start with "mailto:"
$record->rua('dmarc@example.com');

// Throws: Expected one of: "relaxed", "strict", null. Got: "loose"
$record->adkim('loose');

// Throws: Reporting options "all" (0) and "any" (1) are mutually exclusive.
$record->reporting(['all', 'any']);
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Credits
-------

[](#credits)

- [Charles Bowen](https://github.com/cbowofrivia)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

59

—

FairBetter than 98% of packages

Maintenance98

Actively maintained with recent releases

Popularity35

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity73

Established project with proven stability

 Bus Factor1

Top contributor holds 66% 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.

###  Release Activity

Cadence

Every ~188 days

Recently: every ~61 days

Total

9

Last Release

11d ago

Major Versions

1.0.1 → 2.0.02024-04-20

2.1.0 → v3.0.02026-03-20

v3.1.0 → v4.0.02026-06-22

PHP version history (3 changes)1.0.0PHP ^8.0

2.0.0PHP ^8.2

v4.0.0PHP ^8.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/33219142?v=4)[Charles R Bowen](/maintainers/cbowofrivia)[@cbowofrivia](https://github.com/cbowofrivia)

---

Top Contributors

[![cbowofrivia](https://avatars.githubusercontent.com/u/33219142?v=4)](https://github.com/cbowofrivia "cbowofrivia (68 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (23 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (10 commits)")[![jmalinens](https://avatars.githubusercontent.com/u/569039?v=4)](https://github.com/jmalinens "jmalinens (2 commits)")

---

Tags

dmarcemailphpsecuritydmarccbowofriviadmarc-record-builder

###  Code Quality

TestsPest

Static AnalysisRector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/cbowofrivia-dmarc-record-builder/health.svg)

```
[![Health](https://phpackages.com/badges/cbowofrivia-dmarc-record-builder/health.svg)](https://phpackages.com/packages/cbowofrivia-dmarc-record-builder)
```

###  Alternatives

[mailgun/mailgun-php

The Mailgun SDK provides methods for all API functions.

1.1k30.8M181](/packages/mailgun-mailgun-php)[illuminate/database

The Illuminate Database package.

2.8k54.9M11.6k](/packages/illuminate-database)[craftcms/cms

Craft CMS

3.6k3.6M3.1k](/packages/craftcms-cms)[illuminate/support

The Illuminate Support package.

630113.0M41.3k](/packages/illuminate-support)[driftingly/rector-laravel

Rector upgrades rules for Laravel Framework

1.2k15.4M729](/packages/driftingly-rector-laravel)[illuminate/mail

The Illuminate Mail package.

5910.6M499](/packages/illuminate-mail)

PHPackages © 2026

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