phpemailcron

html part of message is ignored when sending an e-mail with attachment using mail() in cron job


I'am sending an e-mail via cron job. The attachment works and is added to the e-mail message. However the html part that is referenced with $body2 does not come through. The code looks like follows:

$body2 = '<!DOCTYPE html><html>...</html>';
$email = 'recipient@something.com';
$attachment = '/path/to/file/file.txt';
$content = file_get_contents($attachment);

$prefix     = "part_"; // This is an optional prefix
$boundary   = uniqid($prefix, true);

// headers
$headers    = implode("\r\n", [
'From: name@server.net',
'Reply-To: anothername@provider.com',
'X-Mailer: PHP/' . PHP_VERSION,
'MIME-Version: 1.0',
// boundary parameter required, must be enclosed by quotes
'Content-Type: multipart/mixed; boundary="' . $boundary . '"',
'Content-Transfer-Encoding: BINARY',
'This is a MIME encoded message.' // message for restricted transports
 ]);

// message and attachment
$message    = implode("\r\n", [ 
"--" . $boundary, // header boundary delimiter line
//'MIME-Version: 1.0',
'Content-Type: text/html; charset="utf8"',
'Content-Transfer-Encoding: 8bit',
//$body2 holds the html part of the e-mail
$body2,
'--' . $boundary, // content boundary delimiter line
'Content-Type: application/octet-stream; name="file.txt"',
'Content-Transfer-Encoding: BINARY',
'Content-Disposition: attachment',
$content,
"--" . $boundary . "--" // closing boundary delimiter line
]);

mail($email, $subject, $message, $headers); // send the email

I’ve spent hours with configuring this. I also tried plain text, with the same result. The text as well as the html won’ t show up in the e-mail message. How do i have to set this up, that the html and the attachment come through? Thanks for any hint.


Solution

  • Actually most developers no longer use php mail function to send email contents (with attachments), they will use library like phpmailer, or similar.

    However, to demonstrate the concept, please find below the corrected code (tested and working)

    1. contents specified as HTML (instead of plain text)
    2. with multiple attachments (this example attaches two binary files as attachments)
    3. I have amended your code so that places with incorrect carriage returns are fixed.
    <?php
    
    $to = "recipient@example.com";
    $from = "sender@example.com";
    $subject = "Email with Multiple Attachments";
    $message_body = "<html>This is the <b>main</b> body of the email.</html>";
    
    // File paths for the attachments
    $file1 = '/var/www/xxxxxdomain/1.pdf';
    $file2 = '/var/www/xxxxxdomain/2.pdf';
    
    // Generate a unique boundary string
    $semi_rand = md5(time());
    $mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";
    
    // Set headers for attachment
    $headers = "From: {$from}\r\n";
    $headers .= "MIME-Version: 1.0\r\n";
    $headers .= "Content-Type: multipart/mixed;\r\n";
    $headers .= " boundary=\"{$mime_boundary}\"\r\n";
    
    // Start constructing the message
    $message = "--{$mime_boundary}\r\n";
    $message .= "Content-Type: text/HTML; charset=\"utf-8\"\r\n";
    $message .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
    $message .= $message_body . "\r\n\r\n";
    
    // Attachment 1
    if (file_exists($file1)) {
        $filename1 = basename($file1);
        $file_size1 = filesize($file1);
        $file_content1 = chunk_split(base64_encode(file_get_contents($file1)));
        $message .= "--{$mime_boundary}\r\n";
        $message .= "Content-Type: application/octet-stream; name=\"{$filename1}\"\r\n";
        $message .= "Content-Transfer-Encoding: base64\r\n";
        $message .= "Content-Disposition: attachment; filename=\"{$filename1}\"\r\n\r\n";
        $message .= $file_content1 . "\r\n\r\n";
    }
    
    // Attachment 2
    if (file_exists($file2)) {
        $filename2 = basename($file2);
        $file_size2 = filesize($file2);
        $file_content2 = chunk_split(base64_encode(file_get_contents($file2)));
        $message .= "--{$mime_boundary}\r\n";
        $message .= "Content-Type: application/octet-stream; name=\"{$filename2}\"\r\n";
        $message .= "Content-Transfer-Encoding: base64\r\n";
        $message .= "Content-Disposition: attachment; filename=\"{$filename2}\"\r\n\r\n";
        $message .= $file_content2 . "\r\n\r\n";
    }
    
    // End of message boundary
    $message .= "--{$mime_boundary}--\r\n";
    
    // Send the email
    if (mail($to, $subject, $message, $headers)) {
        echo "Email sent successfully!";
    } else {
        echo "Email sending failed.";
    }
    
    ?>
    

    Additional point:

    Since you want to use cron to trigger the email sending process, make sure the attachments are accessible when the script is run in console mode, so it is preferrable to use absolute paths

    Feel free to amend the code to suit your needs (e.g. For your case, change the attachment to '/path/to/file/file.txt')