Flick
Guide Validation Services API Pro Examples GitHub
Docs / Features

Mail

Send emails directly from your forms using PHP's native mail() function or SMTP.

Want More Power?

Flick Pro adds file attachments, 6+ email providers (SendGrid, Mailgun, Amazon SES...), and advanced HTML templates with conditionals.

See Pro Mail →

Configuration

Add mail configuration to your Flick setup:

$config = [
    'services' => [
        'mail' => [
            'fromAddress' => 'noreply@example.com',
            'fromName' => 'My App',  // Optional
            'mailer' => [
                'transport' => 'mail'  // or 'smtp'
            ]
        ]
    ]
];

$form = new Flick($config);

PHP mail() Transport

The simplest option. Uses your server's built-in mail function:

'mailer' => [
    'transport' => 'mail'
]

Tip

This config keeps working if you upgrade. With Flick Pro installed, $form->mail becomes the Pro Mail service, which accepts the same mail transport alongside smtp, ses, mailgun, sendgrid, postmark, mailjet, and mailtrap. Nothing to change.

Warning

PHP mail() requires a properly configured mail server. Many shared hosts have this set up, but local development environments typically don't. Use SMTP for more reliable delivery.

On Windows with `sendmail_path` set, a failed send reports success

By default on Windows, PHP's mail() delivers using the SMTP and smtp_port ini settings. Setting sendmail_path overrides both, and PHP then pipes the message to that command through cmd.exe — which starts successfully whether or not the command it was given exists. mail() reports success, so send() returns true even though nothing was delivered.

Flick returns what mail() tells it, so there is nothing it can check to catch this. With sendmail_path set to something broken, $form->mail->send() is not a delivery signal on Windows.

Use the smtp transport instead. Flick connects to the server itself, so an unreachable or rejecting server is a real false with the reason available from the error bag — on every platform.

SMTP Transport

Connect directly to an SMTP server for reliable delivery. Only transport and host are required:

'mailer' => [
    'transport' => 'smtp',
    'host' => 'smtp.example.com',
]

Defaults:

Option Default Description
port 587 SMTP port
encryption 'tls' 'tls' / 'starttls', 'ssl', or 'none' / '' — see below
username '' Auth skipped if empty
password '' Auth skipped if empty
timeout 30 Connection timeout in seconds
verifyPeer true Verify the server's TLS certificate
allowInsecureAuth false Permit sending credentials over an unencrypted connection

What encryption accepts:

Value Behavior
'tls' or 'starttls' STARTTLS, required — the send fails rather than quietly falling back to plaintext
'ssl' Implicit TLS from the first byte, usually port 465
'none' or '' No encryption at all. Only for a local mail catcher — never over a network

Leave it out, or set it to null, and you get 'tls'. Anything else throws an InvalidArgumentException when the mailer is built, so a typo fails loudly instead of quietly sending in the clear.

Full example with authentication:

'mailer' => [
    'transport' => 'smtp',
    'host' => 'smtp.example.com',
    'port' => 587,
    'encryption' => 'tls',
    'username' => 'your-username',
    'password' => 'your-password'
]

Local development (Laravel Herd, Mailpit, etc.):

'mailer' => [
    'transport' => 'smtp',
    'host' => '127.0.0.1',
    'port' => 2525,
    'encryption' => '',  // Disable TLS for local testing
]

Warning

Flick refuses to send a username and password over an unencrypted connection — send() returns false and puts "Refusing to send SMTP credentials over an unencrypted connection" in the error bag. Local catchers usually don't need auth, so the example above simply leaves username and password out. If yours does require auth, add 'allowInsecureAuth' => true and keep it out of production.

Common SMTP settings:

Provider Host Port Encryption
Gmail smtp.gmail.com 587 tls
Outlook smtp.office365.com 587 tls
Yahoo smtp.mail.yahoo.com 587 tls

Basic Usage

Send a Simple Email

if ($form->mail->send('user@example.com', 'Hello!', 'This is the message body.')) {
    $form->successMessage('Email sent!');
} else {
    $form->errorMessage('Failed to send email.');
}

With Options

$form->mail->send('user@example.com', 'Hello!', 'Message body', [
    'fromAddress' => 'custom@example.com',
    'fromName' => 'Custom Sender',
    'cc' => 'copy@example.com',
    'bcc' => 'hidden@example.com',
    'replyTo' => 'reply@example.com',
    'priority' => true  // Mark as high priority
]);

Multiple Recipients

$form->mail->send(
    ['user1@example.com', 'user2@example.com'],
    'Team Update',
    'This message goes to everyone.'
);

Send Form Data

Automatically format and send form submissions:

if ($form->submitted()) {
    $request = $form->request('Name[required], Email[required, email], Message[required]');

    if ($form->ok()) {
        $form->mail->sendFormData(
            'admin@example.com',
            'New Contact Form Submission',
            $request
        );
    }
}

This sends a plain-text body like the following, plus an HTML version with the same fields laid out in a table:

Name: John Doe
Email: john@example.com
Message: Hello, I have a question...

Exclude Fields

Hide sensitive or unnecessary fields:

$form->mail->sendFormData('admin@example.com', 'Contact Form', $request, [
    'exclude' => ['_token', 'honeypot', 'password']
]);

HTML Emails

Send both plain text and HTML versions (multipart/alternative):

$form->mail->send(
    'user@example.com',
    'Welcome!',
    'Welcome to our site. Thanks for signing up!',  // Plain text fallback
    [
        'html' => '<h1>Welcome!</h1><p>Thanks for signing up!</p>'
    ]
);

Simple Variables

Use {{ variable }} placeholders with the data option:

$form->mail->send(
    $request['email'],
    'Order Confirmation',
    'Hi {{ name }}, your order #{{ order_id }} is confirmed.',
    [
        'html' => '<h1>Thanks, {{ name }}!</h1><p>Order #{{ order_id }} confirmed.</p>',
        'data' => [
            'name' => $request['name'],
            'order_id' => '12345'
        ]
    ]
);

Tip

HTML variables are automatically escaped to prevent XSS attacks. Plain text variables are inserted as-is.


Methods

send()

Send an email message.

$form->mail->send(
    string|array $to,
    string $subject,
    string $body,
    array $options = []
): bool
Parameter Type Description
$to string|array Recipient email address(es)
$subject string Email subject line
$body string Plain text message body
$options array Additional options (see below)

Options:

Option Type Description
html string HTML version of the email
data array Variables for {{ placeholder }} replacement
fromAddress string Override sender address
fromName string Override sender name
cc string|array Carbon copy recipient(s)
bcc string|array Blind carbon copy recipient(s)
replyTo string Reply-to address
priority bool Mark as high priority

Returns: true on success, false on failure.

sendFormData()

Send formatted form data as an email.

$form->mail->sendFormData(
    string|array $to,
    string $subject,
    array $formData,
    array $options = []
): bool
Parameter Type Description
$to string|array Recipient email address(es)
$subject string Email subject line
$formData array Key-value pairs from form submission
$options array Same as send(), plus exclude

Additional Option:

Option Type Description
exclude array Field names to omit from the email

Complete Example

<?php
require 'vendor/autoload.php';

$form = new Flick\Flick([
    'services' => [
        'mail' => [
            'fromAddress' => 'noreply@myapp.com',
            'fromName' => 'My App',
            'mailer' => [
                'transport' => 'smtp',
                'host' => 'smtp.example.com',
                'port' => 587,
                'encryption' => 'tls',
                'username' => $_ENV['SMTP_USER'],
                'password' => $_ENV['SMTP_PASS']
            ]
        ]
    ]
]);

$form->create('Name, Email, Phone, Message|textarea');

if ($form->submitted()) {
    $request = $form->request('
        Name[required, min:2],
        Email[required, email],
        Phone[phone],
        Message[required, min:10]
    ');

    if ($form->ok()) {
        // Send notification to admin
        $sent = $form->mail->sendFormData(
            'admin@myapp.com',
            'New Contact Form Submission',
            $request
        );

        // Send confirmation to user
        $confirmed = $form->mail->send(
            $request['email'],
            'We received your message',
            "Hi {{ name }},\n\nThank you for contacting us. We'll respond within 24 hours.",
            ['data' => ['name' => $request['name']]]
        );

        if ($sent && $confirmed) {
            $form->successMessage('Message sent! Check your email for confirmation.');
        } else {
            $form->errorMessage('Sorry, your message could not be sent.');
        }
    }
}

$form->create('Name, Email, Phone, Message|textarea');

Upgrading to Pro

Need more from your email setup? Flick Pro adds:

  • File Attachments - PDFs, images, CSVs with fluent API
  • 6+ Transport Providers - SendGrid, Mailgun, Amazon SES, Postmark, Mailjet, Mailtrap
  • Advanced Templates - File-based templates with conditionals (@if), nested data access, raw HTML output
// Pro: Attachments
$form->mail
    ->attach('/path/to/invoice.pdf')
    ->send('customer@example.com', 'Your Invoice', 'Attached is your invoice.');

// Pro: SendGrid transport
'mailer' => [
    'transport' => 'sendgrid',
    'key' => 'your-api-key'
]

Get Flick Pro →