diff --git a/.php_cs.dist b/.php_cs.dist index 1a50ede4..1459ad4d 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -10,6 +10,8 @@ return PhpCsFixer\Config::create() 'ordered_imports' => true, 'phpdoc_to_comment' => false, 'no_superfluous_phpdoc_tags' => true, + 'declare_strict_types' => true, + 'void_return' => true, 'ordered_class_elements' => true, 'global_namespace_import' => [ 'import_classes' => true, @@ -17,6 +19,13 @@ return PhpCsFixer\Config::create() 'import_functions' => false, ], 'native_function_invocation' => true, + 'php_unit_test_case_static_method_calls' => [ + 'call_type' => 'this', + ], + 'php_unit_method_casing' => true, + 'php_unit_dedicate_assert' => [ + 'target' => 'newest', + ], ]) ->setRiskyAllowed(true) ->setFinder( diff --git a/.travis.yml b/.travis.yml index a35f10ff..5cbe1d88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,7 @@ env: language: php php: - - 5.6 - - 7.0 - - 7.1 + - 7.2.0 - 7.2 - 7.3 @@ -17,10 +15,10 @@ env: matrix: fast_finish: true include: - - php: 5.6 + - php: 7.2.0 dist: xenial env: - - lint="php-cs-fixer" + - lint="yes" - phpunitflags="do not run" - php: 7.4 dist: bionic @@ -45,15 +43,15 @@ before_script: - if [[ "$coverage" = "yes" ]]; then curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter; fi - if [[ "$coverage" = "yes" ]]; then chmod +x ./cc-test-reporter; fi - if [[ "$coverage" = "yes" ]]; then ./cc-test-reporter before-build; fi - - if [[ "$analysis" = "yes" ]]; then composer require --dev paragonie/hidden-string; fi - - if [[ "$analysis" = "yes" ]]; then composer require --dev --update-with-dependencies "phpunit/phpunit:<9" vimeo/psalm psalm/plugin-phpunit maglnet/composer-require-checker:^2.0; fi script: - if [[ "$lint" != "no" ]]; then vendor/bin/parallel-lint .php_cs.dist src tests examples; fi - if [[ "$phpunitflags" != "do not run" ]]; then vendor/bin/phpunit $phpunitflags; fi - if [[ "$analysis" = "yes" ]]; then vendor/bin/composer-require-checker check ./composer.json; fi + - if [[ "$analysis" = "yes" ]]; then vendor/bin/phpmnd ./ --exclude=./.github/ --exclude=./examples/ --exclude=./vendor/ --exclude=./psalm/cache/ --non-zero-exit-on-violation --hint; fi - if [[ "$analysis" = "yes" ]]; then vendor/bin/psalm --show-info=false --shepherd --diff --diff-methods; fi - - if [[ "$lint" = "php-cs-fixer" ]]; then vendor/bin/php-cs-fixer fix --allow-risky=yes --no-interaction --dry-run --diff-format=udiff -v; fi + - if [[ "$lint" = "yes" ]]; then vendor/bin/php-cs-fixer fix --allow-risky=yes --no-interaction --dry-run --diff-format=udiff -v; fi + - if [[ "$lint" = "yes" ]]; then vendor/bin/phpcpd src tests; fi after_script: - if [[ "$coverage" = "yes" ]]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT -t clover; fi diff --git a/README.md b/README.md index ccb30677..bc222963 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) [![Packagist](https://img.shields.io/packagist/dt/php-imap/php-imap.svg?style=flat-square)](https://packagist.org/packages/php-imap/php-imap) [![Build Status](https://travis-ci.org/barbushin/php-imap.svg?branch=master)](https://travis-ci.org/barbushin/php-imap) -[![Supported PHP Version](https://img.shields.io/packagist/php-v/php-imap/php-imap/3.0.8.svg)](README.md) +[![Supported PHP Version](https://img.shields.io/packagist/php-v/php-imap/php-imap.svg)](README.md) [![Maintainability](https://api.codeclimate.com/v1/badges/02f72a4fd695cb7e2976/maintainability)](https://codeclimate.com/github/barbushin/php-imap/maintainability) -[![Coverage](https://api.codeclimate.com/v1/badges/02f72a4fd695cb7e2976/test_coverage)](https://codeclimate.com/github/barbushin/php-imap/test_coverage) +[![Test Coverage](https://api.codeclimate.com/v1/badges/02f72a4fd695cb7e2976/test_coverage)](https://codeclimate.com/github/barbushin/php-imap/test_coverage) [![Type Coverage](https://shepherd.dev/github/barbushin/php-imap/coverage.svg)](https://shepherd.dev/github/barbushin/php-imap) Initially released in December 2012, the PHP IMAP Mailbox is a powerful and open source library to connect to a mailbox by POP3, IMAP and NNTP using the PHP IMAP extension. This library allows you to fetch emails from your email server. Extend the functionality or create powerful web applications to handle your incoming emails. @@ -22,10 +22,19 @@ Initially released in December 2012, the PHP IMAP Mailbox is a powerful and open ### Requirements -* PHP 5.6, 7.0, 7.1, 7.2, 7.3 or 7.4 +| PHP Version | php-imap Version | +| ------------- | ------------- | +| 5.6 | 3.x | +| 7.0 | 3.x | +| 7.1 | 3.x | +| 7.2 | 3.x, 4.x | +| 7.3 | 3.x, 4.x | +| 7.4 | >3.0.33, 4.x | + +* PHP `fileinfo` extension must be present; so make sure this line is active in your php.ini: `extension=php_fileinfo.dll` +* PHP `iconv` extension must be present; so make sure this line is active in your php.ini: `extension=php_iconv.dll` * PHP `imap` extension must be present; so make sure this line is active in your php.ini: `extension=php_imap.dll` * PHP `mbstring` extension must be present; so make sure this line is active in your php.ini: `extension=php_mbstring.dll` -* PHP `iconv` extension must be present, if `mbstring` is not available; so make sure this line is active in your php.ini: `extension=php_iconv.dll` ### Installation by Composer @@ -41,9 +50,15 @@ Install the latest available and may unstable source code from `develop`, which $ composer require php-imap/php-imap:dev-develop -### PHPUnit Tests +### Run Tests -Before you can run the PHPUnit tests you may need to run `composer install` to install all (development) dependencies. +Before you can run the any tests you may need to run `composer install` to install all (development) dependencies. + +#### Run all tests + +You can run all available tests by running the following command (inside of the installed `php-imap` directory): `composer run tests` + +#### Run only PHPUnit tests You can run all PHPUnit tests by running the following command (inside of the installed `php-imap` directory): `php vendor/bin/phpunit --testdox` diff --git a/composer.json b/composer.json index 1c55ef46..a57ff16d 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "sort-packages": true }, "require": { - "php": ">=5.6", + "php": "^7.2", "ext-fileinfo": "*", "ext-iconv": "*", "ext-imap": "*", @@ -32,16 +32,24 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^2.16", "jakub-onderka/php-parallel-lint": "^1.0", - "paragonie/random_compat": "^1", - "phpunit/phpunit": "^5.7" + "maglnet/composer-require-checker": "^2.0", + "paragonie/hidden-string": "^1.0", + "phpunit/phpunit": "^8.5", + "povils/phpmnd": "^2.2", + "psalm/plugin-phpunit": "^0.10.0", + "roave/security-advisories": "dev-master", + "sebastian/phpcpd": "^4.1", + "vimeo/psalm": "^3.11" }, "scripts": { "tests": [ "parallel-lint .php_cs.dist src tests examples", - "phpunit", + "phpcpd src tests", + "phpunit --testdox", "composer-require-checker check ./composer.json", - "psalm --show-info=false", - "php-cs-fixer fix --allow-risky=yes --no-interaction --dry-run --diff-format=udiff -v" + "phpmnd ./ --exclude=./.github/ --exclude=./examples/ --exclude=./vendor/ --non-zero-exit-on-violation --hint", + "php-cs-fixer fix --allow-risky=yes --no-interaction --dry-run --diff-format=udiff -v", + "psalm --show-info=false" ] }, "suggest": { diff --git a/examples/get_and_parse_all_emails_with_matching_subject.php b/examples/get_and_parse_all_emails_with_matching_subject.php index 6ccbf6d4..7cb8e52b 100644 --- a/examples/get_and_parse_all_emails_with_matching_subject.php +++ b/examples/get_and_parse_all_emails_with_matching_subject.php @@ -5,7 +5,10 @@ * * @author Sebastian Krätzig */ + declare(strict_types=1); + require_once __DIR__.'/../vendor/autoload.php'; + use PhpImap\Exceptions\ConnectionException; use PhpImap\Mailbox; diff --git a/examples/get_and_parse_all_emails_without_saving_attachments.php b/examples/get_and_parse_all_emails_without_saving_attachments.php index 73022d29..6913edb6 100644 --- a/examples/get_and_parse_all_emails_without_saving_attachments.php +++ b/examples/get_and_parse_all_emails_without_saving_attachments.php @@ -5,7 +5,10 @@ * * @author Sebastian Krätzig */ + declare(strict_types=1); + require_once __DIR__.'/../vendor/autoload.php'; + use PhpImap\Exceptions\ConnectionException; use PhpImap\Mailbox; diff --git a/examples/get_and_parse_unseen_emails.php b/examples/get_and_parse_unseen_emails.php index b18c82e4..76462366 100644 --- a/examples/get_and_parse_unseen_emails.php +++ b/examples/get_and_parse_unseen_emails.php @@ -5,7 +5,10 @@ * * @author Sebastian Krätzig */ + declare(strict_types=1); + require_once __DIR__.'/../vendor/autoload.php'; + use PhpImap\Exceptions\ConnectionException; use PhpImap\Mailbox; diff --git a/examples/get_and_parse_unseen_emails_save_attachments_one_by_one.php b/examples/get_and_parse_unseen_emails_save_attachments_one_by_one.php index 355acc25..86cd1fca 100644 --- a/examples/get_and_parse_unseen_emails_save_attachments_one_by_one.php +++ b/examples/get_and_parse_unseen_emails_save_attachments_one_by_one.php @@ -5,7 +5,10 @@ * * @author Sebastian Krätzig */ + declare(strict_types=1); + require_once __DIR__.'/../vendor/autoload.php'; + use PhpImap\Exceptions\ConnectionException; use PhpImap\Mailbox; diff --git a/psalm.baseline.xml b/psalm.baseline.xml index fa0b0955..36fc39b2 100644 --- a/psalm.baseline.xml +++ b/psalm.baseline.xml @@ -1,92 +1,16 @@ - + $mailbox - - \is_string($mailbox) - \is_string($message) - \is_string($options) - null !== $options && !\is_string($options) - \is_string($internal_date) - null !== $internal_date && !\is_string($internal_date) - \is_int($msg_number) - \is_int($options) - \is_string($flag) - \is_int($options) - \is_int($flag) - \is_string($mailbox) - \is_int($options) - \is_string($mailbox) - \is_int($options) - \is_int($msg_number) + \is_int($section) !\is_string($section) && !\is_int($section) - \is_int($options) - \is_int($msg_number) - \is_int($options) - \is_int($msg_number) - \is_int($options) - \is_string($quota_root) - \is_string($ref) - \is_string($pattern) - \is_string($ref) - \is_string($pattern) - \is_string($ref) - \is_string($pattern) - \is_string($mailbox) - \is_int($options) - \is_string($mailbox) - \is_int($options) - \is_string($mailbox) - \is_string($username) - \is_string($password) - \is_int($options) - \is_int($n_retries) - \is_string($old_mbox) - \is_string($new_mbox) - \is_string($mailbox) - \is_int($options) - \is_int($n_retries) - \is_int($msg_number) - \is_string($part_number) - \is_int($options) - \is_string($criteria) - \is_int($options) - \is_string($charset) - null !== $charset && !\is_string($charset) - \is_string($flag) - \is_int($options) - \is_int($criteria) - \is_bool($reverse) - \is_int($options) - \is_string($search_criteria) - null !== $search_criteria && !\is_string($search_criteria) - \is_string($charset) - null !== $charset && !\is_string($charset) - \is_string($mailbox) - \is_int($options) - \is_string($mailbox) - \is_int($timeout_type) - \is_int($timeout) - \is_string($mailbox) - \is_string($str) - \is_string($str) - \is_string($method) - \is_int($argument) \is_resource($maybe) - \is_string($method) - \is_string($method) - \is_int($argument) - \is_bool($allow_sequence) - - $result - array[] - @@ -98,18 +22,26 @@ - + \in_array($imapSearchOption, $supported_options, true) - \is_int($retriesNum) \in_array($key, $supported_params, true) - - \random_bytes(20) - \random_bytes(16) + + $element->charset + $element->charset + $element->text + $element->charset + $element->charset + $element->text - - setOAuthToken - getOAuthToken + + array + + + $lowercase_encodings + $lowercase_encodings + + setConnectionRetry setConnectionRetryDelay setExpungeOnDisconnect @@ -131,122 +63,14 @@ getMailboxInfo getQuotaLimit getQuotaUsage - getImapPath getSubscribedMailboxes subscribeMailbox unsubscribeMailbox - - !\is_int($retriesNum) or $retriesNum < 0 - \is_int($retriesNum) - - - - - \is_string($attachment->id) - assertTrue - - - $this->assertTrue($mail->__isset('textPlain')) - $this->assertFalse($mail->hasAttachments()) - $this->assertTrue($mail->hasAttachments()) - $this->assertTrue(\is_string($attachment->id)) - $this->assertTrue($mail->removeAttachment($attachment->id)) - $this->assertFalse($mail->hasAttachments()) - $this->assertTrue(\is_string($attachment->id)) - $this->assertFalse($mail->removeAttachment($attachment->id)) - - - - - \random_bytes(16) - \random_bytes(16) - \random_bytes(16) - \random_bytes(16) - \random_bytes(16) - \random_bytes(16) - \random_bytes(4) - - - $envelope['subject'] - $envelope['subject'] - $envelope['subject'] - $envelope['subject'] - $envelope['subject'] - - - $this->markTestSkipped('paragonie/hidden-string not installed!') - $this->assertTrue(\is_resource($mailbox->getImapStream())) - $this->assertTrue($mailbox->hasImapStream()) - static::assertTrue(\is_array($mailboxes[$i])) - static::assertTrue(isset($mailboxes[$i]['shortpath'])) - static::assertTrue(\is_string($mailboxes[$i]['shortpath'])) - $this->assertTrue(\property_exists($check, $expectedProperty)) - $this->assertTrue(\is_string($check->Date), 'Date property of Mailbox::checkMailbox() result was not a string!') - $this->assertTrue(\is_int($unix), 'Date property of Mailbox::checkMailbox() result was not a valid date!') - $this->assertTrue(\in_array($check->Driver, ['POP3', 'IMAP', 'NNTP', 'pop3', 'imap', 'nntp'], true), 'Driver property of Mailbox::checkMailbox() result was not of an expected value!') - $this->assertTrue(\is_int($check->Nmsgs), 'Nmsgs property of Mailbox::checkMailbox() result was not of an expected type!') - $this->assertTrue(\is_int($check->Recent), 'Recent property of Mailbox::checkMailbox() result was not of an expected type!') - $this->assertTrue(\property_exists($status, $expectedProperty)) - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)) - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)) - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)) - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)) - static::assertCount(1, $info) - static::assertTrue($mail->hasAttachments()) - static::assertCount(1, $attachments) - - - assertAttributeEquals - assertAttributeEquals - assertAttributeEquals - assertAttributeEquals - assertAttributeEquals - assertAttributeEquals - assertAttributeEquals - assertAttributeEquals - self::ANYTHING - - $this->assertAttributeEquals('{imap.example.com:993/imap/ssl}INBOX', 'imapPath', $mailbox) - $this->assertAttributeEquals('php-imap@example.com', 'imapLogin', $mailbox) - $this->assertAttributeEquals(' v3rY!53cEt&P4sSWöRd$', 'imapPassword', $mailbox) - $this->assertAttributeEquals(\realpath('.'), 'attachmentsDir', $mailbox) - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox) - $this->assertEquals($mailbox->getServerEncoding(), $encoding) - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox) - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox) - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox) - $this->assertEquals($encoding, $mailbox->getServerEncoding()) - $this->assertNotEquals($encoding, $mailbox->getServerEncoding()) - $this->assertEquals($this->getMailbox()->getImapSearchOption(), 1) - $this->assertEquals($mailbox->getImapSearchOption(), 2) - $this->assertEquals($mailbox->getImapSearchOption(), 1) - $this->assertEquals($this->getMailbox()->getLogin(), 'php-imap@example.com') - $this->assertNotEmpty($this->getMailbox()->getPathDelimiter()) - $this->assertTrue($mailbox->validatePathDelimiter($str)) - $this->assertEquals($mailbox->getPathDelimiter(), '.') - $this->assertEquals($mailbox->getPathDelimiter(), '/') - $this->assertEquals($this->getMailbox()->getAttachmentsIgnore(), false) - $this->assertEquals($mailbox->getAttachmentsIgnore(), (bool) $paramValue) - $this->assertEquals($utf8_decoded_str, $str) - $this->assertEquals($this->getMailbox()->decodeMimeStr($str, 'utf-8'), $str) - $this->assertEquals($parsedDateTime->format('U'), $epochToCompare) - $this->assertEquals($parsedDt, $dateToParse) - $this->assertEquals($mailbox->decodeMimeStr($str), $expected) - $this->assertNull($mailbox->setTimeouts($timeout, $types)) - $this->assertNull($mailbox->setConnectionArgs($option, $retriesNum, $param)) - $this->assertEquals($mailbox->decodeMimeStr($str, $mailbox->getServerEncoding()), $expectedStr) - $this->expectExceptionMessage($expectedExceptionMessage) - - - - - $this->assertTrue(\extension_loaded($extension)) - diff --git a/psalm.xml b/psalm.xml index 7601cabf..a544ed84 100644 --- a/psalm.xml +++ b/psalm.xml @@ -3,7 +3,7 @@ totallyTyped="true" findUnusedCode="true" resolveFromConfigFile="true" - requireVoidReturnType="false" + requireVoidReturnType="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" diff --git a/src/PhpImap/DataPartInfo.php b/src/PhpImap/DataPartInfo.php index a3d953bf..3598844f 100644 --- a/src/PhpImap/DataPartInfo.php +++ b/src/PhpImap/DataPartInfo.php @@ -1,5 +1,7 @@ mail = $mail; $this->id = $id; @@ -68,10 +69,7 @@ public function __construct(Mailbox $mail, $id, $part, $encoding, $options) $this->options = $options; } - /** - * @return string - */ - public function fetch() + public function fetch(): string { if (0 === $this->part) { $this->data = Imap::body($this->mail->getImapStream(), $this->id, $this->options); @@ -82,10 +80,7 @@ public function fetch() return $this->decodeAfterFetch(); } - /** - * @return string - */ - protected function decodeAfterFetch() + protected function decodeAfterFetch(): string { switch ($this->encoding) { case ENC8BIT: @@ -95,8 +90,7 @@ protected function decodeAfterFetch() $this->data = \imap_binary((string) $this->data); break; case ENCBASE64: - $this->data = \preg_replace('~[^a-zA-Z0-9+=/]+~s', '', (string) $this->data); // https://github.com/barbushin/php-imap/issues/88 - $this->data = \imap_base64((string) $this->data); + $this->data = \base64_decode((string) $this->data, false); break; case ENCQUOTEDPRINTABLE: $this->data = \quoted_printable_decode((string) $this->data); @@ -106,16 +100,11 @@ protected function decodeAfterFetch() return $this->convertEncodingAfterFetch(); } - /** - * @return string - */ - protected function convertEncodingAfterFetch() + protected function convertEncodingAfterFetch(): string { if (isset($this->charset) and !empty(\trim($this->charset))) { - $this->data = $this->mail->convertStringEncoding( - (string) $this->data, // Data to convert - $this->charset, // FROM-Encoding (Charset) - $this->mail->getServerEncoding() // TO-Encoding (Charset) + $this->data = $this->mail->decodeMimeStr( + (string) $this->data // Data to convert ); } diff --git a/src/PhpImap/Exceptions/ConnectionException.php b/src/PhpImap/Exceptions/ConnectionException.php index b4b4f73b..54153a8f 100644 --- a/src/PhpImap/Exceptions/ConnectionException.php +++ b/src/PhpImap/Exceptions/ConnectionException.php @@ -1,5 +1,7 @@ $flag * @psalm-param 0|32768 $flag * * @return true */ - public static function close($imap_stream, $flag = 0) + public static function close($imap_stream, int $flag = 0): bool { - if (!\is_int($flag)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be an integer, '.\gettype($flag).' given!'); - } - \imap_errors(); // flush errors $result = \imap_close(self::EnsureResource($imap_stream, __METHOD__, 1), $flag); @@ -248,16 +205,11 @@ public static function close($imap_stream, $flag = 0) /** * @param false|resource $imap_stream - * @param string $mailbox * * @return true */ - public static function createmailbox($imap_stream, $mailbox) + public static function createmailbox($imap_stream, string $mailbox): bool { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($mailbox).' given!'); - } - \imap_errors(); // flush errors $result = \imap_createmailbox( @@ -275,19 +227,14 @@ public static function createmailbox($imap_stream, $mailbox) /** * @param false|resource $imap_stream * @param string|int $msg_number - * @param int $options * * @return true */ public static function delete( $imap_stream, $msg_number, - $options = 0 - ) { - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + int $options = 0 + ): bool { /** * @var int * @@ -316,16 +263,11 @@ public static function delete( /** * @param false|resource $imap_stream - * @param string $mailbox * * @return true */ - public static function deletemailbox($imap_stream, $mailbox) + public static function deletemailbox($imap_stream, string $mailbox): bool { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($mailbox).' given!'); - } - \imap_errors(); // flush errors $result = \imap_deletemailbox( @@ -345,7 +287,7 @@ public static function deletemailbox($imap_stream, $mailbox) * * @return true */ - public static function expunge($imap_stream) + public static function expunge($imap_stream): bool { \imap_errors(); // flush errors @@ -363,7 +305,6 @@ public static function expunge($imap_stream) /** * @param false|resource $imap_stream * @param int|string $sequence - * @param int $options * * @return object[] * @@ -372,12 +313,8 @@ public static function expunge($imap_stream) public static function fetch_overview( $imap_stream, $sequence, - $options = 0 - ) { - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + int $options = 0 + ): array { \imap_errors(); // flush errors $result = \imap_fetch_overview( @@ -401,26 +338,16 @@ public static function fetch_overview( /** * @param false|resource $imap_stream - * @param int $msg_number * @param string|int $section - * @param int $options - * - * @return string */ public static function fetchbody( $imap_stream, - $msg_number, + int $msg_number, $section, - $options = 0 - ) { - if (!\is_int($msg_number)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be an integer, '.\gettype($msg_number).' given!'); - } + int $options = 0 + ): string { if (!\is_string($section) && !\is_int($section)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be a string or an integer, '.\gettype($section).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); + throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be a string or integer, '.\gettype($section).' given!'); } \imap_errors(); // flush errors @@ -441,23 +368,12 @@ public static function fetchbody( /** * @param false|resource $imap_stream - * @param int $msg_number - * @param int $options - * - * @return string */ public static function fetchheader( $imap_stream, - $msg_number, - $options = 0 - ) { - if (!\is_int($msg_number)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be an integer, '.\gettype($msg_number).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + int $msg_number, + int $options = 0 + ): string { \imap_errors(); // flush errors $result = \imap_fetchheader( @@ -475,25 +391,14 @@ public static function fetchheader( /** * @param false|resource $imap_stream - * @param int $msg_number - * @param int $options - * - * @return object * * @psalm-return PARTSTRUCTURE */ public static function fetchstructure( $imap_stream, - $msg_number, - $options = 0 - ) { - if (!\is_int($msg_number)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be an integer, '.\gettype($msg_number).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + int $msg_number, + int $options = 0 + ): object { \imap_errors(); // flush errors $result = \imap_fetchstructure( @@ -512,18 +417,13 @@ public static function fetchstructure( /** * @param false|resource $imap_stream - * @param string $quota_root * - * @return array[] + * @todo add return array shape pending resolution of https://github.com/vimeo/psalm/issues/2620 */ public static function get_quotaroot( $imap_stream, - $quota_root - ) { - if (!\is_string($quota_root)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($quota_root).' given!'); - } - + string $quota_root + ): array { \imap_errors(); // flush errors $result = \imap_get_quotaroot( @@ -540,8 +440,6 @@ public static function get_quotaroot( /** * @param resource|false $imap_stream - * @param string $ref - * @param string $pattern * * @return object[] * @@ -549,16 +447,9 @@ public static function get_quotaroot( */ public static function getmailboxes( $imap_stream, - $ref, - $pattern - ) { - if (!\is_string($ref)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($ref).' given!'); - } - if (!\is_string($pattern)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be a string, '.\gettype($pattern).' given!'); - } - + string $ref, + string $pattern + ): array { \imap_errors(); // flush errors $result = \imap_getmailboxes( @@ -587,8 +478,6 @@ public static function getmailboxes( /** * @param resource|false $imap_stream - * @param string $ref - * @param string $pattern * * @return object[] * @@ -596,16 +485,9 @@ public static function getmailboxes( */ public static function getsubscribed( $imap_stream, - $ref, - $pattern - ) { - if (!\is_string($ref)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($ref).' given!'); - } - if (!\is_string($pattern)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be a string, '.\gettype($pattern).' given!'); - } - + string $ref, + string $pattern + ): array { \imap_errors(); // flush errors $result = \imap_getsubscribed( @@ -624,10 +506,8 @@ public static function getsubscribed( /** * @param false|resource $imap_stream - * - * @return array */ - public static function headers($imap_stream) + public static function headers($imap_stream): array { \imap_errors(); // flush errors @@ -644,22 +524,13 @@ public static function headers($imap_stream) /** * @param false|resource $imap_stream - * @param string $ref - * @param string $pattern * * @return string[] * * @psalm-return list */ - public static function listOfMailboxes($imap_stream, $ref, $pattern) + public static function listOfMailboxes($imap_stream, string $ref, string $pattern): array { - if (!\is_string($ref)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($ref).' given!'); - } - if (!\is_string($pattern)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be a string, '.\gettype($pattern).' given!'); - } - \imap_errors(); // flush errors $result = \imap_list( @@ -673,12 +544,7 @@ public static function listOfMailboxes($imap_stream, $ref, $pattern) } return \array_values(\array_map( - /** - * @param string $folder - * - * @return string - */ - static function ($folder) { + static function (string $folder): string { return static::decodeStringFromUtf7ImapToUtf8($folder); }, $result @@ -701,11 +567,9 @@ static function ($folder) { * disposition?:array{filename:string} * }> $body An indexed array of bodies (docblock is not complete) * - * @return string - * * @todo flesh out array shape pending resolution of https://github.com/vimeo/psalm/issues/1518 */ - public static function mail_compose(array $envelope, array $body) + public static function mail_compose(array $envelope, array $body): string { return \imap_mail_compose($envelope, $body); } @@ -713,24 +577,15 @@ public static function mail_compose(array $envelope, array $body) /** * @param false|resource $imap_stream * @param int|string $msglist - * @param string $mailbox - * @param int $options * * @return true */ public static function mail_copy( $imap_stream, $msglist, - $mailbox, - $options = 0 - ) { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be a string, '.\gettype($mailbox).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + string $mailbox, + int $options = 0 + ): bool { \imap_errors(); // flush errors $result = \imap_mail_copy( @@ -755,24 +610,15 @@ public static function mail_copy( /** * @param false|resource $imap_stream * @param int|string $msglist - * @param string $mailbox - * @param int $options * * @return true */ public static function mail_move( $imap_stream, $msglist, - $mailbox, - $options = 0 - ) { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be a string, '.\gettype($mailbox).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + string $mailbox, + int $options = 0 + ): bool { \imap_errors(); // flush errors $result = \imap_mail_move( @@ -796,10 +642,8 @@ public static function mail_move( /** * @param false|resource $imap_stream - * - * @return object */ - public static function mailboxmsginfo($imap_stream) + public static function mailboxmsginfo($imap_stream): object { \imap_errors(); // flush errors @@ -816,10 +660,8 @@ public static function mailboxmsginfo($imap_stream) /** * @param false|resource $imap_stream - * - * @return int */ - public static function num_msg($imap_stream) + public static function num_msg($imap_stream): int { \imap_errors(); // flush errors @@ -833,40 +675,18 @@ public static function num_msg($imap_stream) } /** - * @param string $mailbox - * @param string $username - * @param string $password - * @param int $options - * @param int $n_retries - * * @psalm-param array{DISABLE_AUTHENTICATOR:string}|array $params * * @return resource */ public static function open( - $mailbox, - $username, - $password, - $options = 0, - $n_retries = 0, + string $mailbox, + string $username, + string $password, + int $options = 0, + int $n_retries = 0, array $params = [] ) { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 1 passed to '.__METHOD__.' must be a string, '.\gettype($mailbox).' given!'); - } - if (!\is_string($username)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be a string, '.\gettype($username).' given!'); - } - if (!\is_string($password)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.' must be a string, '.\gettype($password).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - if (!\is_int($n_retries)) { - throw new InvalidArgumentException('Argument 5 passed to '.__METHOD__.'() must be an integer, '.\gettype($n_retries).' given!'); - } - if (\preg_match("/^\{.*\}(.*)$/", $mailbox, $matches)) { $mailbox_name = $matches[1]; @@ -894,33 +714,22 @@ public static function open( /** * @param resource|false $imap_stream - * - * @return bool */ - public static function ping($imap_stream) + public static function ping($imap_stream): bool { return \is_resource($imap_stream) && \imap_ping($imap_stream); } /** * @param false|resource $imap_stream - * @param string $old_mbox - * @param string $new_mbox * * @return true */ public static function renamemailbox( $imap_stream, - $old_mbox, - $new_mbox - ) { - if (!\is_string($old_mbox)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be a string, '.\gettype($old_mbox).' given!'); - } - if (!\is_string($new_mbox)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.' must be a string, '.\gettype($new_mbox).' given!'); - } - + string $old_mbox, + string $new_mbox + ): bool { $imap_stream = self::EnsureResource($imap_stream, __METHOD__, 1); $old_mbox = static::encodeStringToUtf7Imap($old_mbox); @@ -939,28 +748,15 @@ public static function renamemailbox( /** * @param false|resource $imap_stream - * @param string $mailbox - * @param int $options - * @param int $n_retries * * @return true */ public static function reopen( $imap_stream, - $mailbox, - $options = 0, - $n_retries = 0 - ) { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be a string, '.\gettype($mailbox).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.' must be an integer, '.\gettype($options).' given!'); - } - if (!\is_int($n_retries)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.' must be an integer, '.\gettype($n_retries).' given!'); - } - + string $mailbox, + int $options = 0, + int $n_retries = 0 + ): bool { $imap_stream = self::EnsureResource($imap_stream, __METHOD__, 1); $mailbox = static::encodeStringToUtf7Imap($mailbox); @@ -979,29 +775,16 @@ public static function reopen( /** * @param false|resource $imap_stream * @param string|false|resource $file - * @param int $msg_number - * @param string $part_number - * @param int $options * * @return true */ public static function savebody( $imap_stream, $file, - $msg_number, - $part_number = '', - $options = 0 - ) { - if (!\is_int($msg_number)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be an integer, '.\gettype($msg_number).' given!'); - } - if (!\is_string($part_number)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.'() must be an integer, '.\gettype($part_number).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 5 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + int $msg_number, + string $part_number = '', + int $options = 0 + ): bool { $imap_stream = self::EnsureResource($imap_stream, __METHOD__, 1); $file = \is_string($file) ? $file : self::EnsureResource($file, __METHOD__, 2); $part_number = self::encodeStringToUtf7Imap($part_number); @@ -1019,9 +802,6 @@ public static function savebody( /** * @param false|resource $imap_stream - * @param string $criteria - * @param int $options - * @param string $charset * * @return int[] * @@ -1029,20 +809,10 @@ public static function savebody( */ public static function search( $imap_stream, - $criteria, - $options = SE_FREE, - $charset = null - ) { - if (!\is_string($criteria)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be a string, '.\gettype($criteria).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.' must be a string, '.\gettype($options).' given!'); - } - if (null !== $charset && !\is_string($charset)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.' must be a string or null, '.\gettype($charset).' given!'); - } - + string $criteria, + int $options = SE_FREE, + string $charset = null + ): array { \imap_errors(); // flush errors $imap_stream = static::EnsureResource($imap_stream, __METHOD__, 1); @@ -1080,24 +850,15 @@ public static function search( /** * @param false|resource $imap_stream * @param int|string $sequence - * @param string $flag - * @param int $options * * @return true */ public static function setflag_full( $imap_stream, $sequence, - $flag, - $options = NIL - ) { - if (!\is_string($flag)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.' must be a string, '.\gettype($flag).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.' must be an integer, '.\gettype($options).' given!'); - } - + string $flag, + int $options = NIL + ): bool { \imap_errors(); // flush errors $result = \imap_setflag_full( @@ -1121,11 +882,6 @@ public static function setflag_full( /** * @param false|resource $imap_stream - * @param int $criteria - * @param bool $reverse - * @param int $options - * @param string|null $search_criteria - * @param string|null $charset * * @psalm-param value-of $criteria * @psalm-param 1|5|0|2|6|3|4 $criteria @@ -1136,28 +892,12 @@ public static function setflag_full( */ public static function sort( $imap_stream, - $criteria, - $reverse, - $options, - $search_criteria = null, - $charset = null - ) { - if (!\is_int($criteria)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be an integer, '.\gettype($criteria).' given!'); - } - if (!\is_bool($reverse)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.' must be a boolean, '.\gettype($reverse).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 4 passed to '.__METHOD__.' must be an integer, '.\gettype($options).' given!'); - } - if (null !== $search_criteria && !\is_string($search_criteria)) { - throw new InvalidArgumentException('Argument 5 passed to '.__METHOD__.' must be a string or null, '.\gettype($search_criteria).' given!'); - } - if (null !== $charset && !\is_string($charset)) { - throw new InvalidArgumentException('Argument 6 passed to '.__METHOD__.' must be a string or null, '.\gettype($charset).' given!'); - } - + int $criteria, + bool $reverse, + int $options, + string $search_criteria = null, + string $charset = null + ): array { \imap_errors(); // flush errors $imap_stream = self::EnsureResource($imap_stream, __METHOD__, 1); @@ -1199,25 +939,14 @@ public static function sort( /** * @param false|resource $imap_stream - * @param string $mailbox - * @param int $options * * @psalm-param SA_MESSAGES|SA_RECENT|SA_UNSEEN|SA_UIDNEXT|SA_UIDVALIDITY|SA_ALL $flags - * - * @return object */ public static function status( $imap_stream, - $mailbox, - $options - ) { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($mailbox).' given!'); - } - if (!\is_int($options)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be an integer, '.\gettype($options).' given!'); - } - + string $mailbox, + int $options + ): object { $imap_stream = self::EnsureResource($imap_stream, __METHOD__, 1); $mailbox = static::encodeStringToUtf7Imap($mailbox); @@ -1235,18 +964,11 @@ public static function status( /** * @param false|resource $imap_stream - * @param string $mailbox - * - * @return void */ public static function subscribe( $imap_stream, - $mailbox - ) { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($mailbox).' given!'); - } - + string $mailbox + ): void { $imap_stream = self::EnsureResource($imap_stream, __METHOD__, 1); $mailbox = static::encodeStringToUtf7Imap($mailbox); @@ -1261,25 +983,15 @@ public static function subscribe( } /** - * @param int $timeout_type - * @param int $timeout - * * @psalm-param value-of $timeout_type * @psalm-param 4|1|2|3 $timeout_type * * @return true|int */ public static function timeout( - $timeout_type, - $timeout = -1 + int $timeout_type, + int $timeout = -1 ) { - if (!\is_int($timeout_type)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be an integer, '.\gettype($timeout_type).' given!'); - } - if (!\is_int($timeout)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.'() must be an integer, '.\gettype($timeout).' given!'); - } - \imap_errors(); // flush errors $result = \imap_timeout( @@ -1296,18 +1008,11 @@ public static function timeout( /** * @param false|resource $imap_stream - * @param string $mailbox - * - * @return void */ public static function unsubscribe( $imap_stream, - $mailbox - ) { - if (!\is_string($mailbox)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($mailbox).' given!'); - } - + string $mailbox + ): void { $imap_stream = self::EnsureResource($imap_stream, __METHOD__, 1); $mailbox = static::encodeStringToUtf7Imap($mailbox); @@ -1324,16 +1029,10 @@ public static function unsubscribe( /** * Returns the provided string in UTF7-IMAP encoded format. * - * @param string $str - * * @return string $str UTF-7 encoded string */ - public static function encodeStringToUtf7Imap($str) + public static function encodeStringToUtf7Imap(string $str): string { - if (!\is_string($str)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($str).' given!'); - } - $out = \mb_convert_encoding($str, 'UTF7-IMAP', \mb_detect_encoding($str, 'UTF-8, ISO-8859-1, ISO-8859-15', true)); if (!\is_string($out)) { @@ -1346,16 +1045,10 @@ public static function encodeStringToUtf7Imap($str) /** * Returns the provided string in UTF-8 encoded format. * - * @param string $str - * * @return string $str, but UTF-8 encoded */ - public static function decodeStringFromUtf7ImapToUtf8($str) + public static function decodeStringFromUtf7ImapToUtf8(string $str): string { - if (!\is_string($str)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.'() must be a string, '.\gettype($str).' given!'); - } - $out = \mb_convert_encoding($str, 'UTF-8', 'UTF7-IMAP'); if (!\is_string($out)) { @@ -1367,22 +1060,13 @@ public static function decodeStringFromUtf7ImapToUtf8($str) /** * @param false|resource $maybe - * @param string $method - * @param int $argument * * @throws InvalidArgumentException if $maybe is not a valid resource * * @return resource */ - private static function EnsureResource($maybe, $method, $argument) + private static function EnsureResource($maybe, string $method, int $argument) { - if (!\is_string($method)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be a string, '.\gettype($method).' given!'); - } - if (!\is_int($argument)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be an integer, '.\gettype($argument).' given!'); - } - if (!$maybe || !\is_resource($maybe)) { throw new InvalidArgumentException('Argument '.(string) $argument.' passed to '.$method.' must be a valid resource!'); } @@ -1393,16 +1077,9 @@ private static function EnsureResource($maybe, $method, $argument) /** * @param array|false $errors - * @param string $method - * - * @return UnexpectedValueException */ - private static function HandleErrors($errors, $method) + private static function HandleErrors($errors, string $method): UnexpectedValueException { - if (!\is_string($method)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be a string, '.\gettype($method).' given!'); - } - if ($errors) { return new UnexpectedValueException('IMAP method '.$method.'() failed with error: '.\implode('. ', $errors)); } @@ -1412,30 +1089,16 @@ private static function HandleErrors($errors, $method) /** * @param scalar $msg_number - * @param string $method - * @param int $argument - * @param bool $allow_sequence - * - * @return string */ private static function EnsureRange( $msg_number, - $method, - $argument, - $allow_sequence = false - ) { + string $method, + int $argument, + bool $allow_sequence = false + ): string { if (!\is_int($msg_number) && !\is_string($msg_number)) { throw new InvalidArgumentException('Argument 1 passed to '.__METHOD__.'() must be an integer or a string!'); } - if (!\is_string($method)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be a string, '.\gettype($method).' given!'); - } - if (!\is_int($argument)) { - throw new InvalidArgumentException('Argument 2 passed to '.__METHOD__.' must be an integer, '.\gettype($argument).' given!'); - } - if (!\is_bool($allow_sequence)) { - throw new InvalidArgumentException('Argument 3 passed to '.__METHOD__.' must be a boolean, '.\gettype($allow_sequence).' given!'); - } $regex = '/^\d+:\d+$/'; $suffix = '() did not appear to be a valid message id range!'; diff --git a/src/PhpImap/IncomingMail.php b/src/PhpImap/IncomingMail.php index 8b30e3d1..c253e830 100644 --- a/src/PhpImap/IncomingMail.php +++ b/src/PhpImap/IncomingMail.php @@ -1,5 +1,7 @@ $name); } - public function setHeader(IncomingMailHeader $header) + public function setHeader(IncomingMailHeader $header): void { /** @psalm-var array */ $array = \get_object_vars($header); @@ -93,12 +95,12 @@ public function setHeader(IncomingMailHeader $header) /** * @param DataPartInfo::TEXT_PLAIN|DataPartInfo::TEXT_HTML $type */ - public function addDataPartInfo(DataPartInfo $dataInfo, $type) + public function addDataPartInfo(DataPartInfo $dataInfo, int $type): void { $this->dataInfo[$type][] = $dataInfo; } - public function addAttachment(IncomingMailAttachment $attachment) + public function addAttachment(IncomingMailAttachment $attachment): void { if (!\is_string($attachment->id)) { throw new InvalidArgumentException('Argument 1 passed to '.__METHOD__.'() does not have an id specified!'); @@ -112,10 +114,8 @@ public function addAttachment(IncomingMailAttachment $attachment) * Sets property $hasAttachments. * * @param bool $hasAttachments True, if IncomingMail[] has one or more attachments - * - * @return void */ - public function setHasAttachments($hasAttachments) + public function setHasAttachments(bool $hasAttachments): void { $this->hasAttachments = $hasAttachments; } @@ -125,7 +125,7 @@ public function setHasAttachments($hasAttachments) * * @return bool true or false */ - public function hasAttachments() + public function hasAttachments(): bool { return $this->hasAttachments; } @@ -133,17 +133,15 @@ public function hasAttachments() /** * @return IncomingMailAttachment[] */ - public function getAttachments() + public function getAttachments(): array { return $this->attachments; } /** * @param string $id The attachment id - * - * @return bool */ - public function removeAttachment($id) + public function removeAttachment(string $id): bool { if (!isset($this->attachments[$id])) { return false; @@ -163,7 +161,7 @@ public function removeAttachment($id) * * @psalm-return array */ - public function getInternalLinksPlaceholders() + public function getInternalLinksPlaceholders(): array { $match = \preg_match_all('/=["\'](ci?d:([\w\.%*@-]+))["\']/i', $this->textHtml, $matches); @@ -173,12 +171,7 @@ public function getInternalLinksPlaceholders() return $match ? \array_combine($matches[2], $matches[1]) : []; } - /** - * @param string $baseUri - * - * @return string - */ - public function replaceInternalLinks($baseUri) + public function replaceInternalLinks(string $baseUri): string { $baseUri = \rtrim($baseUri, '\\/').'/'; $fetchedHtml = $this->textHtml; @@ -203,10 +196,8 @@ public function replaceInternalLinks($baseUri) /** * Embed inline image attachments as base64 to allow for * email html to display inline images automatically. - * - * @return void */ - public function embedImageAttachments() + public function embedImageAttachments(): void { \preg_match_all("/\bcid:[^'\"\s]{1,256}/mi", $this->textHtml, $matches); diff --git a/src/PhpImap/IncomingMailAttachment.php b/src/PhpImap/IncomingMailAttachment.php index 671f83b5..9943c69c 100644 --- a/src/PhpImap/IncomingMailAttachment.php +++ b/src/PhpImap/IncomingMailAttachment.php @@ -1,5 +1,7 @@ file_path = $filePath; } @@ -86,20 +84,16 @@ public function setFilePath($filePath) * Sets the data part info. * * @param DataPartInfo $dataInfo Date info (file content) - * - * @return void */ - public function addDataPartInfo(DataPartInfo $dataInfo) + public function addDataPartInfo(DataPartInfo $dataInfo): void { $this->dataInfo = $dataInfo; } /** * Gets the MIME type. - * - * @return string */ - public function getMimeType() + public function getMimeType(): string { if (!$this->mimeType) { $finfo = new finfo(FILEINFO_MIME); @@ -112,10 +106,8 @@ public function getMimeType() /** * Gets the file content. - * - * @return string */ - public function getContents() + public function getContents(): string { if (null === $this->dataInfo) { throw new UnexpectedValueException(static::class.'::$dataInfo has not been set by calling '.self::class.'::addDataPartInfo()'); @@ -129,7 +121,7 @@ public function getContents() * * @return bool True, if it could save the attachment on the disk */ - public function saveToDisk() + public function saveToDisk(): bool { if (null === $this->dataInfo) { return false; diff --git a/src/PhpImap/IncomingMailHeader.php b/src/PhpImap/IncomingMailHeader.php index ce72b04d..fc0abbd2 100644 --- a/src/PhpImap/IncomingMailHeader.php +++ b/src/PhpImap/IncomingMailHeader.php @@ -1,5 +1,7 @@ imapPath = \trim($imapPath); $this->imapLogin = \trim($login); @@ -131,51 +129,14 @@ public function __destruct() $this->disconnect(); } - /** - * Sets / Changes the OAuth Token for the authentication. - * - * @param string $access_token OAuth token from your application (eg. Google Mail) - * - * @return void - * - * @throws InvalidArgumentException If no access token is provided - * @throws Exception If OAuth authentication was unsuccessful - */ - public function setOAuthToken($access_token) - { - if (empty(\trim($access_token))) { - throw new InvalidParameterException('setOAuthToken() requires an access token as parameter!'); - } - - $this->imapOAuthAccessToken = \trim($access_token); - - try { - $this->_oauthAuthentication(); - } catch (Exception $ex) { - throw new Exception('Invalid OAuth token provided. Error: '.$ex->getMessage()); - } - } - - /** - * Gets the OAuth Token for the authentication. - * - * @return string|null $access_token OAuth Access Token - */ - public function getOAuthToken() - { - return $this->imapOAuthAccessToken; - } - /** * Sets / Changes the path delimiter character (Supported values: '.', '/'). * * @param string $delimiter Path delimiter * - * @return void - * * @throws InvalidParameterException */ - public function setPathDelimiter($delimiter) + public function setPathDelimiter(string $delimiter): void { if (!$this->validatePathDelimiter($delimiter)) { throw new InvalidParameterException('setPathDelimiter() can only set the delimiter to these characters: ".", "/"'); @@ -189,7 +150,7 @@ public function setPathDelimiter($delimiter) * * @return string Path delimiter */ - public function getPathDelimiter() + public function getPathDelimiter(): string { return $this->pathDelimiter; } @@ -201,7 +162,7 @@ public function getPathDelimiter() * * @return bool true (supported) or false (unsupported) */ - public function validatePathDelimiter($delimiter) + public function validatePathDelimiter(string $delimiter): bool { $supported_delimiters = ['.', '/']; @@ -217,7 +178,7 @@ public function validatePathDelimiter($delimiter) * * @return string Server encoding (eg. 'UTF-8') */ - public function getServerEncoding() + public function getServerEncoding(): string { return $this->serverEncoding; } @@ -227,11 +188,9 @@ public function getServerEncoding() * * @param string $serverEncoding Server encoding (eg. 'UTF-8') * - * @return void - * * @throws InvalidParameterException */ - public function setServerEncoding($serverEncoding) + public function setServerEncoding(string $serverEncoding): void { $serverEncoding = \strtoupper(\trim($serverEncoding)); @@ -249,7 +208,7 @@ public function setServerEncoding($serverEncoding) * * @return int IMAP search option (eg. 'SE_UID') */ - public function getImapSearchOption() + public function getImapSearchOption(): int { return $this->imapSearchOption; } @@ -261,11 +220,9 @@ public function getImapSearchOption() * * @psalm-param 1|2 $imapSearchOption * - * @return void - * * @throws InvalidParameterException */ - public function setImapSearchOption($imapSearchOption) + public function setImapSearchOption(int $imapSearchOption): void { $supported_options = [SE_FREE, SE_UID]; @@ -278,21 +235,9 @@ public function setImapSearchOption($imapSearchOption) /** * Set $this->attachmentsIgnore param. Allow to ignore attachments when they are not required and boost performance. - * - * @param scalar $attachmentsIgnore - * - * @return void - * - * @throws InvalidParameterException - * - * @todo drop support for php 5.6, set $attachmentsIgnore to literal bool - * @todo drop support for php 5.6, remove thrown exception */ - public function setAttachmentsIgnore($attachmentsIgnore) + public function setAttachmentsIgnore(bool $attachmentsIgnore): void { - if (!\is_bool($attachmentsIgnore)) { - throw new InvalidParameterException('setAttachmentsIgnore() expects a boolean: true or false'); - } $this->attachmentsIgnore = $attachmentsIgnore; } @@ -301,7 +246,7 @@ public function setAttachmentsIgnore($attachmentsIgnore) * * @return bool $attachmentsIgnore */ - public function getAttachmentsIgnore() + public function getAttachmentsIgnore(): bool { return $this->attachmentsIgnore; } @@ -314,11 +259,9 @@ public function getAttachmentsIgnore() * * @psalm-param list<1|2|3|4> $types * - * @return void - * * @throws InvalidParameterException */ - public function setTimeouts($timeout, array $types = [IMAP_OPENTIMEOUT, IMAP_READTIMEOUT, IMAP_WRITETIMEOUT, IMAP_CLOSETIMEOUT]) + public function setTimeouts(int $timeout, array $types = [IMAP_OPENTIMEOUT, IMAP_READTIMEOUT, IMAP_WRITETIMEOUT, IMAP_CLOSETIMEOUT]): void { $supported_types = [IMAP_OPENTIMEOUT, IMAP_READTIMEOUT, IMAP_WRITETIMEOUT, IMAP_CLOSETIMEOUT]; @@ -337,7 +280,7 @@ public function setTimeouts($timeout, array $types = [IMAP_OPENTIMEOUT, IMAP_REA * * @return string IMAP login */ - public function getLogin() + public function getLogin(): string { return $this->imapLogin; } @@ -345,19 +288,13 @@ public function getLogin() /** * Set custom connection arguments of imap_open method. See http://php.net/imap_open. * - * @param int $options - * @param int $retriesNum * @param string[]|null $params * * @psalm-param array{DISABLE_AUTHENTICATOR?:string}|array|null $params * - * @return void - * * @throws InvalidParameterException - * - * @todo drop support for php 5.6, set $options and $retriesNum to int */ - public function setConnectionArgs($options = 0, $retriesNum = 0, array $params = null) + public function setConnectionArgs(int $options = 0, int $retriesNum = 0, array $params = null): void { if (0 !== $options) { $supported_options = [OP_READONLY, OP_ANONYMOUS, OP_HALFOPEN, CL_EXPUNGE, OP_DEBUG, OP_SHORTCACHE, OP_SILENT, OP_PROTOTYPE, OP_SECURE]; @@ -368,7 +305,7 @@ public function setConnectionArgs($options = 0, $retriesNum = 0, array $params = } if (0 != $retriesNum) { - if (!\is_int($retriesNum) or $retriesNum < 0) { + if ($retriesNum < 0) { throw new InvalidParameterException('Invalid number of retries provided for setConnectionArgs()! It must be a positive integer. (eg. 1 or 3)'); } $this->imapRetriesNum = $retriesNum; @@ -393,11 +330,9 @@ public function setConnectionArgs($options = 0, $retriesNum = 0, array $params = * * @param string $attachmentsDir Folder where to save attachments * - * @return void - * * @throws InvalidParameterException */ - public function setAttachmentsDir($attachmentsDir) + public function setAttachmentsDir(string $attachmentsDir): void { if (empty(\trim($attachmentsDir))) { throw new InvalidParameterException('setAttachmentsDir() expects a string as first parameter!'); @@ -413,31 +348,23 @@ public function setAttachmentsDir($attachmentsDir) * * @return string|null Attachments dir */ - public function getAttachmentsDir() + public function getAttachmentsDir(): ?string { return $this->attachmentsDir; } /** * Sets / Changes the attempts / retries to connect. - * - * @param int $maxAttempts - * - * @return void */ - public function setConnectionRetry($maxAttempts) + public function setConnectionRetry(int $maxAttempts): void { $this->connectionRetry = $maxAttempts; } /** * Sets / Changes the delay between each attempt / retry to connect. - * - * @param int $milliseconds - * - * @return void */ - public function setConnectionRetryDelay($milliseconds) + public function setConnectionRetryDelay(int $milliseconds): void { $this->connectionRetryDelay = $milliseconds; } @@ -449,7 +376,7 @@ public function setConnectionRetryDelay($milliseconds) * * @return resource */ - public function getImapStream($forceConnection = true) + public function getImapStream(bool $forceConnection = true) { if ($forceConnection) { $this->pingOrDisconnect(); @@ -462,8 +389,7 @@ public function getImapStream($forceConnection = true) return $this->imapStream; } - /** @return bool */ - public function hasImapStream() + public function hasImapStream(): bool { return \is_resource($this->imapStream) && \imap_ping($this->imapStream); } @@ -471,60 +397,41 @@ public function hasImapStream() /** * Returns the provided string in UTF7-IMAP encoded format. * - * @param scalar|array|object|resource|null $str - * * @return string $str UTF-7 encoded string */ - public function encodeStringToUtf7Imap($str) + public function encodeStringToUtf7Imap(string $str): string { - if (\is_string($str)) { - $out = \mb_convert_encoding($str, 'UTF7-IMAP', \mb_detect_encoding($str, 'UTF-8, ISO-8859-1, ISO-8859-15', true)); - - if (!\is_string($out)) { - throw new UnexpectedValueException('mb_convert_encoding($str, \'UTF-8\', {detected}) could not convert $str'); - } + $out = \mb_convert_encoding($str, 'UTF7-IMAP', \mb_detect_encoding($str, 'UTF-8, ISO-8859-1, ISO-8859-15', true)); - return $out; + if (!\is_string($out)) { + throw new UnexpectedValueException('mb_convert_encoding($str, \'UTF-8\', {detected}) could not convert $str'); } - throw new InvalidArgumentException('Argument 1 passed to '.__METHOD__.'() must be a string!'); + return $out; } /** * Returns the provided string in UTF-8 encoded format. * - * @param scalar|array|object|resource|null $str - * * @return string $str UTF-7 encoded string or same as before, when it's no string - * - * @todo revisit return issues pending fix of https://github.com/vimeo/psalm/issues/2625 */ - public function decodeStringFromUtf7ImapToUtf8($str) + public function decodeStringFromUtf7ImapToUtf8(string $str): string { - if (\is_string($str)) { - $out = \mb_convert_encoding($str, 'UTF-8', 'UTF7-IMAP'); - - if (!\is_string($out)) { - throw new UnexpectedValueException('mb_convert_encoding($str, \'UTF-8\', \'UTF7-IMAP\') could not convert $str'); - } + $out = \mb_convert_encoding($str, 'UTF-8', 'UTF7-IMAP'); - return $out; + if (!\is_string($out)) { + throw new UnexpectedValueException('mb_convert_encoding($str, \'UTF-8\', \'UTF7-IMAP\') could not convert $str'); } - throw new InvalidArgumentException('Argument 1 passed to '.__METHOD__.'() must be a string!'); + return $out; } /** * Switch mailbox without opening a new connection. * - * @param string $imapPath - * @param bool $absolute - * - * @return void - * * @throws Exception */ - public function switchMailbox($imapPath, $absolute = true) + public function switchMailbox(string $imapPath, bool $absolute = true): void { if (\strpos($imapPath, '}') > 0) { $this->imapPath = $imapPath; @@ -537,10 +444,8 @@ public function switchMailbox($imapPath, $absolute = true) /** * Disconnects from IMAP server / mailbox. - * - * @return void */ - public function disconnect() + public function disconnect(): void { if ($this->hasImapStream()) { Imap::close($this->getImapStream(false), $this->expungeOnDisconnect ? CL_EXPUNGE : 0); @@ -549,12 +454,8 @@ public function disconnect() /** * Sets 'expunge on disconnect' parameter. - * - * @param bool $isEnabled - * - * @return void */ - public function setExpungeOnDisconnect($isEnabled) + public function setExpungeOnDisconnect(bool $isEnabled): void { $this->expungeOnDisconnect = $isEnabled; } @@ -569,11 +470,9 @@ public function setExpungeOnDisconnect($isEnabled) * Nmsgs - number of mails in the mailbox * Recent - number of recent mails in the mailbox * - * @see imap_check - * - * @return object + * @see imap_check */ - public function checkMailbox() + public function checkMailbox(): object { return Imap::check($this->getImapStream()); } @@ -583,11 +482,9 @@ public function checkMailbox() * * @param string $name Name of new mailbox (eg. 'PhpImap') * - * @return void - * - * @see imap_createmailbox() + * @see imap_createmailbox() */ - public function createMailbox($name) + public function createMailbox(string $name): void { Imap::createmailbox($this->getImapStream(), $this->getCombinedPath($name)); } @@ -595,14 +492,11 @@ public function createMailbox($name) /** * Deletes a specific mailbox. * - * @param string $name Name of mailbox, which you want to delete (eg. 'PhpImap') - * @param bool $absolute - * - * @return bool + * @param string $name Name of mailbox, which you want to delete (eg. 'PhpImap') * - * @see imap_deletemailbox() + * @see imap_deletemailbox() */ - public function deleteMailbox($name, $absolute = false) + public function deleteMailbox(string $name, bool $absolute = false): bool { return Imap::deletemailbox($this->getImapStream(), $this->getCombinedPath($name, $absolute)); } @@ -612,10 +506,8 @@ public function deleteMailbox($name, $absolute = false) * * @param string $oldName Current name of mailbox, which you want to rename (eg. 'PhpImap') * @param string $newName New name of mailbox, to which you want to rename it (eg. 'PhpImapTests') - * - * @return void */ - public function renameMailbox($oldName, $newName) + public function renameMailbox(string $oldName, string $newName): void { Imap::renamemailbox($this->getImapStream(), $this->getCombinedPath($oldName), $this->getCombinedPath($newName)); } @@ -625,10 +517,8 @@ public function renameMailbox($oldName, $newName) * * This function returns an object containing status information. * The object has the following properties: messages, recent, unseen, uidnext, and uidvalidity. - * - * @return object */ - public function statusMailbox() + public function statusMailbox(): object { return Imap::status($this->getImapStream(), $this->imapPath, SA_ALL); } @@ -639,11 +529,9 @@ public function statusMailbox() * This function returns an object containing listing the folders. * The object has the following properties: messages, recent, unseen, uidnext, and uidvalidity. * - * @param string $pattern - * * @return array listing the folders */ - public function getListingFolders($pattern = '*') + public function getListingFolders(string $pattern = '*'): array { return Imap::listOfMailboxes($this->getImapStream(), $this->imapPath, $pattern); } @@ -659,7 +547,7 @@ public function getListingFolders($pattern = '*') * * @psalm-return list */ - public function searchMailbox($criteria = 'ALL', $disableServerEncoding = false) + public function searchMailbox(string $criteria = 'ALL', bool $disableServerEncoding = false): array { if ($disableServerEncoding) { /** @psalm-var list */ @@ -673,17 +561,13 @@ public function searchMailbox($criteria = 'ALL', $disableServerEncoding = false) /** * Search the mailbox for emails from multiple, specific senders. * - * @param string $criteria - * @param string $sender - * @param string ...$senders - * * @see Mailbox::searchMailboxFromWithOrWithoutDisablingServerEncoding() * * @return int[] * * @psalm-return list */ - public function searchMailboxFrom($criteria, $sender, ...$senders) + public function searchMailboxFrom(string $criteria, string $sender, string ...$senders): array { return $this->searchMailboxFromWithOrWithoutDisablingServerEncoding($criteria, false, $sender, ...$senders); } @@ -691,17 +575,13 @@ public function searchMailboxFrom($criteria, $sender, ...$senders) /** * Search the mailbox for emails from multiple, specific senders whilst not using server encoding. * - * @param string $criteria - * @param string $sender - * @param string ...$senders - * * @see Mailbox::searchMailboxFromWithOrWithoutDisablingServerEncoding() * * @return int[] * * @psalm-return list */ - public function searchMailboxFromDisableServerEncoding($criteria, $sender, ...$senders) + public function searchMailboxFromDisableServerEncoding(string $criteria, string $sender, string ...$senders): array { return $this->searchMailboxFromWithOrWithoutDisablingServerEncoding($criteria, true, $sender, ...$senders); } @@ -739,14 +619,11 @@ public function searchMailboxMergeResultsDisableServerEncoding($single_criteria, /** * Save a specific body section to a file. * - * @param int $mailId message number - * @param string $filename - * - * @return void + * @param int $mailId message number * - * @see imap_savebody() + * @see imap_savebody() */ - public function saveMail($mailId, $filename = 'email.eml') + public function saveMail(int $mailId, string $filename = 'email.eml'): void { Imap::savebody($this->getImapStream(), $filename, $mailId, '', (SE_UID === $this->imapSearchOption) ? FT_UID : 0); } @@ -756,11 +633,9 @@ public function saveMail($mailId, $filename = 'email.eml') * * @param int $mailId message number * - * @return void - * - * @see imap_delete() + * @see imap_delete() */ - public function deleteMail($mailId) + public function deleteMail(int $mailId): void { Imap::delete($this->getImapStream(), $mailId, (SE_UID === $this->imapSearchOption) ? FT_UID : 0); } @@ -772,10 +647,8 @@ public function deleteMail($mailId) * @param string $mailBox Mailbox name * * @see imap_mail_move() - * - * @return void */ - public function moveMail($mailId, $mailBox) + public function moveMail($mailId, string $mailBox): void { Imap::mail_move($this->getImapStream(), $mailId, $mailBox, CP_UID); $this->expungeDeletedMails(); @@ -787,11 +660,9 @@ public function moveMail($mailId, $mailBox) * @param string|int $mailId a range or message number * @param string $mailBox Mailbox name * - * @return void - * - * @see imap_mail_copy() + * @see imap_mail_copy() */ - public function copyMail($mailId, $mailBox) + public function copyMail($mailId, string $mailBox): void { Imap::mail_copy($this->getImapStream(), $mailId, $mailBox, CP_UID); $this->expungeDeletedMails(); @@ -800,47 +671,33 @@ public function copyMail($mailId, $mailBox) /** * Deletes all the mails marked for deletion by imap_delete(), imap_mail_move(), or imap_setflag_full(). * - * @return void - * * @see imap_expunge() */ - public function expungeDeletedMails() + public function expungeDeletedMails(): void { Imap::expunge($this->getImapStream()); } /** * Add the flag \Seen to a mail. - * - * @param int $mailId - * - * @return void */ - public function markMailAsRead($mailId) + public function markMailAsRead(int $mailId): void { $this->setFlag([$mailId], '\\Seen'); } /** * Remove the flag \Seen from a mail. - * - * @param int $mailId - * - * @return void */ - public function markMailAsUnread($mailId) + public function markMailAsUnread(int $mailId): void { $this->clearFlag([$mailId], '\\Seen'); } /** * Add the flag \Flagged to a mail. - * - * @param int $mailId - * - * @return void */ - public function markMailAsImportant($mailId) + public function markMailAsImportant(int $mailId): void { $this->setFlag([$mailId], '\\Flagged'); } @@ -851,10 +708,8 @@ public function markMailAsImportant($mailId) * @param int[] $mailId * * @psalm-param list $mailId - * - * @return void */ - public function markMailsAsRead(array $mailId) + public function markMailsAsRead(array $mailId): void { $this->setFlag($mailId, '\\Seen'); } @@ -865,10 +720,8 @@ public function markMailsAsRead(array $mailId) * @param int[] $mailId * * @psalm-param list $mailId - * - * @return void */ - public function markMailsAsUnread(array $mailId) + public function markMailsAsUnread(array $mailId): void { $this->clearFlag($mailId, '\\Seen'); } @@ -879,10 +732,8 @@ public function markMailsAsUnread(array $mailId) * @param int[] $mailId * * @psalm-param list $mailId - * - * @return void */ - public function markMailsAsImportant(array $mailId) + public function markMailsAsImportant(array $mailId): void { $this->setFlag($mailId, '\\Flagged'); } @@ -893,9 +744,9 @@ public function markMailsAsImportant(array $mailId) * @param array $mailsIds Array of mail IDs * @param string $flag Which you can set are \Seen, \Answered, \Flagged, \Deleted, and \Draft as defined by RFC2060 * - * @return void + * @psalm-param list $mailsIds */ - public function setFlag(array $mailsIds, $flag) + public function setFlag(array $mailsIds, string $flag): void { Imap::setflag_full($this->getImapStream(), \implode(',', $mailsIds), $flag, ST_UID); } @@ -905,10 +756,8 @@ public function setFlag(array $mailsIds, $flag) * * @param array $mailsIds Array of mail IDs * @param string $flag Which you can delete are \Seen, \Answered, \Flagged, \Deleted, and \Draft as defined by RFC2060 - * - * @return void */ - public function clearFlag(array $mailsIds, $flag) + public function clearFlag(array $mailsIds, string $flag): void { Imap::clearflag_full($this->getImapStream(), \implode(',', $mailsIds), $flag, ST_UID); } @@ -941,7 +790,7 @@ public function clearFlag(array $mailsIds, $flag) * * @todo adjust types & conditionals pending resolution of https://github.com/vimeo/psalm/issues/2619 */ - public function getMailsInfo(array $mailsIds) + public function getMailsInfo(array $mailsIds): array { $mails = Imap::fetch_overview( $this->getImapStream(), @@ -964,16 +813,16 @@ public function getMailsInfo(array $mailsIds) } if (isset($mail->subject) and !empty(\trim($mail->subject))) { - $mail->subject = $this->decodeMimeStr($mail->subject, $this->getServerEncoding()); + $mail->subject = $this->decodeMimeStr($mail->subject); } if (isset($mail->from) and !empty(\trim($mail->from))) { - $mail->from = $this->decodeMimeStr($mail->from, $this->getServerEncoding()); + $mail->from = $this->decodeMimeStr($mail->from); } if (isset($mail->sender) and !empty(\trim($mail->sender))) { - $mail->sender = $this->decodeMimeStr($mail->sender, $this->getServerEncoding()); + $mail->sender = $this->decodeMimeStr($mail->sender); } if (isset($mail->to) and !empty(\trim($mail->to))) { - $mail->to = $this->decodeMimeStr($mail->to, $this->getServerEncoding()); + $mail->to = $this->decodeMimeStr($mail->to); } } } @@ -987,11 +836,9 @@ public function getMailsInfo(array $mailsIds) * returns an array of string formatted with header info, * one element per mail message. * - * @return array - * - * @see imap_headers() + * @see imap_headers() */ - public function getMailboxHeaders() + public function getMailboxHeaders(): array { return Imap::headers($this->getImapStream()); } @@ -1011,9 +858,9 @@ public function getMailboxHeaders() * * @return object Object with info * - * @see mailboxmsginfo + * @see mailboxmsginfo */ - public function getMailboxInfo() + public function getMailboxInfo(): object { return Imap::mailboxmsginfo($this->getImapStream()); } @@ -1033,7 +880,6 @@ public function getMailboxInfo() * @param int $criteria Sorting criteria (eg. SORTARRIVAL) * @param bool $reverse Sort reverse or not * @param string|null $searchCriteria See http://php.net/imap_search for a complete list of available criteria - * @param string|null $charset * * @psalm-param value-of $criteria * @psalm-param 1|5|0|2|6|3|4 $criteria @@ -1041,11 +887,11 @@ public function getMailboxInfo() * @return array Mails ids */ public function sortMails( - $criteria = SORTARRIVAL, - $reverse = true, - $searchCriteria = 'ALL', - $charset = null - ) { + int $criteria = SORTARRIVAL, + bool $reverse = true, + ? string $searchCriteria = 'ALL', + string $charset = null + ): array { return Imap::sort( $this->getImapStream(), $criteria, @@ -1059,11 +905,9 @@ public function sortMails( /** * Get mails count in mail box. * - * @return int - * - * @see imap_num_msg() + * @see imap_num_msg() */ - public function countMails() + public function countMails(): int { return Imap::num_msg($this->getImapStream()); } @@ -1072,10 +916,8 @@ public function countMails() * Return quota limit in KB. * * @param string $quota_root Should normally be in the form of which mailbox (i.e. INBOX) - * - * @return int */ - public function getQuotaLimit($quota_root = 'INBOX') + public function getQuotaLimit(string $quota_root = 'INBOX'): int { $quota = $this->getQuota($quota_root); @@ -1090,7 +932,7 @@ public function getQuotaLimit($quota_root = 'INBOX') * * @return int|false FALSE in the case of call failure */ - public function getQuotaUsage($quota_root = 'INBOX') + public function getQuotaUsage(string $quota_root = 'INBOX') { $quota = $this->getQuota($quota_root); @@ -1106,7 +948,7 @@ public function getQuotaUsage($quota_root = 'INBOX') * * @return string Message of the fetched body */ - public function getRawMail($msgId, $markAsSeen = true) + public function getRawMail(int $msgId, bool $markAsSeen = true): string { $options = (SE_UID == $this->imapSearchOption) ? FT_UID : 0; if (!$markAsSeen) { @@ -1121,13 +963,11 @@ public function getRawMail($msgId, $markAsSeen = true) * * @param int $mailId ID of the message * - * @return IncomingMailHeader - * * @throws Exception * * @todo update type checking pending resolution of https://github.com/vimeo/psalm/issues/2619 */ - public function getMailHeader($mailId) + public function getMailHeader(int $mailId): IncomingMailHeader { $headersRaw = Imap::fetchheader( $this->getImapStream(), @@ -1198,7 +1038,7 @@ public function getMailHeader($mailId) $header->date = self::parseDateTime($now->format('Y-m-d H:i:s')); } - $header->subject = (isset($head->subject) and !empty(\trim($head->subject))) ? $this->decodeMimeStr($head->subject, $this->getServerEncoding()) : null; + $header->subject = (isset($head->subject) and !empty(\trim($head->subject))) ? $this->decodeMimeStr($head->subject) : null; if (isset($head->from) and !empty($head->from)) { list($header->fromHost, $header->fromName, $header->fromAddress) = $this->possiblyGetHostNameAndAddress($head->from); } elseif (\preg_match('/smtp.mailfrom=[-0-9a-zA-Z.+_]+@[-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}/', $headersRaw, $matches)) { @@ -1262,9 +1102,6 @@ public function getMailHeader($mailId) * * @param stdClass[] $messageParts * @param stdClass[] $flattenedParts - * @param string $prefix - * @param int $index - * @param bool $fullPrefix * * @psalm-param array $flattenedParts * @@ -1272,7 +1109,7 @@ public function getMailHeader($mailId) * * @psalm-return array */ - public function flattenParts(array $messageParts, array $flattenedParts = [], $prefix = '', $index = 1, $fullPrefix = true) + public function flattenParts(array $messageParts, array $flattenedParts = [], string $prefix = '', int $index = 1, bool $fullPrefix = true): array { foreach ($messageParts as $part) { $flattenedParts[$prefix.$index] = $part; @@ -1280,7 +1117,7 @@ public function flattenParts(array $messageParts, array $flattenedParts = [], $p /** @var stdClass[] */ $part_parts = $part->parts; - if (2 == $part->type) { + if (self::PART_TYPE_TWO == $part->type) { /** @var array */ $flattenedParts = $this->flattenParts($part_parts, $flattenedParts, $prefix.$index.'.', 0, false); } elseif ($fullPrefix) { @@ -1304,10 +1141,8 @@ public function flattenParts(array $messageParts, array $flattenedParts = [], $p * * @param int $mailId ID of the mail * @param bool $markAsSeen Mark the email as seen, when set to true - * - * @return IncomingMail */ - public function getMail($mailId, $markAsSeen = true) + public function getMail(int $mailId, bool $markAsSeen = true): IncomingMail { $mail = new IncomingMail(); $mail->setHeader($this->getMailHeader($mailId)); @@ -1336,17 +1171,14 @@ public function getMail($mailId, $markAsSeen = true) * * @param array $params Array of params of mail * @param object $partStructure Part of mail - * @param int $_mailId ID of mail * @param bool $emlOrigin True, if it indicates, that the attachment comes from an EML (mail) file * * @psalm-param array $params * @psalm-param PARTSTRUCTURE $partStructure * * @return IncomingMailAttachment $attachment - * - * @todo consider "requiring" psalm (suggest + conflict) then setting $params to array */ - public function downloadAttachment(DataPartInfo $dataInfo, array $params, $partStructure, $_mailId, $emlOrigin = false) + public function downloadAttachment(DataPartInfo $dataInfo, array $params, object $partStructure, bool $emlOrigin = false): IncomingMailAttachment { if ('RFC822' == $partStructure->subtype && isset($partStructure->disposition) && 'attachment' == $partStructure->disposition) { $fileName = \strtolower($partStructure->subtype).'.eml'; @@ -1356,8 +1188,8 @@ public function downloadAttachment(DataPartInfo $dataInfo, array $params, $partS $fileName = \strtolower($partStructure->subtype); } else { $fileName = (isset($params['filename']) and !empty(\trim($params['filename']))) ? $params['filename'] : $params['name']; - $fileName = $this->decodeMimeStr($fileName, $this->getServerEncoding()); - $fileName = $this->decodeRFC2231($fileName, $this->getServerEncoding()); + $fileName = $this->decodeMimeStr($fileName); + $fileName = $this->decodeRFC2231($fileName); } $partStructure_id = ($partStructure->ifid && isset($partStructure->id)) ? $partStructure->id : null; @@ -1385,9 +1217,9 @@ public function downloadAttachment(DataPartInfo $dataInfo, array $params, $partS $fileSysName = \bin2hex(\random_bytes(16)).'.bin'; $filePath = $attachmentsDir.DIRECTORY_SEPARATOR.$fileSysName; - if (\strlen($filePath) > 255) { + if (\strlen($filePath) > self::MAX_LENGTH_FILEPATH) { $ext = \pathinfo($filePath, PATHINFO_EXTENSION); - $filePath = \substr($filePath, 0, 255 - 1 - \strlen($ext)).'.'.$ext; + $filePath = \substr($filePath, 0, self::MAX_LENGTH_FILEPATH - 1 - \strlen($ext)).'.'.$ext; } $attachment->setFilePath($filePath); $attachment->saveToDisk(); @@ -1399,8 +1231,7 @@ public function downloadAttachment(DataPartInfo $dataInfo, array $params, $partS /** * Decodes a mime string. * - * @param string $string MIME string to decode - * @param string $toCharset + * @param string $string MIME string to decode * * @return string Converted string if conversion was successful, or the original string if not * @@ -1408,7 +1239,7 @@ public function downloadAttachment(DataPartInfo $dataInfo, array $params, $partS * * @todo update implementation pending resolution of https://github.com/vimeo/psalm/issues/2619 & https://github.com/vimeo/psalm/issues/2620 */ - public function decodeMimeStr($string, $toCharset = 'utf-8') + public function decodeMimeStr(string $string): string { if (empty(\trim($string))) { throw new Exception('decodeMimeStr() Can not decode an empty string!'); @@ -1419,27 +1250,38 @@ public function decodeMimeStr($string, $toCharset = 'utf-8') $elements = \imap_mime_header_decode($string); if (false === $elements) { - return $newString; + return $string; } foreach ($elements as $element) { - if (isset($element->text)) { - $fromCharset = !isset($element->charset) ? 'iso-8859-1' : $element->charset; - // Convert to UTF-8, if string has UTF-8 characters to avoid broken strings. See https://github.com/barbushin/php-imap/issues/232 - $toCharset = isset($element->charset) && \preg_match('/(UTF\-8)|(default)/i', $element->charset) ? 'UTF-8' : $toCharset; - $newString .= $this->convertStringEncoding($element->text, $fromCharset, $toCharset); + switch (\strtolower($element->charset)) { + case 'default': // Charset default is already ASCII (not encoded) + case 'utf-8': // Charset UTF-8 is OK + $newString .= $element->text; + break; + default: + // If charset exists in mb_list_encodings(), convert using mb_convert function + if (\in_array(\strtolower($element->charset), $this->lowercase_mb_list_encodings())) { + $newString .= \mb_convert_encoding($element->text, 'UTF-8', $element->charset); + } else { + // Fallback: Try to convert with iconv() + $iconv_converted_string = @\iconv($element->charset, 'UTF-8', $element->text); + if (!$iconv_converted_string) { + // If iconv() could also not convert, return string as it is + // (unknown charset) + $newString .= $element->text; + } else { + $newString .= $iconv_converted_string; + } + } + break; } } return $newString; } - /** - * @param string $string - * - * @return bool - */ - public function isUrlEncoded($string) + public function isUrlEncoded(string $string): bool { $hasInvalidChars = \preg_match('#[^%a-zA-Z0-9\-_\.\+]#', $string); $hasEscapedChars = \preg_match('#%[a-zA-Z0-9]{2}#', $string); @@ -1455,7 +1297,7 @@ public function isUrlEncoded($string) * @return string RFC 3339 compliant format or original (unchanged) format, * if conversation is not possible */ - public function parseDateTime($dateHeader) + public function parseDateTime(string $dateHeader): string { if (empty(\trim($dateHeader))) { throw new InvalidParameterException('parseDateTime() expects parameter 1 to be a parsable string datetime'); @@ -1476,39 +1318,10 @@ public function parseDateTime($dateHeader) return $dateHeaderRfc3339; } - /** - * Converts a string from one encoding to another. - * - * @param string $string the string, which you want to convert - * @param string $fromEncoding the current charset (encoding) - * @param string $toEncoding the new charset (encoding) - * - * @return string Converted string if conversion was successful, or the original string if not - */ - public function convertStringEncoding($string, $fromEncoding, $toEncoding) - { - if (\preg_match('/default|ascii/i', $fromEncoding) || !$string || $fromEncoding == $toEncoding) { - return $string; - } - $supportedEncodings = \array_map('strtolower', \mb_list_encodings()); - if (\in_array(\strtolower($fromEncoding), $supportedEncodings) && \in_array(\strtolower($toEncoding), $supportedEncodings)) { - $convertedString = \mb_convert_encoding($string, $toEncoding, $fromEncoding); - } else { - $convertedString = @\iconv($fromEncoding, $toEncoding.'//TRANSLIT//IGNORE', $string); - } - if (('' == $convertedString) or (false === $convertedString)) { - return $string; - } - - return $convertedString; - } - /** * Gets IMAP path. - * - * @return string */ - public function getImapPath() + public function getImapPath(): string { return $this->imapPath; } @@ -1517,24 +1330,18 @@ public function getImapPath() * Get message in MBOX format. * * @param int $mailId message number - * - * @return string */ - public function getMailMboxFormat($mailId) + public function getMailMboxFormat(int $mailId): string { $option = (SE_UID == $this->imapSearchOption) ? FT_UID : 0; - return \imap_fetchheader($this->getImapStream(), $mailId, $option | FT_PREFETCHTEXT).Imap::body($this->getImapStream(), $mailId, $option); + return Imap::fetchheader($this->getImapStream(), $mailId, $option | FT_PREFETCHTEXT).Imap::body($this->getImapStream(), $mailId, $option); } /** * Get folders list. - * - * @param string $search - * - * @return array */ - public function getMailboxes($search = '*') + public function getMailboxes(string $search = '*'): array { /** @psalm-var array */ $mailboxes = Imap::getmailboxes($this->getImapStream(), $this->imapPath, $search); @@ -1544,12 +1351,8 @@ public function getMailboxes($search = '*') /** * Get folders list. - * - * @param string $search - * - * @return array */ - public function getSubscribedMailboxes($search = '*') + public function getSubscribedMailboxes(string $search = '*'): array { /** @psalm-var array */ $mailboxes = Imap::getsubscribed($this->getImapStream(), $this->imapPath, $search); @@ -1560,13 +1363,9 @@ public function getSubscribedMailboxes($search = '*') /** * Subscribe to a mailbox. * - * @param string $mailbox - * - * @return void - * * @throws Exception */ - public function subscribeMailbox($mailbox) + public function subscribeMailbox(string $mailbox): void { Imap::subscribe( $this->getImapStream(), @@ -1577,13 +1376,9 @@ public function subscribeMailbox($mailbox) /** * Unsubscribe from a mailbox. * - * @param string $mailbox - * - * @return void - * * @throws Exception */ - public function unsubscribeMailbox($mailbox) + public function unsubscribeMailbox(string $mailbox): void { Imap::unsubscribe( $this->getImapStream(), @@ -1595,9 +1390,6 @@ public function unsubscribeMailbox($mailbox) * Appends $message to $mailbox. * * @param string|array $message - * @param string $mailbox - * @param string|null $options - * @param string|null $internal_date * * @psalm-param string|array{0:COMPOSE_ENVELOPE, 1:COMPOSE_BODY} $message * @@ -1607,13 +1399,13 @@ public function unsubscribeMailbox($mailbox) */ public function appendMessageToMailbox( $message, - $mailbox = '', - $options = null, - $internal_date = null - ) { + string $mailbox = '', + string $options = null, + string $internal_date = null + ): bool { if ( \is_array($message) && - 2 === \count($message) && + self::EXPECTED_SIZE_OF_MESSAGE_AS_ARRAY === \count($message) && isset($message[0], $message[1]) ) { $message = Imap::mail_compose($message[0], $message[1]); @@ -1633,37 +1425,19 @@ public function appendMessageToMailbox( } /** - * Builds an OAuth2 authentication string for the given email address and access token. - * - * @return string $access_token Formatted OAuth access token - */ - protected function _constructAuthString() - { - return \base64_encode("user=$this->imapLogin\1auth=Bearer $this->imapOAuthAccessToken\1\1"); - } - - /** - * Authenticates the IMAP client with the OAuth access token. + * Returns the list of available encodings in lower case. * - * @return void - * - * @throws Exception If any error occured + * @return array mb_list_encodings() in lower case */ - protected function _oauthAuthentication() + protected function lowercase_mb_list_encodings() { - $oauth_command = 'A AUTHENTICATE XOAUTH2 '.$this->_constructAuthString(); - - $oauth_result = \fwrite($this->getImapStream(), $oauth_command); - - if (false === $oauth_result) { - throw new Exception('Could not authenticate using OAuth!'); + $lowercase_encodings = []; + $encodings = \mb_list_encodings(); + foreach ($encodings as $encoding) { + $lowercase_encodings[] = \strtolower($encoding); } - try { - $this->checkMailbox(); - } catch (Throwable $ex) { - throw new Exception('OAuth authentication failed! IMAP Error: '.$ex->getMessage()); - } + return $lowercase_encodings; } /** @return resource */ @@ -1686,11 +1460,9 @@ protected function initImapStreamWithRetry() * * @param string $quota_root Should normally be in the form of which mailbox (i.e. INBOX) * - * @see imap_get_quotaroot() - * - * @return array[] + * @see imap_get_quotaroot() */ - protected function getQuota($quota_root = 'INBOX') + protected function getQuota(string $quota_root = 'INBOX'): array { return Imap::get_quotaroot($this->getImapStream(), $quota_root); } @@ -1721,18 +1493,13 @@ protected function initImapStream() } /** - * @param object $partStructure * @param string|0 $partNum - * @param bool $markAsSeen - * @param bool $emlParse * * @psalm-param PARTSTRUCTURE $partStructure * - * @return void - * * @todo refactor type checking pending resolution of https://github.com/vimeo/psalm/issues/2619 */ - protected function initMailPart(IncomingMail $mail, $partStructure, $partNum, $markAsSeen = true, $emlParse = false) + protected function initMailPart(IncomingMail $mail, object $partStructure, $partNum, bool $markAsSeen = true, bool $emlParse = false): void { if (!isset($mail->id)) { throw new InvalidArgumentException('Argument 1 passeed to '.__METHOD__.'() did not have the id property set!'); @@ -1782,7 +1549,7 @@ protected function initMailPart(IncomingMail $mail, $partStructure, $partNum, $m if ('RFC822' === $partStructure->subtype && isset($partStructure->disposition) && 'attachment' === $partStructure->disposition) { // Although we are downloading each part separately, we are going to download the EML to a single file //incase someone wants to process or parse in another process - $attachment = self::downloadAttachment($dataInfo, $params, $partStructure, $mail->id, false); + $attachment = self::downloadAttachment($dataInfo, $params, $partStructure, false); $mail->addAttachment($attachment); } @@ -1800,7 +1567,7 @@ protected function initMailPart(IncomingMail $mail, $partStructure, $partNum, $m } if ($isAttachment) { - $attachment = self::downloadAttachment($dataInfo, $params, $partStructure, $mail->id, $emlParse); + $attachment = self::downloadAttachment($dataInfo, $params, $partStructure, $emlParse); $mail->addAttachment($attachment); } else { if (isset($params['charset']) && !empty(\trim($params['charset']))) { @@ -1841,19 +1608,12 @@ protected function initMailPart(IncomingMail $mail, $partStructure, $partNum, $m } } - /** - * @param string $string - * @param string $charset - * - * @return string - */ - protected function decodeRFC2231($string, $charset = 'utf-8') + protected function decodeRFC2231(string $string): string { if (\preg_match("/^(.*?)'.*?'(.*?)$/", $string, $matches)) { - $encoding = $matches[1]; $data = $matches[2]; if ($this->isUrlEncoded($data)) { - $string = $this->convertStringEncoding(\urldecode($data), $encoding, $charset); + $string = $this->decodeMimeStr(\urldecode($data)); } } @@ -1870,7 +1630,7 @@ protected function decodeRFC2231($string, $charset = 'utf-8') * * @return string Return the new path */ - protected function getCombinedPath($folder, $absolute = false) + protected function getCombinedPath(string $folder, bool $absolute = false): string { if (empty(\trim($folder))) { return $this->imapPath; @@ -1891,13 +1651,9 @@ protected function getCombinedPath($folder, $absolute = false) } /** - * @param object $recipient - * - * @return array|null - * * @psalm-return array{0:string, 1:string|null}|null */ - protected function possiblyGetEmailAndNameFromRecipient($recipient) + protected function possiblyGetEmailAndNameFromRecipient(object $recipient): ?array { if (isset($recipient->mailbox, $recipient->host)) { /** @var mixed */ @@ -1919,7 +1675,7 @@ protected function possiblyGetEmailAndNameFromRecipient($recipient) if ('' !== \trim($recipientMailbox) && '' !== \trim($recipientHost)) { $recipientEmail = \strtolower($recipientMailbox.'@'.$recipientHost); - $recipientName = (\is_string($recipientPersonal) and '' !== \trim($recipientPersonal)) ? $this->decodeMimeStr($recipientPersonal, $this->getServerEncoding()) : null; + $recipientName = (\is_string($recipientPersonal) and '' !== \trim($recipientPersonal)) ? $this->decodeMimeStr($recipientPersonal) : null; return [ $recipientEmail, @@ -1932,23 +1688,22 @@ protected function possiblyGetEmailAndNameFromRecipient($recipient) } /** - * @psalm-param (scalar|array|object|resource|null)[] $t - * - * @return array + * @psalm-param array $t * * @todo revisit implementation pending resolution of https://github.com/vimeo/psalm/issues/2619 */ - protected function possiblyGetMailboxes(array $t) + protected function possiblyGetMailboxes(array $t): array { $arr = []; if ($t) { foreach ($t as $index => $item) { - /** @var scalar|array|object|resource|null */ - $item_name = \is_object($item) && isset($item->name) ? $item->name : null; - if (!\is_object($item)) { throw new UnexpectedValueException('Index '.(string) $index.' of argument 1 passed to '.__METHOD__.'() corresponds to a non-object value, '.\gettype($item).' given!'); - } elseif (!isset($item->name, $item->attributes, $item->delimiter)) { + } + /** @var scalar|array|object|resource|null */ + $item_name = isset($item->name) ? $item->name : null; + + if (!isset($item->name, $item->attributes, $item->delimiter)) { throw new UnexpectedValueException('The object at index '.(string) $index.' of argument 1 passed to '.__METHOD__.'() was missing one or more of the required properties "name", "attributes", "delimiter"!'); } elseif (!\is_string($item_name)) { throw new UnexpectedValueException('The object at index '.(string) $index.' of argument 1 passed to '.__METHOD__.'() has a non-string value for the name property!'); @@ -1975,11 +1730,9 @@ protected function possiblyGetMailboxes(array $t) /** * @psalm-param HOSTNAMEANDADDRESS $t * - * @return array - * * @psalm-return array{0:string|null, 1:string|null, 2:string} */ - protected function possiblyGetHostNameAndAddress(array $t) + protected function possiblyGetHostNameAndAddress(array $t): array { $out = [ isset($t[0]->host) ? $t[0]->host : (isset($t[1], $t[1]->host) ? $t[1]->host : null), @@ -1988,7 +1741,7 @@ protected function possiblyGetHostNameAndAddress(array $t) foreach ([0, 1] as $index) { $maybe = isset($t[$index], $t[$index]->personal) ? $t[$index]->personal : null; if (\is_string($maybe) && '' !== \trim($maybe)) { - $out[1] = $this->decodeMimeStr($maybe, $this->getServerEncoding()); + $out[1] = $this->decodeMimeStr($maybe); break; } @@ -2002,11 +1755,9 @@ protected function possiblyGetHostNameAndAddress(array $t) } /** - * @return void - * * @todo revisit redundant condition issues pending fix of https://github.com/vimeo/psalm/issues/2626 */ - protected function pingOrDisconnect() + protected function pingOrDisconnect(): void { if ($this->imapStream && !Imap::ping($this->imapStream)) { $this->disconnect(); @@ -2017,18 +1768,13 @@ protected function pingOrDisconnect() /** * Search the mailbox for emails from multiple, specific senders. * - * @param string $criteria - * @param bool $disableServerEncoding - * @param string $sender - * @param string ...$senders - * * This function wraps Mailbox::searchMailbox() to overcome a shortcoming in ext-imap * * @return int[] * * @psalm-return list */ - protected function searchMailboxFromWithOrWithoutDisablingServerEncoding($criteria, $disableServerEncoding, $sender, ...$senders) + protected function searchMailboxFromWithOrWithoutDisablingServerEncoding(string $criteria, bool $disableServerEncoding, string $sender, string ...$senders): array { \array_unshift($senders, $sender); diff --git a/tests/unit/Fixtures/DataPartInfo.php b/tests/unit/Fixtures/DataPartInfo.php index 03caee79..367e6942 100644 --- a/tests/unit/Fixtures/DataPartInfo.php +++ b/tests/unit/Fixtures/DataPartInfo.php @@ -1,18 +1,19 @@ decodeAfterFetch(); } - /** @param string|null $data */ - public function setData($data) + public function setData(string $data = null): void { $this->data = $data; } diff --git a/tests/unit/Fixtures/Mailbox.php b/tests/unit/Fixtures/Mailbox.php new file mode 100644 index 00000000..60c7e736 --- /dev/null +++ b/tests/unit/Fixtures/Mailbox.php @@ -0,0 +1,15 @@ +imapPassword; + } +} diff --git a/tests/unit/IncomingMailTest.php b/tests/unit/IncomingMailTest.php index 79389a85..03f3e136 100644 --- a/tests/unit/IncomingMailTest.php +++ b/tests/unit/IncomingMailTest.php @@ -2,6 +2,7 @@ /** * @author BAPCLTD-Marv */ +declare(strict_types=1); namespace PhpImap; @@ -9,7 +10,7 @@ class IncomingMailTest extends TestCase { - public function testSetHeader() + public function testSetHeader(): void { $mail = new IncomingMail(); $header = new IncomingMailHeader(); @@ -38,7 +39,7 @@ public function testSetHeader() } } - public function testDataPartInfo() + public function testDataPartInfo(): void { $mail = new IncomingMail(); $mailbox = new Mailbox('', '', ''); @@ -55,7 +56,7 @@ public function testDataPartInfo() $this->assertTrue($mail->__isset('textPlain')); } - public function testAttachments() + public function testAttachments(): void { $mail = new IncomingMail(); @@ -75,7 +76,7 @@ public function testAttachments() $this->assertSame($attachments, $mail->getAttachments()); foreach ($attachments as $attachment) { - $this->assertTrue(\is_string($attachment->id)); + $this->assertIsString($attachment->id); $this->assertTrue($mail->removeAttachment($attachment->id)); } @@ -83,7 +84,7 @@ public function testAttachments() $this->assertSame([], $mail->getAttachments()); foreach ($attachments as $attachment) { - $this->assertTrue(\is_string($attachment->id)); + $this->assertIsString($attachment->id); $this->assertFalse($mail->removeAttachment($attachment->id)); } } diff --git a/tests/unit/LiveMailboxTest.php b/tests/unit/LiveMailboxTest.php index 5fb6486b..61e9e31d 100644 --- a/tests/unit/LiveMailboxTest.php +++ b/tests/unit/LiveMailboxTest.php @@ -1,5 +1,4 @@ * * @todo see @todo of Imap::mail_compose() - * @todo drop php 5.6, remove paragonie/random_compat */ class LiveMailboxTest extends TestCase { @@ -53,17 +52,9 @@ class LiveMailboxTest extends TestCase * Provides constructor arguments for a live mailbox. * * @psalm-return MAILBOX_ARGS[] - * - * @todo drop php 5.6, add paragonie/hidden-string to require-dev */ - public function MailBoxProvider() + public function MailBoxProvider(): array { - if (!\class_exists(HiddenString::class)) { - $this->markTestSkipped('paragonie/hidden-string not installed!'); - - return []; - } - $sets = []; $imapPath = \getenv('PHPIMAP_IMAP_PATH'); @@ -81,13 +72,8 @@ public function MailBoxProvider() * @dataProvider MailBoxProvider * * @group live - * - * @param string $attachmentsDir - * @param string $serverEncoding - * - * @return void */ - public function testGetImapStream(HiddenString $imapPath, HiddenString $login, HiddenString $password, $attachmentsDir, $serverEncoding = 'UTF-8') + public function testGetImapStream(HiddenString $imapPath, HiddenString $login, HiddenString $password, string $attachmentsDir, string $serverEncoding = 'UTF-8'): void { list($mailbox, $remove_mailbox) = $this->getMailbox( $imapPath, @@ -101,7 +87,7 @@ public function testGetImapStream(HiddenString $imapPath, HiddenString $login, H $exception = null; try { - $this->assertTrue(\is_resource($mailbox->getImapStream())); + $mailbox->getImapStream(); $this->assertTrue($mailbox->hasImapStream()); $mailboxes = $mailbox->getMailboxes(); @@ -112,9 +98,9 @@ public function testGetImapStream(HiddenString $imapPath, HiddenString $login, H $limit = \min(\count($mailboxes), self::RANDOM_MAILBOX_SAMPLE_SIZE); for ($i = 0; $i < $limit; ++$i) { - static::assertTrue(\is_array($mailboxes[$i])); - static::assertTrue(isset($mailboxes[$i]['shortpath'])); - static::assertTrue(\is_string($mailboxes[$i]['shortpath'])); + $this->assertIsArray($mailboxes[$i]); + $this->assertTrue(isset($mailboxes[$i]['shortpath'])); + $this->assertIsString($mailboxes[$i]['shortpath']); $mailbox->switchMailbox($mailboxes[$i]['shortpath']); $check = $mailbox->checkMailbox(); @@ -129,7 +115,7 @@ public function testGetImapStream(HiddenString $imapPath, HiddenString $login, H $this->assertTrue(\property_exists($check, $expectedProperty)); } - $this->assertTrue(\is_string($check->Date), 'Date property of Mailbox::checkMailbox() result was not a string!'); + $this->assertIsString($check->Date, 'Date property of Mailbox::checkMailbox() result was not a string!'); $unix = \strtotime($check->Date); @@ -141,10 +127,10 @@ public function testGetImapStream(HiddenString $imapPath, HiddenString $login, H $unix = \strtotime(\substr($check->Date, 0, $pos)); } - $this->assertTrue(\is_int($unix), 'Date property of Mailbox::checkMailbox() result was not a valid date!'); + $this->assertIsInt($unix, 'Date property of Mailbox::checkMailbox() result was not a valid date!'); $this->assertTrue(\in_array($check->Driver, ['POP3', 'IMAP', 'NNTP', 'pop3', 'imap', 'nntp'], true), 'Driver property of Mailbox::checkMailbox() result was not of an expected value!'); - $this->assertTrue(\is_int($check->Nmsgs), 'Nmsgs property of Mailbox::checkMailbox() result was not of an expected type!'); - $this->assertTrue(\is_int($check->Recent), 'Recent property of Mailbox::checkMailbox() result was not of an expected type!'); + $this->assertIsInt($check->Nmsgs, 'Nmsgs property of Mailbox::checkMailbox() result was not of an expected type!'); + $this->assertIsInt($check->Recent, 'Recent property of Mailbox::checkMailbox() result was not of an expected type!'); $status = $mailbox->statusMailbox(); @@ -176,7 +162,7 @@ public function testGetImapStream(HiddenString $imapPath, HiddenString $login, H /** * @psalm-return Generator */ - public function ComposeProvider() + public function ComposeProvider(): Generator { $random_subject = 'test: '.\bin2hex(\random_bytes(16)); @@ -330,14 +316,10 @@ public function ComposeProvider() * * @group compose * - * @param string $expected_result - * * @psalm-param COMPOSE_ENVELOPE $envelope * @psalm-param COMPOSE_BODY $body - * - * @return void */ - public function test_mail_compose(array $envelope, array $body, $expected_result) + public function testMailCompose(array $envelope, array $body, string $expected_result): void { $actual_result = Imap::mail_compose($envelope, $body); @@ -346,7 +328,7 @@ public function test_mail_compose(array $envelope, array $body, $expected_result $actual_result ); - static::assertSame($expected_result, $actual_result); + $this->assertSame($expected_result, $actual_result); } /** @@ -358,17 +340,17 @@ public function test_mail_compose(array $envelope, array $body, $expected_result * 4:bool * }, mixed, void> */ - public function AppendProvider() + public function AppendProvider(): Generator { foreach ($this->MailBoxProvider() as $mailbox_args) { foreach ($this->ComposeProvider() as $compose_args) { - list($envelope, $body, $expected_compose_result) = $compose_args; + [$envelope, $body, $expected_compose_result] = $compose_args; yield [$mailbox_args, $envelope, $body, $expected_compose_result, false]; } foreach ($this->ComposeProvider() as $compose_args) { - list($envelope, $body, $expected_compose_result) = $compose_args; + [$envelope, $body, $expected_compose_result] = $compose_args; yield [$mailbox_args, $envelope, $body, $expected_compose_result, true]; } @@ -381,49 +363,32 @@ public function AppendProvider() * @group live * * @depends testGetImapStream - * @depends test_mail_compose - * - * @param string $_expected_compose_result - * @param bool $pre_compose + * @depends testMailCompose * * @psalm-param MAILBOX_ARGS $mailbox_args * @psalm-param COMPOSE_ENVELOPE $envelope * @psalm-param COMPOSE_BODY $body - * - * @return void */ - public function test_append( + public function testAppend( array $mailbox_args, array $envelope, array $body, - $_expected_compose_result, - $pre_compose - ) { - if (!isset($envelope['subject'])) { - static::markTestSkipped( - 'Cannot search for message by subject, no subject specified!' - ); - + string $_expected_compose_result, + bool $pre_compose + ): void { + if ($this->MaybeSkipAppendTest($envelope)) { return; } - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)); - - list($path, $username, $password, $attachments_dir) = $mailbox_args; + list($search_criteria) = $this->SubjectSearchCriteriaAndSubject($envelope); - list($mailbox, $remove_mailbox) = $this->getMailbox( - $path, - $username, - $password, - $attachments_dir, - isset($mailbox_args[4]) ? $mailbox_args[4] : 'UTF-8' + list($mailbox, $remove_mailbox, $path) = $this->getMailboxFromArgs( + $mailbox_args ); - $search_criteria = \sprintf('SUBJECT "%s"', $envelope['subject']); - $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 0, $search, ( @@ -443,7 +408,7 @@ public function test_append( $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 1, $search, ( @@ -460,7 +425,7 @@ public function test_append( $mailbox->switchMailbox($path->getString()); $mailbox->deleteMailbox($remove_mailbox); - static::assertCount( + $this->assertCount( 0, $mailbox->searchMailbox($search_criteria), ( @@ -475,46 +440,29 @@ public function test_append( * * @group live * - * @depends test_append - * - * @param string $_expected_compose_result - * @param bool $pre_compose + * @depends testAppend * * @psalm-param MAILBOX_ARGS $mailbox_args * @psalm-param COMPOSE_ENVELOPE $envelope * @psalm-param COMPOSE_BODY $body - * - * @return void */ - public function test_append_nudges_mailbox_count( + public function testAppendNudgesMailboxCount( array $mailbox_args, array $envelope, array $body, - $_expected_compose_result, - $pre_compose - ) { - if (!isset($envelope['subject'])) { - static::markTestSkipped( - 'Cannot search for message by subject, no subject specified!' - ); - + string $_expected_compose_result, + bool $pre_compose + ): void { + if ($this->MaybeSkipAppendTest($envelope)) { return; } - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)); + list($search_criteria) = $this->SubjectSearchCriteriaAndSubject($envelope); - list($path, $username, $password, $attachments_dir) = $mailbox_args; - - list($mailbox, $remove_mailbox) = $this->getMailbox( - $path, - $username, - $password, - $attachments_dir, - isset($mailbox_args[4]) ? $mailbox_args[4] : 'UTF-8' + list($mailbox, $remove_mailbox, $path) = $this->getMailboxFromArgs( + $mailbox_args ); - $search_criteria = \sprintf('SUBJECT "%s"', $envelope['subject']); - $count = $mailbox->countMails(); $message = [$envelope, $body]; @@ -525,7 +473,7 @@ public function test_append_nudges_mailbox_count( $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 0, $search, ( @@ -539,7 +487,7 @@ public function test_append_nudges_mailbox_count( $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 1, $search, ( @@ -549,7 +497,7 @@ public function test_append_nudges_mailbox_count( ) ); - static::assertSame( + $this->assertSame( $count + 1, $mailbox->countMails(), ( @@ -566,7 +514,7 @@ public function test_append_nudges_mailbox_count( $mailbox->switchMailbox($path->getString()); $mailbox->deleteMailbox($remove_mailbox); - static::assertCount( + $this->assertCount( 0, $mailbox->searchMailbox($search_criteria), ( @@ -581,46 +529,29 @@ public function test_append_nudges_mailbox_count( * * @group live * - * @depends test_append - * - * @param string $_expected_compose_result - * @param bool $pre_compose + * @depends testAppend * * @psalm-param MAILBOX_ARGS $mailbox_args * @psalm-param COMPOSE_ENVELOPE $envelope * @psalm-param COMPOSE_BODY $body - * - * @return void */ - public function test_append_single_search_matches_sort( + public function testAppendSingleSearchMatchesSort( array $mailbox_args, array $envelope, array $body, - $_expected_compose_result, - $pre_compose - ) { - if (!isset($envelope['subject'])) { - static::markTestSkipped( - 'Cannot search for message by subject, no subject specified!' - ); - + string $_expected_compose_result, + bool $pre_compose + ): void { + if ($this->MaybeSkipAppendTest($envelope)) { return; } - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)); - - list($path, $username, $password, $attachments_dir) = $mailbox_args; + list($search_criteria) = $this->SubjectSearchCriteriaAndSubject($envelope); - list($mailbox, $remove_mailbox) = $this->getMailbox( - $path, - $username, - $password, - $attachments_dir, - isset($mailbox_args[4]) ? $mailbox_args[4] : 'UTF-8' + list($mailbox, $remove_mailbox, $path) = $this->getMailboxFromArgs( + $mailbox_args ); - $search_criteria = \sprintf('SUBJECT "%s"', $envelope['subject']); - $message = [$envelope, $body]; if ($pre_compose) { @@ -629,7 +560,7 @@ public function test_append_single_search_matches_sort( $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 0, $search, ( @@ -643,7 +574,7 @@ public function test_append_single_search_matches_sort( $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 1, $search, ( @@ -653,22 +584,22 @@ public function test_append_single_search_matches_sort( ) ); - static::assertSame( + $this->assertSame( $search, $mailbox->sortMails(SORTARRIVAL, true, $search_criteria) ); - static::assertSame( + $this->assertSame( $search, $mailbox->sortMails(SORTARRIVAL, false, $search_criteria) ); - static::assertSame( + $this->assertSame( $search, $mailbox->sortMails(SORTARRIVAL, false, $search_criteria, 'UTF-8') ); - static::assertTrue(\in_array( + $this->assertTrue(\in_array( $search[0], $mailbox->sortMails(SORTARRIVAL, false, null), true @@ -681,7 +612,7 @@ public function test_append_single_search_matches_sort( $mailbox->switchMailbox($path->getString()); $mailbox->deleteMailbox($remove_mailbox); - static::assertCount( + $this->assertCount( 0, $mailbox->searchMailbox($search_criteria), ( @@ -696,46 +627,29 @@ public function test_append_single_search_matches_sort( * * @group live * - * @depends test_append - * - * @param string $expected_compose_result - * @param bool $pre_compose + * @depends testAppend * * @psalm-param MAILBOX_ARGS $mailbox_args * @psalm-param COMPOSE_ENVELOPE $envelope * @psalm-param COMPOSE_BODY $body - * - * @return void */ - public function test_append_retrieval_matches_expected( + public function testAppendRetrievalMatchesExpected( array $mailbox_args, array $envelope, array $body, - $expected_compose_result, - $pre_compose - ) { - if (!isset($envelope['subject'])) { - static::markTestSkipped( - 'Cannot search for message by subject, no subject specified!' - ); - + string $expected_compose_result, + bool $pre_compose + ): void { + if ($this->MaybeSkipAppendTest($envelope)) { return; } - static::assertTrue(\is_string(isset($envelope['subject']) ? $envelope['subject'] : null)); + list($search_criteria, $search_subject) = $this->SubjectSearchCriteriaAndSubject($envelope); - list($path, $username, $password, $attachments_dir) = $mailbox_args; - - list($mailbox, $remove_mailbox) = $this->getMailbox( - $path, - $username, - $password, - $attachments_dir, - isset($mailbox_args[4]) ? $mailbox_args[4] : 'UTF-8' + list($mailbox, $remove_mailbox, $path) = $this->getMailboxFromArgs( + $mailbox_args ); - $search_criteria = \sprintf('SUBJECT "%s"', $envelope['subject']); - $message = [$envelope, $body]; if ($pre_compose) { @@ -744,7 +658,7 @@ public function test_append_retrieval_matches_expected( $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 0, $search, ( @@ -758,7 +672,7 @@ public function test_append_retrieval_matches_expected( $search = $mailbox->searchMailbox($search_criteria); - static::assertCount( + $this->assertCount( 1, $search, ( @@ -770,7 +684,7 @@ public function test_append_retrieval_matches_expected( $actual_result = $mailbox->getMailMboxFormat($search[0]); - static::assertSame( + $this->assertSame( $this->ReplaceBoundaryHere( $expected_compose_result, $actual_result @@ -780,7 +694,7 @@ public function test_append_retrieval_matches_expected( $actual_result = $mailbox->getRawMail($search[0]); - static::assertSame( + $this->assertSame( $this->ReplaceBoundaryHere( $expected_compose_result, $actual_result @@ -790,8 +704,8 @@ public function test_append_retrieval_matches_expected( $mail = $mailbox->getMail($search[0], false); - static::assertSame( - $envelope['subject'], + $this->assertSame( + $search_subject, $mail->subject, ( 'If a retrieved mail did not have a matching subject'. @@ -802,10 +716,10 @@ public function test_append_retrieval_matches_expected( $info = $mailbox->getMailsInfo($search); - static::assertCount(1, $info); + $this->assertCount(1, $info); - static::assertSame( - $envelope['subject'], + $this->assertSame( + $search_subject, $info[0]->subject, ( 'If a retrieved mail did not have a matching subject'. @@ -816,20 +730,20 @@ public function test_append_retrieval_matches_expected( if (1 === \preg_match( '/^barbushin\/php-imap#(448|391):/', - $envelope['subject'], + $envelope['subject'] ?? '', $matches )) { - static::assertTrue($mail->hasAttachments()); + $this->assertTrue($mail->hasAttachments()); $attachments = $mail->getAttachments(); - static::assertCount(self::ISSUE_EXPECTED_ATTACHMENT_COUNT[ + $this->assertCount(self::ISSUE_EXPECTED_ATTACHMENT_COUNT[ (int) $matches[1]], $attachments ); if ('448' === $matches[1]) { - static::assertSame( + $this->assertSame( \file_get_contents(__DIR__.'/../../.gitignore'), \current($attachments)->getContents() ); @@ -843,7 +757,7 @@ public function test_append_retrieval_matches_expected( $mailbox->switchMailbox($path->getString()); $mailbox->deleteMailbox($remove_mailbox); - static::assertCount( + $this->assertCount( 0, $mailbox->searchMailbox($search_criteria), ( @@ -861,7 +775,7 @@ public function test_append_retrieval_matches_expected( * * @return mixed[] * - * @psalm-return array{0:Mailbox, 1:string} + * @psalm-return array{0:Mailbox, 1:string, 2:HiddenString} */ protected function getMailbox(HiddenString $imapPath, HiddenString $login, HiddenString $password, $attachmentsDir, $serverEncoding = 'UTF-8') { @@ -873,7 +787,60 @@ protected function getMailbox(HiddenString $imapPath, HiddenString $login, Hidde $mailbox->switchMailbox($random, false); - return [$mailbox, $random]; + return [$mailbox, $random, $imapPath]; + } + + /** + * @psalm-param MAILBOX_ARGS $mailbox_args + * + * @return mixed[] + * + * @psalm-return array{0:Mailbox, 1:string, 2:HiddenString} + */ + protected function getMailboxFromArgs(array $mailbox_args): array + { + list($path, $username, $password, $attachments_dir) = $mailbox_args; + + return $this->getMailbox( + $path, + $username, + $password, + $attachments_dir, + isset($mailbox_args[4]) ? $mailbox_args[4] : 'UTF-8' + ); + } + + protected function MaybeSkipAppendTest(array $envelope): bool + { + if (!isset($envelope['subject'])) { + $this->markTestSkipped( + 'Cannot search for message by subject, no subject specified!' + ); + + return true; + } + + return false; + } + + /** + * Get subject search criteria and subject. + * + * @psalm-param array{subject?:mixed} $envelope + * + * @psalm-return array{0:string, 1:string} + */ + protected function SubjectSearchCriteriaAndSubject(array $envelope): array + { + /** @var string|null */ + $subject = isset($envelope['subject']) ? $envelope['subject'] : null; + + $this->assertIsString($subject); + + $search_criteria = \sprintf('SUBJECT "%s"', (string) $subject); + + /** @psalm-var array{0:string, 1:string} */ + return [$search_criteria, (string) $subject]; } /** diff --git a/tests/unit/MailboxTest.php b/tests/unit/MailboxTest.php index 012c343a..3e97a31a 100644 --- a/tests/unit/MailboxTest.php +++ b/tests/unit/MailboxTest.php @@ -1,10 +1,10 @@ */ +declare(strict_types=1); namespace PhpImap; @@ -57,10 +57,8 @@ final class MailboxTest extends TestCase /** * Test, that the constructor trims possible variables * Leading and ending spaces are not even possible in some variables. - * - * @return void */ - public function testConstructorTrimsPossibleVariables() + public function testConstructorTrimsPossibleVariables(): void { $imapPath = ' {imap.example.com:993/imap/ssl}INBOX '; $login = ' php-imap@example.com'; @@ -69,13 +67,13 @@ public function testConstructorTrimsPossibleVariables() $attachmentsDir = '.'; $serverEncoding = 'UTF-8 '; - $mailbox = new Mailbox($imapPath, $login, $password, $attachmentsDir, $serverEncoding); + $mailbox = new Fixtures\Mailbox($imapPath, $login, $password, $attachmentsDir, $serverEncoding); - $this->assertAttributeEquals('{imap.example.com:993/imap/ssl}INBOX', 'imapPath', $mailbox); - $this->assertAttributeEquals('php-imap@example.com', 'imapLogin', $mailbox); - $this->assertAttributeEquals(' v3rY!53cEt&P4sSWöRd$', 'imapPassword', $mailbox); - $this->assertAttributeEquals(\realpath('.'), 'attachmentsDir', $mailbox); - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox); + $this->assertSame('{imap.example.com:993/imap/ssl}INBOX', $mailbox->getImapPath()); + $this->assertSame('php-imap@example.com', $mailbox->getLogin()); + $this->assertSame(' v3rY!53cEt&P4sSWöRd$', $mailbox->getImapPassword()); + $this->assertSame(\realpath('.'), $mailbox->getAttachmentsDir()); + $this->assertSame('UTF-8', $mailbox->getServerEncoding()); } /** @@ -110,12 +108,8 @@ public function SetAndGetServerEncodingProvider() * Test, that the server encoding can be set. * * @dataProvider SetAndGetServerEncodingProvider - * - * @param string $encoding - * - * @return void */ - public function testSetAndGetServerEncoding($encoding) + public function testSetAndGetServerEncoding(string $encoding): void { $mailbox = $this->getMailbox(); @@ -128,30 +122,26 @@ public function testSetAndGetServerEncoding($encoding) /** * Test, that server encoding is set to a default value. - * - * @return void */ - public function testServerEncodingHasDefaultSetting() + public function testServerEncodingHasDefaultSetting(): void { // Default character encoding should be set $mailbox = new Mailbox($this->imapPath, $this->login, $this->password, $this->attachmentsDir); - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox); + $this->assertSame('UTF-8', $mailbox->getServerEncoding()); } /** * Test, that server encoding that all functions uppers the server encoding setting. - * - * @return void */ - public function testServerEncodingUppersSetting() + public function testServerEncodingUppersSetting(): void { // Server encoding should be always upper formatted $mailbox = new Mailbox($this->imapPath, $this->login, $this->password, $this->attachmentsDir, 'utf-8'); - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox); + $this->assertSame('UTF-8', $mailbox->getServerEncoding()); $mailbox = new Mailbox($this->imapPath, $this->login, $this->password, $this->attachmentsDir, 'UTF7-IMAP'); $mailbox->setServerEncoding('uTf-8'); - $this->assertAttributeEquals('UTF-8', 'serverEncoding', $mailbox); + $this->assertSame('UTF-8', $mailbox->getServerEncoding()); } /** @@ -159,7 +149,7 @@ public function testServerEncodingUppersSetting() * * @return array */ - public function serverEncodingProvider() + public function serverEncodingProvider(): array { return [ // Supported encodings @@ -186,13 +176,8 @@ public function serverEncodingProvider() * Test, that server encoding only can use supported character encodings. * * @dataProvider serverEncodingProvider - * - * @param bool $bool - * @param string $encoding - * - * @return void */ - public function testServerEncodingOnlyUseSupportedSettings($bool, $encoding) + public function testServerEncodingOnlyUseSupportedSettings(bool $bool, string $encoding): void { $mailbox = $this->getMailbox(); @@ -210,10 +195,8 @@ public function testServerEncodingOnlyUseSupportedSettings($bool, $encoding) * Test, that the IMAP search option has a default value * 1 => SE_UID * 2 => SE_FREE. - * - * @return void */ - public function testImapSearchOptionHasADefault() + public function testImapSearchOptionHasADefault(): void { $this->assertEquals($this->getMailbox()->getImapSearchOption(), 1); } @@ -222,10 +205,8 @@ public function testImapSearchOptionHasADefault() * Test, that the IMAP search option can be changed * 1 => SE_UID * 2 => SE_FREE. - * - * @return void */ - public function testSetAndGetImapSearchOption() + public function testSetAndGetImapSearchOption(): void { $mailbox = $this->getMailbox(); @@ -241,20 +222,16 @@ public function testSetAndGetImapSearchOption() /** * Test, that the imap login can be retrieved. - * - * @return void */ - public function testGetLogin() + public function testGetLogin(): void { $this->assertEquals($this->getMailbox()->getLogin(), 'php-imap@example.com'); } /** * Test, that the path delimiter has a default value. - * - * @return void */ - public function testPathDelimiterHasADefault() + public function testPathDelimiterHasADefault(): void { $this->assertNotEmpty($this->getMailbox()->getPathDelimiter()); } @@ -264,7 +241,7 @@ public function testPathDelimiterHasADefault() * * @psalm-return array{0:string}[] */ - public function pathDelimiterProvider() + public function pathDelimiterProvider(): array { return [ '0' => ['0'], @@ -332,12 +309,8 @@ public function pathDelimiterProvider() * Test, that the path delimiter is checked for supported chars. * * @dataProvider pathDelimiterProvider - * - * @param string $str - * - * @return void */ - public function testPathDelimiterIsBeingChecked($str) + public function testPathDelimiterIsBeingChecked(string $str): void { $supported_delimiters = ['.', '/']; @@ -353,10 +326,8 @@ public function testPathDelimiterIsBeingChecked($str) /** * Test, that the path delimiter can be set. - * - * @return void */ - public function testSetAndGetPathDelimiter() + public function testSetAndGetPathDelimiter(): void { $mailbox = $this->getMailbox(); @@ -369,10 +340,8 @@ public function testSetAndGetPathDelimiter() /** * Test, that the attachments are not ignored by default. - * - * @return void */ - public function testGetAttachmentsAreNotIgnoredByDefault() + public function testGetAttachmentsAreNotIgnoredByDefault(): void { $this->assertEquals($this->getMailbox()->getAttachmentsIgnore(), false); } @@ -380,18 +349,14 @@ public function testGetAttachmentsAreNotIgnoredByDefault() /** * Provides test data for testing attachments ignore. * - * @psalm-return array + * @psalm-return array */ - public function attachmentsIgnoreProvider() + public function attachmentsIgnoreProvider(): array { - /** @psalm-var array */ + /** @psalm-var array */ return [ - 'true' => ['assertEquals', true], - 'false' => ['assertEquals', false], - '1' => ['expectException', 1], - '0' => ['expectException', 0], - 'something' => ['expectException', 'something'], - '2' => ['expectException', 2], + 'true' => [true], + 'false' => [false], ]; } @@ -399,24 +364,12 @@ public function attachmentsIgnoreProvider() * Test, that attachments can be ignored and only valid values are accepted. * * @dataProvider attachmentsIgnoreProvider - * - * @psalm-param 'expectException'|'assertEquals' $assertTest - * - * @param scalar $paramValue - * - * @return void */ - public function testSetAttachmentsIgnore($assertTest, $paramValue) + public function testSetAttachmentsIgnore(bool $paramValue): void { $mailbox = $this->getMailbox(); - - if ('expectException' == $assertTest) { - $this->expectException(InvalidParameterException::class); - $mailbox->setAttachmentsIgnore($paramValue); - } else { - $mailbox->setAttachmentsIgnore((bool) $paramValue); - $this->assertEquals($mailbox->getAttachmentsIgnore(), (bool) $paramValue); - } + $mailbox->setAttachmentsIgnore($paramValue); + $this->assertEquals($mailbox->getAttachmentsIgnore(), $paramValue); } /** @@ -424,7 +377,7 @@ public function testSetAttachmentsIgnore($assertTest, $paramValue) * * @psalm-return array */ - public function encodingTestStringsProvider() + public function encodingTestStringsProvider(): array { return [ 'Avañe’ẽ' => ['Avañe’ẽ'], // Guaraní @@ -463,12 +416,8 @@ public function encodingTestStringsProvider() * Test, that strings encoded to UTF-7 can be decoded back to UTF-8. * * @dataProvider encodingTestStringsProvider - * - * @param string $str - * - * @return void */ - public function testEncodingToUtf7DecodeBackToUtf8($str) + public function testEncodingToUtf7DecodeBackToUtf8(string $str): void { $mailbox = $this->getMailbox(); @@ -482,38 +431,34 @@ public function testEncodingToUtf7DecodeBackToUtf8($str) * Test, that strings encoded to UTF-7 can be decoded back to UTF-8. * * @dataProvider encodingTestStringsProvider - * - * @param string $str - * - * @return void */ - public function testMimeDecodingReturnsCorrectValues($str) + public function testMimeDecodingReturnsCorrectValues(string $str): void { - $this->assertEquals($this->getMailbox()->decodeMimeStr($str, 'utf-8'), $str); + $this->assertEquals($this->getMailbox()->decodeMimeStr($str), $str); } /** * Provides test data for testing parsing datetimes. * - * @psalm-return array + * @psalm-return array */ - public function datetimeProvider() + public function datetimeProvider(): array { return [ - 'Sun, 14 Aug 2005 16:13:03 +0000 (CEST)' => ['2005-08-14T16:13:03+00:00', '1124035983'], - 'Sun, 14 Aug 2005 16:13:03 +0000' => ['2005-08-14T16:13:03+00:00', '1124035983'], + 'Sun, 14 Aug 2005 16:13:03 +0000 (CEST)' => ['2005-08-14T16:13:03+00:00', 1124035983], + 'Sun, 14 Aug 2005 16:13:03 +0000' => ['2005-08-14T16:13:03+00:00', 1124035983], - 'Sun, 14 Aug 2005 16:13:03 +1000 (CEST)' => ['2005-08-14T06:13:03+00:00', '1123999983'], - 'Sun, 14 Aug 2005 16:13:03 +1000' => ['2005-08-14T06:13:03+00:00', '1123999983'], - 'Sun, 14 Aug 2005 16:13:03 -1000' => ['2005-08-15T02:13:03+00:00', '1124071983'], + 'Sun, 14 Aug 2005 16:13:03 +1000 (CEST)' => ['2005-08-14T06:13:03+00:00', 1123999983], + 'Sun, 14 Aug 2005 16:13:03 +1000' => ['2005-08-14T06:13:03+00:00', 1123999983], + 'Sun, 14 Aug 2005 16:13:03 -1000' => ['2005-08-15T02:13:03+00:00', 1124071983], - 'Sun, 14 Aug 2005 16:13:03 +1100 (CEST)' => ['2005-08-14T05:13:03+00:00', '1123996383'], - 'Sun, 14 Aug 2005 16:13:03 +1100' => ['2005-08-14T05:13:03+00:00', '1123996383'], - 'Sun, 14 Aug 2005 16:13:03 -1100' => ['2005-08-15T03:13:03+00:00', '1124075583'], + 'Sun, 14 Aug 2005 16:13:03 +1100 (CEST)' => ['2005-08-14T05:13:03+00:00', 1123996383], + 'Sun, 14 Aug 2005 16:13:03 +1100' => ['2005-08-14T05:13:03+00:00', 1123996383], + 'Sun, 14 Aug 2005 16:13:03 -1100' => ['2005-08-15T03:13:03+00:00', 1124075583], - '14 Aug 2005 16:13:03 +1000 (CEST)' => ['2005-08-14T06:13:03+00:00', '1123999983'], - '14 Aug 2005 16:13:03 +1000' => ['2005-08-14T06:13:03+00:00', '1123999983'], - '14 Aug 2005 16:13:03 -1000' => ['2005-08-15T02:13:03+00:00', '1124071983'], + '14 Aug 2005 16:13:03 +1000 (CEST)' => ['2005-08-14T06:13:03+00:00', 1123999983], + '14 Aug 2005 16:13:03 +1000' => ['2005-08-14T06:13:03+00:00', 1123999983], + '14 Aug 2005 16:13:03 -1000' => ['2005-08-15T02:13:03+00:00', 1124071983], ]; } @@ -521,17 +466,12 @@ public function datetimeProvider() * Test, different datetimes conversions using differents timezones. * * @dataProvider datetimeProvider - * - * @param string $dateToParse - * @param numeric $epochToCompare - * - * @return void */ - public function testParsedDateDifferentTimeZones($dateToParse, $epochToCompare) + public function testParsedDateDifferentTimeZones(string $dateToParse, int $epochToCompare): void { $parsedDt = $this->getMailbox()->parseDateTime($dateToParse); $parsedDateTime = new DateTime($parsedDt); - $this->assertEquals($parsedDateTime->format('U'), $epochToCompare); + $this->assertEquals((int) $parsedDateTime->format('U'), $epochToCompare); } /** @@ -539,7 +479,7 @@ public function testParsedDateDifferentTimeZones($dateToParse, $epochToCompare) * * @psalm-return array */ - public function invalidDatetimeProvider() + public function invalidDatetimeProvider(): array { return [ 'Sun, 14 Aug 2005 16:13:03 +9000 (CEST)' => ['Sun, 14 Aug 2005 16:13:03 +9000 (CEST)'], @@ -552,12 +492,8 @@ public function invalidDatetimeProvider() * Test, different invalid / unparseable datetimes conversions. * * @dataProvider invalidDatetimeProvider - * - * @param string $dateToParse - * - * @return void */ - public function testParsedDateWithUnparseableDateTime($dateToParse) + public function testParsedDateWithUnparseableDateTime(string $dateToParse): void { $parsedDt = $this->getMailbox()->parseDateTime($dateToParse); $this->assertEquals($parsedDt, $dateToParse); @@ -565,10 +501,8 @@ public function testParsedDateWithUnparseableDateTime($dateToParse) /** * Test, parsed datetime being emtpy the header date. - * - * @return void */ - public function testParsedDateTimeWithEmptyHeaderDate() + public function testParsedDateTimeWithEmptyHeaderDate(): void { $this->expectException(InvalidParameterException::class); $this->getMailbox()->parseDateTime(''); @@ -581,7 +515,7 @@ public function testParsedDateTimeWithEmptyHeaderDate() * * @psalm-return list */ - public function mimeEncodingProvider() + public function mimeEncodingProvider(): array { return [ ['=?iso-8859-1?Q?Sebastian_Kr=E4tzig?= ', 'Sebastian Krätzig '], @@ -599,13 +533,8 @@ public function mimeEncodingProvider() * Test, that mime encoding returns correct strings. * * @dataProvider mimeEncodingProvider - * - * @param string $str - * @param string $expected - * - * @return void */ - public function testMimeEncoding($str, $expected) + public function testMimeEncoding(string $str, string $expected): void { $mailbox = $this->getMailbox(); @@ -622,7 +551,7 @@ public function testMimeEncoding($str, $expected) * * @psalm-return array}> */ - public function timeoutsProvider() + public function timeoutsProvider(): array { /** @psalm-var array}> */ return [ @@ -631,11 +560,6 @@ public function timeoutsProvider() 'array(IMAP_WRITETIMEOUT)' => ['assertNull', 1, [IMAP_WRITETIMEOUT]], 'array(IMAP_CLOSETIMEOUT)' => ['assertNull', 1, [IMAP_CLOSETIMEOUT]], 'array(IMAP_OPENTIMEOUT, IMAP_READTIMEOUT, IMAP_WRITETIMEOUT, IMAP_CLOSETIMEOUT)' => ['assertNull', 1, [IMAP_OPENTIMEOUT, IMAP_READTIMEOUT, IMAP_WRITETIMEOUT, IMAP_CLOSETIMEOUT]], - 'array(OPENTIMEOUT)' => ['expectException', 1, [\constant('OPENTIMEOUT')]], - 'array(READTIMEOUT)' => ['expectException', 1, [\constant('READTIMEOUT')]], - 'array(WRITETIMEOUT)' => ['expectException', 1, [\constant('WRITETIMEOUT')]], - 'array(CLOSETIMEOUT)' => ['expectException', 1, [\constant('CLOSETIMEOUT')]], - 'array(IMAP_OPENTIMEOUT, IMAP_READTIMEOUT, WRITETIMEOUT, IMAP_CLOSETIMEOUT)' => ['expectException', 1, [IMAP_OPENTIMEOUT, IMAP_READTIMEOUT, \constant('WRITETIMEOUT'), IMAP_CLOSETIMEOUT]], ]; } @@ -644,16 +568,12 @@ public function timeoutsProvider() * * @dataProvider timeoutsProvider * - * @param string $assertMethod - * @param int $timeout - * @param int[] $types + * @param int[] $types * * @psalm-param 'assertNull'|'expectException' $assertMethod * @psalm-param list<1|2|3|4> $types - * - * @return void */ - public function testSetTimeouts($assertMethod, $timeout, $types) + public function testSetTimeouts(string $assertMethod, int $timeout, array $types): void { $mailbox = $this->getMailbox(); @@ -670,7 +590,7 @@ public function testSetTimeouts($assertMethod, $timeout, $types) * * @psalm-return list}> */ - public function connectionArgsProvider() + public function connectionArgsProvider(): array { /** @psalm-var list}> */ return [ @@ -688,12 +608,11 @@ public function connectionArgsProvider() ['assertNull', OP_READONLY, 3, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], ['assertNull', OP_READONLY, 12, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], - ['expectException', OP_READONLY.OP_DEBUG, 0, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], + ['expectException', OP_READONLY | OP_DEBUG, 0, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], ['expectException', OP_READONLY, -1, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], ['expectException', OP_READONLY, -3, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], ['expectException', OP_READONLY, -12, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], - ['expectException', OP_READONLY, '-1', ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], - ['expectException', OP_READONLY, '1', ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], + ['expectException', OP_READONLY, -1, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']], ['expectException', OP_READONLY, 0, [null]], ]; } @@ -703,16 +622,9 @@ public function connectionArgsProvider() * * @dataProvider connectionArgsProvider * - * @param string $assertMethod - * @param int $option - * @param int $retriesNum - * @param array|null $param - * * @psalm-param array{DISABLE_AUTHENTICATOR?:string}|array $param - * - * @return void */ - public function testSetConnectionArgs($assertMethod, $option, $retriesNum, $param) + public function testSetConnectionArgs(string $assertMethod, int $option, int $retriesNum, array $param = null): void { $mailbox = $this->getMailbox(); @@ -729,7 +641,7 @@ public function testSetConnectionArgs($assertMethod, $option, $retriesNum, $para * * @psalm-return array */ - public function mimeStrDecodingProvider() + public function mimeStrDecodingProvider(): array { return [ '' => ['', ''], @@ -742,6 +654,7 @@ public function mimeStrDecodingProvider() 'Some subject here 😘 US-ASCII' => ['=?UTF-8?q?Some_subject_here_?= =?UTF-8?q?=F0=9F=98=98?=', 'Some subject here 😘', 'US-ASCII'], 'mountainguan测试 US-ASCII' => ['=?UTF-8?Q?mountainguan=E6=B5=8B=E8=AF=95?=', 'mountainguan测试', 'US-ASCII'], 'مقتطفات من: صن تزو. "فن الحرب". كتب أبل. Something' => ['مقتطفات من: صن تزو. "فن الحرب". كتب أبل. Something', 'مقتطفات من: صن تزو. "فن الحرب". كتب أبل. Something'], + '(事件单编号:TESTA-111111)(通报)入口有陌生人' => ['=?utf-8?b?KOS6i+S7tuWNlee8luWPtzpURVNUQS0xMTExMTEpKOmAmuaKpSnl?= =?utf-8?b?haXlj6PmnInpmYznlJ/kuro=?=', '(事件单编号:TESTA-111111)(通报)入口有陌生人'], ]; } @@ -749,27 +662,46 @@ public function mimeStrDecodingProvider() * Test, that decoding mime strings return unchanged / not broken strings. * * @dataProvider mimeStrDecodingProvider - * - * @param string $str - * @param string $expectedStr - * @param string $serverEncoding - * - * @return void */ - public function testDecodeMimeStr($str, $expectedStr, $serverEncoding = 'utf-8') + public function testDecodeMimeStr(string $str, string $expectedStr, string $serverEncoding = 'utf-8'): void { $mailbox = $this->getMailbox(); $mailbox->setServerEncoding($serverEncoding); - $this->assertEquals($mailbox->decodeMimeStr($str, $mailbox->getServerEncoding()), $expectedStr); + $this->assertEquals($mailbox->decodeMimeStr($str), $expectedStr); } /** - * @return array + * Provides test data for testing base64 string decoding. * + * @psalm-return list + */ + public function Base64DecodeProvider() + { + return [ + ['bm8tcmVwbHlAZXhhbXBsZS5jb20=', 'no-reply@example.com'], + ['TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4K', 'Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.'."\n"], + ['SSBjYW4gZWF0IGdsYXNzIGFuZCBpdCBkb2VzIG5vdCBodXJ0IG1lLg==', 'I can eat glass and it does not hurt me.'], + ['77u/4KSV4KS+4KSa4KSCIOCktuCkleCljeCkqOCli+CkruCljeCkr+CkpOCljeCkpOClgeCkruCljSDgpaQg4KSo4KWL4KSq4KS54KS/4KSo4KS44KWN4KSk4KS/IOCkruCkvuCkruCljSDgpaU=', 'काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥'], + ['SmUgcGV1eCBtYW5nZXIgZHUgdmVycmUsIMOnYSBuZSBtZSBmYWl0IHBhcyBtYWwu', 'Je peux manger du verre, ça ne me fait pas mal.'], + ['UG90IHPEgyBtxINuw6JuYyBzdGljbMSDIMiZaSBlYSBudSBtxIMgcsSDbmXImXRlLg==', 'Pot să mănânc sticlă și ea nu mă rănește.'], + ['5oiR6IO95ZCe5LiL546755KD6ICM5LiN5YK36Lqr6auU44CC', '我能吞下玻璃而不傷身體。'], + ]; + } + + /** + * @dataProvider Base64DecodeProvider + */ + public function testBase64Decode(string $input, string $expected): void + { + $this->assertSame($expected, \imap_base64(\preg_replace('~[^a-zA-Z0-9+=/]+~s', '', $input))); + $this->assertSame($expected, \base64_decode($input, false)); + } + + /** * @psalm-return list, 3:string}> */ - public function attachmentDirFailureProvider() + public function attachmentDirFailureProvider(): array { return [ [ @@ -798,14 +730,9 @@ public function attachmentDirFailureProvider() * * @dataProvider attachmentDirFailureProvider * - * @param string $initialDir - * @param string $attachmentsDir - * @param string $expectedException - * @param string $expectedExceptionMessage - * * @psalm-param class-string<\Exception> $expectedException */ - public function testAttachmentDirFailure($initialDir, $attachmentsDir, $expectedException, $expectedExceptionMessage) + public function testAttachmentDirFailure(string $initialDir, string $attachmentsDir, string $expectedException, string $expectedExceptionMessage): void { $mailbox = new Mailbox('', '', '', $initialDir); @@ -817,10 +744,7 @@ public function testAttachmentDirFailure($initialDir, $attachmentsDir, $expected $mailbox->setAttachmentsDir($attachmentsDir); } - /** - * @return Mailbox - */ - protected function getMailbox() + protected function getMailbox(): Mailbox { return new Mailbox($this->imapPath, $this->login, $this->password, $this->attachmentsDir, $this->serverEncoding); } diff --git a/tests/unit/RequirementsTest.php b/tests/unit/RequirementsTest.php deleted file mode 100644 index 1319e8d7..00000000 --- a/tests/unit/RequirementsTest.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ - public function extensionProvider() - { - return [ - 'imap' => ['imap'], - 'mbstring' => ['mbstring'], - 'iconv' => ['iconv'], - ]; - } - - /** - * Test, that required modules are enabled. - * - * @dataProvider extensionProvider - * - * @param string $extension - * - * @return void - */ - public function testRequiredExtensionsAreEnabled($extension) - { - $this->assertTrue(\extension_loaded($extension)); - } -}