rednucleus/emailsender
A Laravel/PHP package for sending application emails through a simple, reusable API. It helps compose messages, manage recipients and content, and integrate email delivery into Laravel projects with less boilerplate.
Architecture fit
The package promises two transports — Microsoft Graph API and SMTP — behind a single Laravel-facing API. From a TPM lens, the single most important architectural question is where it attaches to the framework. There are two very different outcomes:
Illuminate\Mail\Transport\Transport (or the newer Symfony\Component\Mailer\Transport\TransportInterface), registers via Mail::extend() / a MailManager driver in a service provider, and is selected through config/mail.php ('driver' => 'graph'). If so, it composes cleanly with Mailable, Mail::to()->queue(), Notifications, Markdown templates, queued jobs, Mail::fake(), and all existing mail events. That is the only fit worth adopting.EmailSender::send($to, $subject, $body) facade. This is a parallel mail path that bypasses queues, retry/backoff, mail events, localization, view rendering, and attachments already solved by Laravel. Adopting it means two mail code paths forever.Given zero stars, zero dependents, and a score of 0, there is no community evidence that the transport-level integration exists or is correct. Treat that as unverified until source review.
The Graph half is the real value: it is the sanctioned path for Microsoft 365 tenants where SMTP AUTH with basic credentials has been disabled or deprecated. The SMTP half is only meaningful if it does OAuth2/XOAUTH2 rather than username/password — otherwise it solves the problem that no longer exists.
Integration feasibility
Distribution is the first blocker. Bitbucket-hosted PHP packages are outside the Composer/Packagist mainstream. You will need an explicit repositories entry of type vcs (with app-password or SSH credentials in CI), and you inherit Bitbucket's availability for every build. There is no Packagist metadata, which means:
composer audit has no advisory feed for it — supply-chain scanning is blind.composer require discoverability for other teams, so internal reuse requires documenting the VCS entry.Second blocker is the unknown version matrix. There is no visible CI badge, no composer.json compatibility statement in the metadata, and no test suite evidence. Laravel's mail layer has churned across 9 → 10 → 11 (Symfony Mailer migration, MailManager internals, Transport interface signature changes). A package without a tested support matrix is a package that will break on your next framework minor.
Third is Graph-side correctness, which is where most homegrown packages are subtly wrong: delegated (/me/sendMail) vs application (/users/{id}/sendMail) permission models, token acquisition via client credentials on login.microsoftonline.com/{tenant}/oauth2/v2.0/token, token caching and refresh, attachment handling (fileAttachment with base64 has a ~3–4 MB ceiling before you need an upload session), and 429 throttling with Retry-After observance.
Technical risk
| Risk | Severity | Why |
|---|---|---|
| Bus factor / vendor viability | High | Single small vendor, Bitbucket-only, zero dependents; abandonment gives you a dead mail path |
| No security advisory coverage | High | Mail credentials and OAuth secrets flow through this code; no CVE feed, no disclosure process |
| Silent contract drift with Graph | Medium | Microsoft changes throttling, permission scopes, and deprecates endpoints on its own cadence; nobody is watching |
| Framework coupling | Medium | Untested Laravel/PHP matrix; mail internals are not a stable public API across majors |
| Correctness of error semantics | Medium | Graph sendMail returns 202 Accepted — delivery is asynchronous. Naive implementations report success for mail that later bounces. Whether the package surfaces this honestly is unknown |
Blast radius if it fails: total outbound email loss for every application wired to it — password resets, invoices, notifications. That is a tier-1 dependency for a package with 0 stars. Do not let it become the only path.
Key questions (answer before any commitment)
Retry-After on 429/503, and does it distinguish "accepted" from "delivered"?Stack fit
Fits a standard Laravel monolith or modular monolith that already uses the Mail facade. It does not fit a polyglot estate where mail is centralized in a separate service, nor a Symfony-only or framework-agnostic PHP service without adaptation. If your platform already routes mail through a provider like Postmark/SES via a first-party or well-maintained transport, this package adds a second provider for one specific tenant scenario — justify it as a tenant-specific requirement (Microsoft 365 with basic auth disabled), not a general mail strategy.
Migration path
MailTransport seam in your own code — even if it is a thin wrapper around Laravel's MailManager. Every later decision (keep, fork, replace with a 300-line in-house transport) then costs hours, not a refactor.MAIL_MAILER=graph plus services.microsoft.* credentials.Mail.Send application permission with admin consent, client secret/certificate in your secret manager (not .env in the repo), and — for multi-tenant or restricted mailboxes — an Application Access Policy in Exchange Online to scope the app to specific mailboxes. This is often the long pole, not the code.Compatibility
Verify explicitly against your actual composer.json constraints. If the package pins Symfony Mailer below your Laravel version's requirement, you have an unresolvable conflict and the decision is made for you. Also verify certificate-based auth support — client secrets are increasingly disallowed by tenant policy, and a package that only does secrets will strand you.
Sequencing
Spike → tenant app registration → internal abstraction → canary stream → transactional cutover. Budget roughly 2–4 weeks calendar time, most of it tenant/security approval rather than engineering.
Maintenance
Assume a fork-and-own posture from day one, not a consume-and-hope posture. Practical measures: vendor the code or mirror it to your own Git host, pin to an exact tag or commit SHA, and record the upstream diff baseline so you can re-apply your patches. Because there is no advisory feed, add it to a manual quarterly review: Graph API version in use, Microsoft deprecation notices, token endpoint changes. This is a real recurring cost against a package that likely contains a few hundred lines of code — which is precisely the argument for owning those lines outright.
Support
No vendor SLA, no issue tracker visibility from the metadata, unknown responsiveness. Escalation path is "the one engineer who read the source." Name that engineer and give them a backup. If mail delivery failure is a customer-visible incident class for you, this dependency should sit on the same on-call rotation as your own services, with a runbook.
Scaling
Three concerns:
sendMail; bursts produce 429s. Queue workers with high concurrency will hit this. The package must honor Retry-After, and your queue backoff policy must accommodate it — otherwise you convert a throttle into a job failure.Cache rather than a private in-memory property. Otherwise you burn token requests against the identity provider's limits.Failure modes to instrument
ErrorAccessDenied, ErrorInvalidUser) → alert; usually a tenant config drift or Application Access Policy change, not a code bug.smtp with no credentials) → assert the effective mailer at boot.Instrument send counts, latency, and failure rates per driver from day one. Without that, a partial outage looks like "some users didn't get emails" for far too long.
Ramp-up
Engineering ramp-up is low — a day or two for a Laravel developer who has read the source. The real ramp-up is Microsoft identity and Exchange administration knowledge: permission models, admin consent, Application Access Policies, mailbox scoping, and diagnosing Graph errors. Budget that on the platform/identity side, not the application side, and make sure the person who configures the tenant is available at cutover. Document the app registration, the permission grants, and the secret rotation procedure in your runbook — this is the knowledge that vanishes when one person leaves and takes the mail path with them.
Recommendation
Do not adopt as a strategic dependency at its current maturity signal. Use the source audit as a reference implementation: if it is transport-level and correct, adopt it pinned and vendored under an internal abstraction while accepting ownership. If it is not transport-level, or if the audit surfaces token/attachment/throttling gaps, write the equivalent 300–500 lines in-house — the scope is small enough that the dependency risk exceeds the build cost, and you keep the advisory coverage, CI, and support path you actually need for a tier-1 mail dependency.
How can I help you explore Laravel packages today?