PHP Mailto Link Generator: Complete Guide
Learn how to generate perfect mailto links in PHP with proper encoding, validation, and framework integration.
Quick Start (Pure PHP)
<?php
function create_mailto($to, $subject = '', $body = '', $cc = '', $bcc = '') {
$params = [];
if (!empty($subject)) {
$params[] = 'subject=' . rawurlencode($subject);
}
if (!empty($body)) {
$params[] = 'body=' . rawurlencode($body);
}
if (!empty($cc)) {
$params[] = 'cc=' . rawurlencode($cc);
}
if (!empty($bcc)) {
$params[] = 'bcc=' . rawurlencode($bcc);
}
$query = !empty($params) ? '?' . implode('&', $params) : '';
return "mailto:{$to}{$query}";
}
// Usage
$mailto = create_mailto(
'[email protected]',
'Bug Report',
"Description:\n- Issue: Login failed\n- Browser: Chrome"
);
echo $mailto;
// mailto:[email protected]?subject=Bug%20Report&body=Description%3A%0A-%20Issue%3A%20Login%20failed%0A-%20Browser%3A%20Chrome
?>
Object-Oriented Approach
<?php
class MailtoBuilder {
private $to;
private $subject = '';
private $body = '';
private $cc = '';
private $bcc = '';
private $maxLength = 1800;
public function __construct($to) {
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email address: {$to}");
}
$this->to = $to;
}
public function setSubject($subject) {
$this->subject = $subject;
return $this;
}
public function setBody($body) {
$this->body = $body;
return $this;
}
public function setCC($cc) {
$this->cc = $cc;
return $this;
}
public function setBCC($bcc) {
$this->bcc = $bcc;
return $this;
}
public function build() {
$params = [];
if (!empty($this->subject)) {
$params[] = 'subject=' . rawurlencode($this->subject);
}
if (!empty($this->body)) {
$params[] = 'body=' . rawurlencode($this->body);
}
if (!empty($this->cc)) {
$params[] = 'cc=' . rawurlencode($this->cc);
}
if (!empty($this->bcc)) {
$params[] = 'bcc=' . rawurlencode($this->bcc);
}
$query = !empty($params) ? '?' . implode('&', $params) : '';
$mailto = "mailto:{$this->to}{$query}";
if (strlen($mailto) > $this->maxLength) {
throw new LengthException("Mailto URL too long: " . strlen($mailto) . " chars");
}
return $mailto;
}
public function getStats() {
$mailto = $this->build();
return [
'total_length' => strlen($mailto),
'max_length' => $this->maxLength,
'remaining' => $this->maxLength - strlen($mailto),
'percentage' => round((strlen($mailto) / $this->maxLength) * 100, 2)
];
}
}
// Usage
$builder = new MailtoBuilder('[email protected]');
$mailto = $builder
->setSubject('Support Request')
->setBody("Hi Team,\n\nI need help with...")
->build();
echo $mailto;
// Check stats
print_r($builder->getStats());
?>
Laravel Integration
<?php
// app/Helpers/MailtoHelper.php
namespace App\Helpers;
class MailtoHelper {
public static function create($to, $subject = '', $body = '', $cc = '', $bcc = '') {
$params = collect([
'subject' => $subject,
'body' => $body,
'cc' => $cc,
'bcc' => $bcc
])->filter()->map(function($value, $key) {
return "{$key}=" . rawurlencode($value);
})->implode('&');
$query = !empty($params) ? "?{$params}" : '';
return "mailto:{$to}{$query}";
}
}
// Controller usage
namespace App\Http\Controllers;
use App\Helpers\MailtoHelper;
use Illuminate\Http\Request;
class ContactController extends Controller {
public function show() {
$supportMailto = MailtoHelper::create(
'[email protected]',
'Support Request from Website',
"Please describe your issue:\n\n"
);
return view('contact', compact('supportMailto'));
}
public function generateMailto(Request $request) {
$validated = $request->validate([
'to' => 'required|email',
'subject' => 'nullable|string|max:200',
'body' => 'nullable|string|max:1500',
]);
$mailto = MailtoHelper::create(
$validated['to'],
$validated['subject'] ?? '',
$validated['body'] ?? ''
);
return response()->json([
'success' => true,
'mailto' => $mailto,
'length' => strlen($mailto)
]);
}
}
?>
Laravel Blade Template
<!-- resources/views/contact.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
<h1>Contact Us</h1>
<a href="{{ $supportMailto }}" class="btn btn-primary">
📧 Email Support
</a>
<!-- Dynamic mailto generation -->
<form id="mailto-form">
<input type="email" name="to" placeholder="To" required>
<input type="text" name="subject" placeholder="Subject">
<textarea name="body" rows="5" placeholder="Message"></textarea>
<button type="submit">Generate Mailto Link</button>
</form>
<div id="result"></div>
</div>
<script>
document.getElementById('mailto-form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const response = await fetch('/api/mailto', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify(Object.fromEntries(formData))
});
const data = await response.json();
document.getElementById('result').innerHTML =
`<a href="${data.mailto}">${data.mailto}</a>`;
});
</script>
@endsection
WordPress Integration
<?php
// functions.php or custom plugin
/**
* Generate mailto link
*/
function wp_mailto_link($to, $args = []) {
$defaults = [
'subject' => '',
'body' => '',
'cc' => '',
'bcc' => ''
];
$args = wp_parse_args($args, $defaults);
$params = [];
foreach (['subject', 'body', 'cc', 'bcc'] as $key) {
if (!empty($args[$key])) {
$params[] = $key . '=' . rawurlencode($args[$key]);
}
}
$query = !empty($params) ? '?' . implode('&', $params) : '';
return esc_url("mailto:{$to}{$query}");
}
// Usage in template
$contact_link = wp_mailto_link('[email protected]', [
'subject' => 'Contact from ' . get_bloginfo('name'),
'body' => "Hi,\n\nI found your site..."
]);
?>
<a href="<?php echo $contact_link; ?>" class="contact-button">
Email Us
</a>
<?php
// Shortcode version
function mailto_shortcode($atts) {
$atts = shortcode_atts([
'to' => get_option('admin_email'),
'subject' => '',
'body' => '',
'text' => 'Email Us'
], $atts);
$mailto = wp_mailto_link($atts['to'], $atts);
return sprintf(
'<a href="%s" class="mailto-link">%s</a>',
esc_url($mailto),
esc_html($atts['text'])
);
}
add_shortcode('mailto', 'mailto_shortcode');
// Usage in post/page:
// [mailto to="[email protected]" subject="Help Request" text="Get Support"]
?>
Advanced Template System
<?php
class MailtoTemplate {
private static $templates = [
'support' => [
'to' => '[email protected]',
'subject' => 'Support Request: {issue}',
'body' => "Hi Support Team,\n\nIssue: {issue}\nUser: {user}\n\nDetails:\n{details}"
],
'sales' => [
'to' => '[email protected]',
'subject' => 'Sales Inquiry from {company}',
'body' => "Company: {company}\nContact: {contact}\n\nInterest: {interest}"
]
];
public static function render($template_name, $vars = []) {
if (!isset(self::$templates[$template_name])) {
throw new InvalidArgumentException("Template not found: {$template_name}");
}
$template = self::$templates[$template_name];
// Replace variables
foreach ($vars as $key => $value) {
$template['subject'] = str_replace("{{$key}}", $value, $template['subject']);
$template['body'] = str_replace("{{$key}}", $value, $template['body']);
}
return create_mailto(
$template['to'],
$template['subject'],
$template['body']
);
}
}
// Usage
$mailto = MailtoTemplate::render('support', [
'issue' => 'Login Error',
'user' => '[email protected]',
'details' => 'Cannot login after password reset'
]);
echo $mailto;
?>
Security Best Practices
<?php
// Sanitize and validate inputs
function safe_mailto($to, $subject = '', $body = '') {
// Validate email
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email address');
}
// Sanitize inputs (remove potential XSS)
$subject = strip_tags($subject);
$body = strip_tags($body);
// Limit lengths
$subject = substr($subject, 0, 200);
$body = substr($body, 0, 1500);
// Build mailto
$mailto = create_mailto($to, $subject, $body);
// Final length check
if (strlen($mailto) > 1800) {
throw new LengthException('Mailto URL too long');
}
return $mailto;
}
// Usage
try {
$mailto = safe_mailto(
$_POST['email'],
$_POST['subject'],
$_POST['message']
);
echo htmlspecialchars($mailto, ENT_QUOTES, 'UTF-8');
} catch (Exception $e) {
echo 'Error: ' . htmlspecialchars($e->getMessage());
}
?>
Testing
<?php
// Using PHPUnit
use PHPUnit\Framework\TestCase;
class MailtoBuilderTest extends TestCase {
public function testBasicMailto() {
$mailto = create_mailto('[email protected]');
$this->assertEquals('mailto:[email protected]', $mailto);
}
public function testWithSubject() {
$mailto = create_mailto('[email protected]', 'Hello World');
$this->assertStringContainsString('subject=Hello%20World', $mailto);
}
public function testLineBreaksEncoded() {
$mailto = create_mailto('[email protected]', '', "Line 1\nLine 2");
$this->assertStringContainsString('%0A', $mailto);
}
public function testInvalidEmail() {
$this->expectException(InvalidArgumentException::class);
new MailtoBuilder('invalid-email');
}
public function testLengthLimit() {
$this->expectException(LengthException::class);
$builder = new MailtoBuilder('[email protected]');
$builder->setBody(str_repeat('x', 2000))->build();
}
}
?>
Common Pitfalls
<?php
// ❌ WRONG: Using urlencode() instead of rawurlencode()
$mailto = "mailto:[email protected]?subject=" . urlencode($subject);
// urlencode converts spaces to +, which doesn't work in mailto
// ✅ CORRECT: Use rawurlencode()
$mailto = "mailto:[email protected]?subject=" . rawurlencode($subject);
// ❌ WRONG: Not handling special characters
$mailto = "mailto:[email protected]?subject={$subject}";
// ✅ CORRECT: Always encode
$mailto = "mailto:[email protected]?subject=" . rawurlencode($subject);
// ❌ WRONG: Forgetting to validate email
$mailto = create_mailto($_POST['email']);
// ✅ CORRECT: Validate first
if (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$mailto = create_mailto($_POST['email']);
}
?>
API Endpoint Example
<?php
// api/mailto.php
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
try {
$mailto = safe_mailto(
$input['to'] ?? '',
$input['subject'] ?? '',
$input['body'] ?? ''
);
echo json_encode([
'success' => true,
'mailto' => $mailto,
'length' => strlen($mailto)
]);
} catch (Exception $e) {
http_response_code(400);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
?>
Related Resources
📚 Mailto Guides
💻 Other Language Guides
- Python Guide - Flask, Django, FastAPI integration
- JavaScript Guide - React, Vue, Node.js, TypeScript
- Ruby Guide - Rails, Sinatra, RSpec tests
📖 Deep Dives
Test your PHP implementation with our free mailto link generator to ensure proper encoding.