-
-
Save hakre/2363305 to your computer and use it in GitHub Desktop.
| <?php | |
| /** | |
| * imap-attachment.php | |
| * | |
| * @author hakre <hakre.wordpress.com> | |
| * @link http://stackoverflow.com/questions/9974334/how-to-download-mails-attachment-to-a-specific-folder-using-imap-and-php | |
| */ | |
| /** | |
| * Utility Class | |
| */ | |
| class IMAP | |
| { | |
| /** | |
| * | |
| * =?x-unknown?B? | |
| * =?iso-8859-1?Q? | |
| * =?windows-1252?B? | |
| * | |
| * @param string $stringQP | |
| * @param string $base (optional) charset (IANA, lowercase) | |
| * @return string UTF-8 | |
| */ | |
| public static function decodeToUTF8($stringQP, $base = 'windows-1252') | |
| { | |
| $pairs = array( | |
| '?x-unknown?' => "?$base?" | |
| ); | |
| $stringQP = strtr($stringQP, $pairs); | |
| return imap_utf8($stringQP); | |
| } | |
| } | |
| class IMAPMailbox implements IteratorAggregate, Countable | |
| { | |
| private $stream; | |
| public function __construct($hostname, $username, $password) | |
| { | |
| $stream = imap_open($hostname, $username, $password); | |
| if (FALSE === $stream) { | |
| throw new Exception('Connect failed: ' . imap_last_error()); | |
| } | |
| $this->stream = $stream; | |
| } | |
| public function getStream() | |
| { | |
| return $this->stream; | |
| } | |
| /** | |
| * @return stdClass | |
| */ | |
| public function check() | |
| { | |
| $info = imap_check($this->stream); | |
| if (FALSE === $info) { | |
| throw new Exception('Check failed: ' . imap_last_error()); | |
| } | |
| return $info; | |
| } | |
| /** | |
| * @param string $criteria | |
| * @param int $options | |
| * @param int $charset | |
| * @return IMAPMessage[] | |
| * @throws Exception | |
| */ | |
| public function search($criteria, $options = NULL, $charset = NULL) | |
| { | |
| $emails = imap_search($this->stream, $criteria, $options, $charset); | |
| if (FALSE === $emails) { | |
| throw new Exception('Search failed: ' . imap_last_error()); | |
| } | |
| foreach ($emails as &$email) { | |
| $email = $this->getMessageByNumber($email); | |
| } | |
| return $emails; | |
| } | |
| /** | |
| * @param int $number | |
| * @return IMAPMessage | |
| */ | |
| public function getMessageByNumber($number) | |
| { | |
| return new IMAPMessage($this, $number); | |
| } | |
| public function getOverview($sequence = NULL) | |
| { | |
| if (NULL === $sequence) { | |
| $sequence = sprintf('1:%d', count($this)); | |
| } | |
| return new IMAPOverview($this, $sequence); | |
| } | |
| /** | |
| * Retrieve an external iterator | |
| * @link http://php.net/manual/en/iteratoraggregate.getiterator.php | |
| * @return Traversable An instance of an object implementing Iterator or | |
| * Traversable | |
| */ | |
| public function getIterator() | |
| { | |
| return $this->getOverview()->getIterator(); | |
| } | |
| /** | |
| * @return int | |
| */ | |
| public function count() | |
| { | |
| return $this->check()->Nmsgs; | |
| } | |
| } | |
| class IMAPOverview extends ArrayObject | |
| { | |
| private $mailbox; | |
| public function __construct(IMAPMailbox $mailbox, $sequence) | |
| { | |
| $result = imap_fetch_overview($mailbox->getStream(), $sequence); | |
| if (FALSE === $result) { | |
| throw new Exception('Overview failed: ' . imap_last_error()); | |
| } | |
| $this->mailbox = $mailbox; | |
| foreach ($result as $overview) | |
| { | |
| if (!isset($overview->subject)) { | |
| $overview->subject = ''; | |
| } else { | |
| $overview->subject = IMAP::decodeToUTF8($overview->subject); | |
| } | |
| } | |
| parent::__construct($result); | |
| } | |
| /** | |
| * @return IMAPMailbox | |
| */ | |
| public function getMailbox() | |
| { | |
| return $this->mailbox; | |
| } | |
| } | |
| class IMAPMessage | |
| { | |
| private $mailbox; | |
| private $number; | |
| private $stream; | |
| public function __construct(IMAPMailbox $mailbox, $number) | |
| { | |
| $this->mailbox = $mailbox; | |
| $this->number = $number; | |
| $this->stream = $mailbox->getStream(); | |
| } | |
| public function getNumber() | |
| { | |
| return $this->number; | |
| } | |
| /** | |
| * @param int $number | |
| * @return string | |
| */ | |
| public function fetchBody($number) | |
| { | |
| return imap_fetchbody($this->stream, $this->number, $number); | |
| } | |
| /** | |
| * @return stdClass | |
| * @throws Exception | |
| */ | |
| public function fetchOverview() | |
| { | |
| $result = imap_fetch_overview($this->stream, $this->number); | |
| if (FALSE === $result) { | |
| throw new Exception('FetchOverview failed: ' . imap_last_error()); | |
| } | |
| list($result) = $result; | |
| foreach ($result as &$prop) { | |
| $prop = imap_utf8($prop); | |
| } | |
| return $result; | |
| } | |
| public function fetchStructure() | |
| { | |
| $structure = imap_fetchstructure($this->stream, $this->number); | |
| if (FALSE === $structure) { | |
| throw new Exception('FetchStructure failed: ' . imap_last_error()); | |
| } | |
| return $structure; | |
| } | |
| /** | |
| * @return IMAPAttachments | |
| */ | |
| public function getAttachments() | |
| { | |
| return new IMAPAttachments($this); | |
| } | |
| public function __toString() | |
| { | |
| return (string)$this->number; | |
| } | |
| } | |
| class IMAPAttachment | |
| { | |
| private $attachment; | |
| private $message; | |
| public function __construct(IMAPMessage $message, $attachment) | |
| { | |
| $this->message = $message; | |
| $this->attachment = $attachment; | |
| } | |
| /** | |
| * @return string; | |
| */ | |
| public function getBody() | |
| { | |
| return $this->message->fetchBody($this->attachment->number); | |
| } | |
| /** | |
| * @return int | |
| */ | |
| public function getSize() | |
| { | |
| return (int)$this->attachment->bytes; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function getExtension() | |
| { | |
| return pathinfo($this->getFilename(), PATHINFO_EXTENSION); | |
| } | |
| public function getFilename() | |
| { | |
| $filename = $this->attachment->filename; | |
| NULL === $filename && $filename = $this->attachment->name; | |
| return $filename; | |
| } | |
| public function __toString() | |
| { | |
| $encoding = $this->attachment->encoding; | |
| switch ($encoding) { | |
| case 0: // 7BIT | |
| case 1: // 8BIT | |
| case 2: // BINARY | |
| return $this->getBody(); | |
| case 3: // BASE-64 | |
| return base64_decode($this->getBody()); | |
| case 4: // QUOTED-PRINTABLE | |
| return imap_qprint($this->getBody()); | |
| } | |
| throw new Exception(sprintf('Encoding failed: Unknown encoding %s (5: OTHER).', $encoding)); | |
| } | |
| } | |
| class IMAPAttachments extends ArrayObject | |
| { | |
| private $message; | |
| public function __construct(IMAPMessage $message) | |
| { | |
| $array = $this->setMessage($message); | |
| parent::__construct($array); | |
| } | |
| private function setMessage(IMAPMessage $message) | |
| { | |
| $this->message = $message; | |
| return $this->parseStructure($message->fetchStructure()); | |
| } | |
| private function parseStructure($structure) | |
| { | |
| $attachments = array(); | |
| if (!isset($structure->parts)) { | |
| return $attachments; | |
| } | |
| foreach ($structure->parts as $index => $part) | |
| { | |
| if (!$part->ifdisposition) continue; | |
| $attachment = new stdClass; | |
| $attachment->isAttachment = FALSE; | |
| $attachment->number = $index + 1; | |
| $attachment->bytes = $part->bytes; | |
| $attachment->encoding = $part->encoding; | |
| $attachment->filename = NULL; | |
| $attachment->name = NULL; | |
| $part->ifdparameters | |
| && ($attachment->filename = $this->getAttribute($part->dparameters, 'filename')) | |
| && $attachment->isAttachment = TRUE; | |
| $part->ifparameters | |
| && ($attachment->name = $this->getAttribute($part->parameters, 'name')) | |
| && $attachment->isAttachment = TRUE; | |
| $attachment->isAttachment | |
| && $attachments[] = new IMAPAttachment($this->message, $attachment); | |
| } | |
| return $attachments; | |
| } | |
| private function getAttribute($params, $name) | |
| { | |
| foreach ($params as $object) | |
| { | |
| if ($object->attribute == $name) { | |
| return IMAP::decodeToUTF8($object->value); | |
| } | |
| } | |
| return NULL; | |
| } | |
| } |
Awesome clean code! I adore you!
Somebody try to download mp3 attachment ? I can't make it work
Fatal error: Uncaught Error: Call to undefined function imap_open()
Fatal error: Uncaught Error: Call to undefined function imap_open()
@SerenellaWonder U must install imap extension for php
like this:
sudo apt-get update -y
sudo apt-get install -y php7.2-imap
Hi :)
Can somebody help to get out the Email date, subject and email from using this code, sorry for bothering you guys I am just starting with PHP and not to much into objects, help will be appreciated
Regards.
I use the code and it works perfect for me, but I cant get the subject, email date, email can somebody help me
Regards.
throw new Exception(sprintf('Encoding failed: Unknown encoding %s (5: OTHER).', $encoding)); in IMAPAttachment->__toString() cause error
Throwing an exception from ''__toString'' is only possible since PHP 7.4
))))))) i`m have 7.3 )))))))')
@Den2016: if you're bound to PHP 7.3, change the throw into a trigger_error() with E_USER_ERROR which also fatals and/or E_USER_WARNING and return some fall-through string, depending on your workflow. It's not completely the same, but may give you the wiggle room.
@hakre Many thanks for the source code.
Unfortunately, I have the following problem: If the e-mails are S/MIME signed, I only receive the smime.p7s certificate as an attachment. All other attachments are unfortunately missing.
@twoconcepts: Many thanks for your comment. That's related to S/MIME I'd guess. Are you seeing the email message in plain text or only the attachment? Perhaps the test for $part->ifdisposition (line 304) is now bogus or for the attributes as they all are falsy (e.g. empty string or null, lines 313 and 316).
This was a super helpful gist. Turned it into a set of services for symfony, and it works pretty much outta the box. Thanks a lot.