Santaji GadePHP, DevelopmentYesterday7 Views

Write reliable send email code in PHP with PHPMailer and SMTP authentication, covering HTML emails, attachments, error handling, and safe testing.
Table of Contents
ToggleA contact form that quietly fails to deliver its emails is worse than no form at all, because nobody finds out until a customer complains somewhere else. Send email code in PHP done properly means SMTP, authentication, and a way to know when a message actually failed.
PHP ships with a built in mail() function, and it looks like the obvious starting point for sending a message from a form.
In practice it depends on the server having a working local mail transfer agent configured, which many shared hosts either skip or configure poorly.
Messages sent through a poorly configured mail() call also tend to land in spam, since they usually skip proper authentication headers entirely.
An SMTP library talking directly to a real mail provider avoids both problems, which is why most working PHP applications reach for one instead.
Three realistic options cover almost every PHP project, and picking between them mostly comes down to how much control the app needs over delivery.
| Approach | Reliability | Best For |
|---|---|---|
| Built in mail() function | Depends entirely on server configuration | Quick local testing only |
| PHPMailer over SMTP | Reliable, authenticated delivery | Most production applications |
| Transactional email API (SendGrid, Mailgun) | Very reliable, includes analytics | High volume or marketing style sending |
This guide builds around PHPMailer, the most widely used SMTP library in the PHP ecosystem, since it fits both a small contact form and a larger notification system. Both SendGrid and Mailgun also publish their own PHP quickstarts, worth comparing once send volume grows past what shared SMTP handles well.
Composer is the standard way to pull PHPMailer into a project, and it also handles autoloading so nothing needs a manual include path.
# install PHPMailer through Composer
composer require phpmailer/phpmailer
If the project has no composer.json yet, running composer init first creates one, and PHPMailer becomes the first real dependency listed inside it.
A single reusable function keeps SMTP configuration in one place instead of scattered across every form handler that needs to send a message.
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
function send_notification_email($to, $subject, $body) {
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = getenv('SMTP_USER');
$mail->Password = getenv('SMTP_PASS');
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
$mail->setFrom('noreply@brandella.in', 'Brandella Journal');
$mail->addAddress($to);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->send();
return true;
} catch (Exception $e) {
error_log("Mail failed: " . $mail->ErrorInfo);
return false;
}
}
Reading credentials with getenv() instead of hardcoding them keeps a real password out of version control entirely. Gmail also requires an app password rather than the normal account password once two factor authentication is turned on.
Plain text is fine for a simple alert, but most real notifications benefit from formatting, a logo, and a clear call to action.
$mail->isHTML(true);
$mail->Subject = 'Your order has shipped';
$mail->Body = sprintf(
'<h2>Thanks, %s</h2><p>Order #%s is on its way.</p>',
htmlspecialchars($customer_name),
htmlspecialchars($order_id)
);
$mail->AltBody = sprintf('Thanks, %s. Order #%s is on its way.', $customer_name, $order_id);
htmlspecialchars() around any value that came from a user matters here, since an unescaped name field is a real injection risk inside an HTML email body.
Setting AltBody gives email clients that block HTML a readable plain text fallback instead of a blank or broken message.
An invoice, a receipt, or a generated report often needs to travel with the email rather than living behind a separate download link.
$mail->addAttachment('/tmp/invoice_1042.pdf', 'Invoice.pdf');
PHPMailer reads the file straight from disk, so anything generated moments earlier by an invoice script can be attached without an extra upload step.
A send that fails silently is the exact problem this whole approach is meant to fix, so failures need to surface somewhere a developer will actually see them.
$mail->SMTPDebug = 0;
if (!send_notification_email($to, $subject, $body)) {
error_log(sprintf('Notification to %s failed at %s', $to, date('c')));
}
Logging the recipient and timestamp on failure turns a silent problem into something a support ticket or a monitoring alert can actually catch. This is the part of send email code in PHP that most tutorials skip, and it is usually the part that saves a debugging session later.
Sending real test emails to a real inbox during development is how a test message ends up in front of an actual customer by accident.
A tool like Mailtrap catches outgoing SMTP traffic in a safe sandbox inbox, so every send during development can be inspected without ever leaving the test environment.
Codecourse walks through a working PHPMailer setup end to end, which pairs well with the environment variable pattern used throughout this guide.
Video credit: Codecourse.
Seeing the SMTP handshake fail and succeed on screen makes the try and catch block in the code above much easier to reason about.
It depends on local server configuration that is often missing or incomplete, and messages sent through it commonly land in spam because they lack proper authentication.
PHPMailer configured for SMTP with a real provider like Gmail, SendGrid, or Mailgun is the most reliable starting point, since it handles authentication and delivery details the built in function does not.
Yes. Calling addAddress multiple times adds more recipients, and addBCC or addCC work the same way for blind copies and carbon copies.
Authenticate through a real SMTP provider, set up SPF and DKIM records for the sending domain, and avoid spam trigger words in the subject line.
No. Environment variables or a properly excluded configuration file keep credentials out of version control, which matters even for a small internal project.
Server configuration and missing authentication headers make it a poor production choice.
Authenticating with a real provider gets messages delivered instead of flagged.
AltBody keeps the message readable for clients that block HTML.
A silent failure defeats the entire purpose of sending a notification.
A tool like Mailtrap keeps test sends away from real inboxes.
SPF and DKIM records affect delivery just as much as the script itself.
Pair send email code in PHP with a wider toolkit to keep every notification landing where it should.









