-
-
Notifications
You must be signed in to change notification settings - Fork 258
MailboxesParser #268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
MailboxesParser #268
Changes from 7 commits
8919812
9da10aa
62827ce
e1b9bd5
c47ca92
65c9841
3868527
ebb9271
d0f855f
bbd78f1
7d5bed6
bb972bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,3 +2,5 @@ vendor/ | |
| .php_cs.cache | ||
| composer.lock | ||
| phpunit.xml | ||
| .idea/ | ||
| phpunit.xml.dist | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,9 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
|
|
||
| <phpunit | ||
| bootstrap="./vendor/autoload.php" | ||
| colors="true" | ||
| verbose="true" | ||
| bootstrap="./vendor/autoload.php" | ||
| colors="true" | ||
| verbose="true" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want to keep one indentation for this file, please revert it. |
||
| > | ||
| <testsuites> | ||
| <testsuite name="ddeboer/imap"> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Ddeboer\Imap\Exception; | ||
|
|
||
| final class MailboxesParserException extends AbstractException | ||
| { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,245 @@ | ||
| <?php | ||
| /** | ||
| * Created by PhpStorm. | ||
| * User: Lukasz | ||
| * Date: 2017-11-10 | ||
| * Time: 19:49 | ||
| */ | ||
|
|
||
| namespace Ddeboer\Imap\MailboxesParser; | ||
|
|
||
|
|
||
| use Ddeboer\Imap\Exception\MailboxesParserException; | ||
| use Ddeboer\Imap\MailboxInterface; | ||
|
|
||
| class MailboxesParser | ||
| { | ||
| /** @var MailboxInterface[] */ | ||
| protected $mailboxes; | ||
| /** @var ParsedMailbox[] */ | ||
| protected $folders; | ||
| protected $treeStructure; | ||
|
|
||
| const INBOX = 'inbox'; | ||
| const SENT = 'sent'; | ||
| const DRAFT = 'drafts'; | ||
| const SPAM = 'spam'; | ||
| const TRASH = 'trash'; | ||
| const TEMPLATES = 'templates'; | ||
| const ARCHIVES = 'archives'; | ||
|
|
||
| protected $specialFoldersIds = [ | ||
| self::INBOX => ['inbox',], | ||
| self::SENT => ['sent', 'sent messages', 'INBOX.Sent', '[Gmail]/Sent Mail',], | ||
| self::DRAFT => ['drafts', 'INBOX.Drafts', '[Gmail]/Drafts',], | ||
| self::SPAM => ['spam', 'INBOX.spam', '[Gmail]/spam'], | ||
| self::TRASH => ['trash', 'bin', 'INBOX.trash', '[Gmail]/trash'], | ||
| self::TEMPLATES => ['templates'], | ||
| self::ARCHIVES => ['archives',], | ||
| ]; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ID of special folder vary a lot from different email provider, I don't want to have a static list to keep up to date. What about letting the user specify it in the constructor, so this library doesn't have to maintain it?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think those are common ones. All other folders are simply listed
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately not, those are not as common as you may think. I'm still against to maintain this list: let the user specify them. Also the tests would be much more easy to implement and verify. |
||
|
|
||
| protected $specialFoldersNames = [ | ||
| self::DRAFT => 'Drafts', | ||
| self::INBOX => 'Inbox', | ||
| self::SENT => 'Sent', | ||
| self::SPAM => 'Spam', | ||
| self::TRASH => 'Trash', | ||
| self::TEMPLATES => 'Templates', | ||
| self::ARCHIVES => 'Archives', | ||
| ]; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same goes for folder names: if you need them, let the user specify them as a constructor dependency. We don't have the resources to maintain translations.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can manually name special folders with |
||
|
|
||
| protected $specialFoldersOrder = [ | ||
| self::INBOX => 1, | ||
| self::SENT => 2, | ||
| self::DRAFT => 3, | ||
| self::TEMPLATES => 4, | ||
| self::ARCHIVES => 10000, | ||
| self::SPAM => 20000, | ||
| self::TRASH => 30000, | ||
| ]; | ||
|
|
||
| /** | ||
| * MailboxesTree constructor. | ||
| * | ||
| * @param MailboxInterface[] $mailboxes | ||
| */ | ||
| public function __construct($mailboxes) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add type-hints
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in php 7.0 I cannot use
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, but you can typehint the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did it
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still see Can you change this to |
||
| { | ||
| $this->mailboxes = $mailboxes; | ||
| } | ||
|
|
||
| public function setLanguage($lang) | ||
| { | ||
| $path = __DIR__ . DIRECTORY_SEPARATOR . 'languages' . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . 'names.php'; | ||
| if (!is_file($path)) { | ||
| throw new MailboxesParserException(\sprintf('File for language names %s does not exist', $path)); | ||
| } | ||
| $names = require $path; | ||
| $this->setSpecialFoldersNames($names); | ||
|
|
||
| $path = __DIR__ . DIRECTORY_SEPARATOR . 'languages' . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . 'ids.php'; | ||
| if (!is_file($path)) { | ||
| throw new MailboxesParserException(\sprintf('File for language ids %s does not exist', $path)); | ||
| } | ||
| $ids = require $path; | ||
| foreach ($ids AS $specialFolder => $idsArray) { | ||
| foreach ($idsArray AS $id) { | ||
| $this->addSpecialFolderId($specialFolder, $id); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected function parse() | ||
|
||
| { | ||
| $this->folders = []; | ||
| usort($this->mailboxes, [$this, "sortByMailboxName"]); | ||
| foreach ($this->mailboxes AS $k => $mailbox) { | ||
| $mailboxName = $mailbox->getName(); | ||
| $folder = new ParsedMailbox(); | ||
| $folder->setMailbox($mailbox); | ||
| $folder->setMailboxName($mailboxName); | ||
| $special = $this->getSpecialFolder($mailboxName); | ||
| $folder->setSpecial($special); | ||
| $folder->setName($special ? $this->specialFoldersNames[$special] : $this->getName($mailboxName, $mailbox->getDelimiter())); | ||
| $folder->setOrder($special ? $this->specialFoldersOrder[$special] : $this->getOrder($mailboxName, $mailbox->getDelimiter())); | ||
| $folder->setLevel($this->getFolderLevel($mailboxName, $mailbox->getDelimiter())); | ||
| $folder->setDelimiter($mailbox->getDelimiter()); | ||
| $this->folders[] = $folder; | ||
| } | ||
|
|
||
| usort($this->folders, [$this, "sortByOrder"]); | ||
|
|
||
| return $this->folders; | ||
| } | ||
|
|
||
|
|
||
| protected function sortByMailboxName(MailboxInterface $a, MailboxInterface $b) | ||
| { | ||
| return ($a->getName() < $b->getName()) ? -1 : 1; | ||
|
||
| } | ||
|
|
||
| protected function sortByOrder(ParsedMailbox $a, ParsedMailbox $b) | ||
| { | ||
| return ($a->getOrder() < $b->getOrder()) ? -1 : 1; | ||
|
||
| } | ||
|
|
||
| /** | ||
| * @return ParsedMailbox[] | ||
| */ | ||
| public function getFolders() | ||
|
||
| { | ||
| if (!$this->folders) { | ||
| $this->parse(); | ||
| } | ||
|
|
||
| return $this->folders; | ||
| } | ||
|
|
||
| /** | ||
| * @return mixed | ||
| */ | ||
| public function getTreeStructure() | ||
|
||
| { | ||
| if (!$this->treeStructure) { | ||
| $treeParser = new MailboxesTreeParser(); | ||
| $this->treeStructure = $treeParser->parse($this->getFolders()); | ||
| } | ||
|
|
||
| return $this->treeStructure; | ||
| } | ||
|
|
||
| /** | ||
| * @param array $specialFoldersNames | ||
| */ | ||
| public function setSpecialFoldersNames(array $specialFoldersNames) | ||
| { | ||
| $this->specialFoldersNames = $specialFoldersNames; | ||
| } | ||
|
|
||
| protected function getSpecialFolder($mailboxName) | ||
| { | ||
| foreach ($this->specialFoldersIds AS $specialFolderKind => $names) { | ||
| $lower = mb_strtolower($mailboxName); | ||
| foreach ($names AS $name) { | ||
| if ($lower === mb_strtolower($name)) { | ||
| return $specialFolderKind; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| private function getName($mailboxName, $delimiter = '.') | ||
|
||
| { | ||
| $e = explode($delimiter, $mailboxName); | ||
|
|
||
| return ucfirst($e[count($e) - 1]); | ||
| } | ||
|
|
||
| private function getOrder($mailboxName, $delimiter) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add type-hints, return-type and doc-block |
||
| { | ||
| if ($this->folders[$mailboxName]['special']) { | ||
| return $this->specialFoldersOrder[$this->folders[$mailboxName]['special']]; | ||
| } else { | ||
| $e = explode($delimiter, $mailboxName); | ||
|
|
||
| $level = count($e) - 1; | ||
| if ($e[0] === 'INBOX' && $level === 1) { | ||
| $level = -1; | ||
| } | ||
| if ($e[1]) { | ||
| array_pop($e); | ||
| $parentMailboxName = implode($delimiter, $e); | ||
| if ($level === -1) { | ||
| $multiplier = 100; | ||
| } else { | ||
| $power = -1 * ($level * 2); | ||
| $multiplier = pow(10, ($power)); | ||
| } | ||
| $parsedParent = null; | ||
| /** @var ParsedMailbox $parsedMailbox */ | ||
| foreach ($this->folders as $parsedMailbox) { | ||
| if ($parsedMailbox->getMailboxName() === $parentMailboxName) { | ||
| $parsedParent = $parsedMailbox; | ||
| break; | ||
| } | ||
| } | ||
| if ($parsedParent) { | ||
| $parsedParent->setSubfolders($parsedParent->getSubfolders() + 1); | ||
| $order = ($parsedParent->getOrder() + ($parsedParent->getSubfolders() * $multiplier)); | ||
| } else { | ||
| $order = 10; | ||
| } | ||
| } else { | ||
| $order = 10; | ||
| } | ||
|
|
||
| return $order; | ||
| } | ||
| } | ||
|
|
||
| public function getMailboxNameForSpecial($special) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add type-hints, return-type and doc-block |
||
| { | ||
| /** @var ParsedMailbox $parsedMailbox */ | ||
| foreach ($this->folders as $parsedMailbox) { | ||
| if ($parsedMailbox->getSpecial() === $special) { | ||
| return $parsedMailbox->getMailboxName(); | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| public function addSpecialFolderId($specialFolder, $id) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add type-hints, return-type and doc-block |
||
| { | ||
| $this->specialFoldersIds[$specialFolder][] = $id; | ||
| } | ||
|
|
||
| private function getFolderLevel($mailboxName, $delimiter) | ||
|
||
| { | ||
| $e = explode($delimiter, $mailboxName); | ||
|
|
||
| return count($e); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| <?php | ||
| /** | ||
| * Created by PhpStorm. | ||
| * User: Lukasz | ||
| * Date: 2017-11-10 | ||
| * Time: 19:50 | ||
| */ | ||
|
|
||
| namespace Ddeboer\Imap\MailboxesParser; | ||
|
|
||
|
|
||
| class MailboxesTreeParser | ||
|
||
| { | ||
| /** | ||
| * @param ParsedMailbox[] $input | ||
| * | ||
| * @return array | ||
| */ | ||
| public function parse($input) | ||
|
||
| { | ||
| $newKeys = array_map( | ||
| function ($key, $value) { | ||
| /** | ||
| * @var ParsedMailbox $value | ||
| */ | ||
| $k = explode($value->getDelimiter(), $value->getMailboxName()); | ||
| $newkey = []; | ||
| foreach ($k as $segment) { | ||
| $newkey[] = $segment; | ||
| $newkey[] = "subfolders"; | ||
| } | ||
|
|
||
| return implode(".", $newkey); | ||
| }, array_keys($input), $input); | ||
|
|
||
| $arrayToParse = []; | ||
| foreach ($newKeys AS $index => $value) { | ||
| $k = explode(".", $value); | ||
| $keyWithoutLast = implode('.', array_splice($k, 0, -1)); | ||
| $arrayToParse[$value] = []; | ||
| if ($input[$index]) { | ||
| /** @var ParsedMailbox $parsedMailbox */ | ||
| $parsedMailbox = $input[$index]; | ||
| $arrayToParse[$keyWithoutLast . '.mailboxName'] = $parsedMailbox->getMailboxName(); | ||
| $arrayToParse[$keyWithoutLast . '.name'] = $parsedMailbox->getName(); | ||
| } | ||
| } | ||
|
|
||
| $res = []; | ||
| array_walk($arrayToParse, function ($value, $key) use (&$res) { | ||
| $this->set($res, $key, $value); | ||
| }); | ||
|
|
||
| return $res; | ||
| } | ||
|
|
||
| private function set(&$array, $key, $value) | ||
| { | ||
| if (is_null($key)) { | ||
| return $array = $value; | ||
| } | ||
| $keys = explode('.', $key); | ||
| while (count($keys) > 1) { | ||
| $key = array_shift($keys); | ||
| // If the key doesn't exist at this depth, we will just create an empty array | ||
| // to hold the next value, allowing us to create the arrays to hold final | ||
| // values at the correct depth. Then we'll keep digging into the array. | ||
| if (!isset($array[$key]) || !is_array($array[$key])) { | ||
| $array[$key] = []; | ||
| } | ||
| $array = &$array[$key]; | ||
| } | ||
| $array[array_shift($keys)] = $value; | ||
|
|
||
| return $array; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need
phpunit.xml.distin the package, please revert.gitignorefile