Home / Get message body and attachments using PHP IMAP

Get message body and attachments using PHP IMAP

The last post looked at how to get a message structure using the PHP IMAP functions into an easier to use flat array. This post looks at how to loop through that array to easily get all the message parts and attachments.

PHP code to flatten the IMAP structure

The function in the previous post is called flattenParts. Refer to that post for more details, but to save time if you don’t wish to read that post as well here’s another copy of the same function:

function flattenParts($messageParts, $flattenedParts = array(), $prefix = '', $index = 1, $fullPrefix = true) {

	foreach($messageParts as $part) {
		$flattenedParts[$prefix.$index] = $part;
		if(isset($part->parts)) {
			if($part->type == 2) {
				$flattenedParts = flattenParts($part->parts, $flattenedParts, $prefix.$index.'.', 0, false);
			}
			elseif($fullPrefix) {
				$flattenedParts = flattenParts($part->parts, $flattenedParts, $prefix.$index.'.');
			}
			else {
				$flattenedParts = flattenParts($part->parts, $flattenedParts, $prefix);
			}
			unset($flattenedParts[$prefix.$index]->parts);
		}
		$index++;
	}

	return $flattenedParts;
			
}

Functions for getting the content and filename for a part

The looping function below relies on another couple of functions which get the message or attachment content and the filename when it’s an attachment. These are as follows:

function getPart($connection, $messageNumber, $partNumber, $encoding) {
	
	$data = imap_fetchbody($connection, $messageNumber, $partNumber);
	switch($encoding) {
		case 0: return $data; // 7BIT
		case 1: return $data; // 8BIT
		case 2: return $data; // BINARY
		case 3: return base64_decode($data); // BASE64
		case 4: return quoted_printable_decode($data); // QUOTED_PRINTABLE
		case 5: return $data; // OTHER
	}
	
	
}

function getFilenameFromPart($part) {

	$filename = '';
	
	if($part->ifdparameters) {
		foreach($part->dparameters as $object) {
			if(strtolower($object->attribute) == 'filename') {
				$filename = $object->value;
			}
		}
	}

	if(!$filename && $part->ifparameters) {
		foreach($part->parameters as $object) {
			if(strtolower($object->attribute) == 'name') {
				$filename = $object->value;
			}
		}
	}
	
	return $filename;
	
}

Looping through the message parts

And finally, here’s the code to connect to the IMAP server, download the first message and then loop through it to get the various message parts. Modify this code to suit your own circumstances. You can get the second, third, etc message by changing the $messageNumber variable.

$connection = imap_open($server, $login, $password);
$messageNumber = 1;
$structure = imap_fetchstructure($connection, $messageNumber);
$flattenedParts = flattenParts($structure->parts);

foreach($flattenedParts as $partNumber => $part) {

	switch($part->type) {
		
		case 0:
			// the HTML or plain text part of the email
			$message = getPart($connection, $messageNumber, $partNumber, $part->encoding);
			// now do something with the message, e.g. render it
		break;
	
		case 1:
			// multi-part headers, can ignore
	
		break;
		case 2:
			// attached message headers, can ignore
		break;
	
		case 3: // application
		case 4: // audio
		case 5: // image
		case 6: // video
		case 7: // other
			$filename = getFilenameFromPart($part);
			if($filename) {
				// it's an attachment
				$attachment = getPart($connection, $messageNumber, $partNumber, $part->encoding);
				// now do something with the attachment, e.g. save it somewhere
			}
			else {
				// don't know what it is
			}
		break;
	
	}
	
}

Dealing with inline attachments

Images can be attached inline in the HTML and there’s a special way they are embedded. As with regular attachments, these are just another "part" in the email. I’ll look at how to deal with these tomorrow.