cbowofrivia/dmarc-record-builder
A PHP package for building DMARC TXT records programmatically. Configure policy, subdomain policy, alignment, reporting addresses and percentage, then generate a valid DMARC record string for your DNS zone.
composer require cbowofrivia/dmarc-record-builder:^4.0 — the package is now dependency-free, so it adds no transitive packages and does not pin your Laravel version. Requirements: PHP 8.3+.use directly.create() factory / constructor arguments, the chainable tag methods, the static parse(), and Exceptions\InvalidDmarcRecordException.use CbowOfRivia\DmarcRecordBuilder\DmarcRecord;
// Tag methods mirror the DMARC tag names; confirm exact signatures
// against the README before wiring this into a form.
$record = DmarcRecord::create()
->policy('quarantine')
->rua('mailto:[email protected]');
return $record->toString(); // "v=DMARC1; p=quarantine; rua=mailto:..."
Publish that string to a TXT at _dmarc.example.com via your DNS provider's API.
Store structured state, render on demand. Don't make the TXT string the source of truth. Keep the individual settings in columns/JSON on your domains table and cast to the record whenever you need to emit DNS. This survives both provider changes and future package upgrades.
A cast for the model layer. Wrap hydration/serialisation in a custom cast so controllers never touch the builder:
public function getDmarcRecordAttribute(): DmarcRecord
{
return json_decode($this->dmarc_config, true) === []
? DmarcRecord::create()
: DmarcRecord::fromArray(json_decode($this->dmarc_config, true)); // or equivalent
}
Validate by constructing, then translate the exception. The builder is your validator for DMARC-specific rules; convert its exception into Laravel validation errors at the boundary:
try {
$record = DmarcRecord::create()->policy($request->policy);
} catch (\InvalidArgumentException $e) { // was Webmozart's class in v3
throw ValidationException::withMessages(['policy' => $e->getMessage()]);
}
Parsing for audits and imports. When you ingest existing records from dns_get_record($domain, DNS_TXT), run them through parse() in an artisan command and diff against what the tenant has configured. This is the cheapest way to spot records that drifted from your UI.
Keep DNS I/O out of the builder's job. The package produces a string; publishing, TTLs, and the _dmarc subdomain naming are yours to own in a DmarcPublisher service. This keeps the package swappable.
Round-trip tests are trivial now. With zero runtime dependencies, unit-test the exact rendered string and asserted parse-back in a plain PHPUnit test — no Laravel bootstrapping needed.
pct() and interval() are gone in v4. RFC 9989 dropped pct and ri. Calls will now fatal with "undefined method"; the pct: / interval: constructor and create() arguments and the $pct / $interval properties are also removed. Grep your app for all five forms.parse() still accepts legacy records containing pct and ri without error, but drops those tags when you re-render. If a tenant is on pct=25 and you round-trip their record through a settings save, they silently jump to 100%. Snapshot the raw TXT string before any re-cast if you need an audit trail.CbowOfRivia\DmarcRecordBuilder\Exceptions\InvalidDmarcRecordException, which extends \InvalidArgumentException. Existing catch (\InvalidArgumentException) blocks keep working; only code that named Webmozart\Assert\InvalidArgumentException will silently stop catching. Exception messages are unchanged, so old log greps still match.illuminate/collections is no longer a dependency. If you were relying on parse() handing back a Collection (calling ->map(), ->first() on the result), wrap it yourself with collect($record->parse(...)). Plain PHP arrays now.parse() as a strict linter for third-party records — an unsupported tag disappears quietly instead of raising. Validate values (policy strength, rua addresses) yourself if the input isn't yours.App\Support\Dmarc wrapper containing every call to the package means the next breaking release touches exactly one file.How can I help you explore Laravel packages today?