Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/code_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['8.1', '8.2', '8.3', '8.4']
php: ['8.1', '8.2', '8.3', '8.4', '8.5']

name: PHP ${{ matrix.php }} tests
env:
Expand All @@ -33,6 +33,6 @@ jobs:
- name: Static analysis for source
run: ./vendor/bin/phpstan analyse -l max src
- name: Static analysis for tests
run: ./vendor/bin/phpstan analyse -l 5 -c .phpstan.test.neon test
run: ./vendor/bin/phpstan analyse -l 5 test
- name: Code Style
run: ./vendor/bin/php-cs-fixer fix --dry-run --verbose --config .php-cs-fixer.dist.php ./src ./test
2 changes: 0 additions & 2 deletions .phpstan.test.neon

This file was deleted.

15 changes: 7 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
"description": "Library to send e-mails over different transports and protocols (like SMTP and IMAP) using immutable messages and streams. Also includes SMTP server.",
"license": "MIT",
"require": {
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"ext-intl": "*",
"ext-iconv": "*"
},
"require-dev": {
"phpunit/phpunit": "^9.5.24",
"phpstan/phpstan": "^1.8.5",
"phpstan/phpstan-phpunit": "^1.1.1",
"phpstan/phpstan": "^2.0",
"friendsofphp/php-cs-fixer": "^3.0",
"predis/predis": "^1.0",
"psr/log": "^1.0",
Expand Down Expand Up @@ -38,13 +37,13 @@
},
"scripts": {
"lint": [
"./vendor/bin/php-cs-fixer fix --verbose --config .php-cs-fixer.dist.php ./src ./test"
"@php ./vendor/bin/php-cs-fixer fix --verbose --config .php-cs-fixer.dist.php ./src ./test"
],
"test": [
"./vendor/bin/phpunit -c phpunit.xml",
"./vendor/bin/php-cs-fixer fix --verbose --dry-run --config .php-cs-fixer.dist.php ./src ./test",
"./vendor/bin/phpstan analyse -l max src",
"./vendor/bin/phpstan analyse -l 5 -c .phpstan.test.neon test"
"@php ./vendor/bin/phpunit -c phpunit.xml",
"@php ./vendor/bin/php-cs-fixer fix --verbose --dry-run --config .php-cs-fixer.dist.php ./src ./test",
"@php ./vendor/bin/phpstan analyse -l max src",
"@php ./vendor/bin/phpstan analyse -l 5 test"
]
}
}
3 changes: 2 additions & 1 deletion src/AddressList.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ final class AddressList implements \Countable, \IteratorAggregate
private $addresses = [];

/**
* @param array<int, mixed> $recipients
* @param array<int, Address> $recipients
*/
public function __construct(array $recipients = [])
{
foreach ($recipients as $recipient) {
/** @phpstan-ignore-next-line */
if ($recipient instanceof Address === false) {
throw new \InvalidArgumentException('Recipient must be Address object');
}
Expand Down
1 change: 1 addition & 0 deletions src/Dkim/Sha256Signer.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function signHeaders(string $canonicalizedHeaders): string
{
/** @phpstan-ignore-next-line */
if (\openssl_sign($canonicalizedHeaders, $signature, $this->privateKey, self::SIGN_ALGORITHM)) {
/** @var string $signature */
return $signature;
}

Expand Down
25 changes: 12 additions & 13 deletions src/Header/AbstractRecipient.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,18 @@ final public static function fromSingleRecipient(string $emailAddress, string $n
*/
final public static function fromArray(array $array): AbstractRecipient
{
return new static(
new AddressList(
\array_map(
function (array $pair) {
[$emailAddress, $name] = $pair + [1 => ''];
return new Address(
new EmailAddress($emailAddress),
$name
);
},
$array
)
)
/** @var array<int, Address> $map */
$map = \array_map(
function (array $pair) {
[$emailAddress, $name] = $pair + [1 => ''];
return new Address(
new EmailAddress($emailAddress),
$name
);
},
$array
);

return new static(new AddressList($map));
}
}
190 changes: 96 additions & 94 deletions src/Protocol/Imap/MessageData/ItemList.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,121 +202,123 @@ public static function fromBytes(\Iterator $bytes): self
continue;
}

if ($state !== self::STATE_BODY) {
switch ($char) {
case '(':
$sequence = '';
if ($state === self::STATE_NONE) {
$lastKey = \array_key_last($list->list);
$sequence .= $list->list[$lastKey]->getName() . ' ';
unset($list->list[$lastKey]);
}

$sequence .= '(';
$state = self::STATE_FLAGS;
break;
case ')':
if ($state === self::STATE_FLAGS) {
$flagsItem = FlagsItem::fromString($sequence);
$list->list[$flagsItem->getName()] = $flagsItem;
$sequence = '';
$state = self::STATE_NAME;
break;
switch ($char) {
case '(':
$sequence = '';
if ($state === self::STATE_NONE) {
$lastKey = \array_key_last($list->list);
if ($lastKey === null) {
throw new \UnexpectedValueException('Empty list');
}

if ($sequence) {
$nameItem = new NameItem(\substr($sequence, 0, -1));
$list->list[$nameItem->getName()] = $nameItem;
}
$sequence .= $list->list[$lastKey]->getName() . ' ';
unset($list->list[$lastKey]);
}

return $list;
case '[':
if ($state !== self::STATE_NAME && $state !== self::STATE_NONE) {
throw new \InvalidArgumentException('Invalid character [ found');
}
$sequence .= '(';
$state = self::STATE_FLAGS;
break;
case ')':
if ($state === self::STATE_FLAGS) {
$flagsItem = FlagsItem::fromString($sequence);
$list->list[$flagsItem->getName()] = $flagsItem;
$sequence = '';
$state = self::STATE_NAME;
break;
}

if ($sequence) {
$nameItem = new NameItem(\substr($sequence, 0, -1));
$list->list[$nameItem->getName()] = $nameItem;
}

$sequence = '[';
$state = self::STATE_SECTION;
break;
case ']':
if ($state !== self::STATE_SECTION) {
throw new \InvalidArgumentException('Invalid character ] found');
}
return $list;
case '[':
if ($state !== self::STATE_NAME && $state !== self::STATE_NONE) {
throw new \InvalidArgumentException('Invalid character [ found');
}

$sectionItem = new SectionItem($list->last(), SectionList::fromString($sequence));
$list->list[$sectionItem->getName()] = $sectionItem;
$nameItem = new NameItem(\substr($sequence, 0, -1));
$list->list[$nameItem->getName()] = $nameItem;

$sequence = '';
$state = self::STATE_NAME;
break;
case '<':
if ($state !== self::STATE_NAME) {
throw new \InvalidArgumentException('Invalid character < found');
}
$sequence = '[';
$state = self::STATE_SECTION;
break;
case ']':
if ($state !== self::STATE_SECTION) {
throw new \InvalidArgumentException('Invalid character ] found');
}

$state = self::STATE_PARTIAL;
break;
case '>':
if ($state !== self::STATE_PARTIAL) {
throw new \InvalidArgumentException('Invalid character > found');
}
$sectionItem = new SectionItem($list->last(), SectionList::fromString($sequence));
$list->list[$sectionItem->getName()] = $sectionItem;

$partialItem = new PartialItem($list->last(), Partial::fromString($sequence));
$list->list[$partialItem->getName()] = $partialItem;
$sequence = '';
$state = self::STATE_NAME;
break;
case '<':
if ($state !== self::STATE_NAME) {
throw new \InvalidArgumentException('Invalid character < found');
}

$sequence = '';
$state = self::STATE_NAME;
break;
case '{':
if ($state !== self::STATE_NONE) {
throw new \InvalidArgumentException('Invalid character { found');
}
$state = self::STATE_PARTIAL;
break;
case '>':
if ($state !== self::STATE_PARTIAL) {
throw new \InvalidArgumentException('Invalid character > found');
}

$state = self::STATE_OCTET;
break;
case '}':
if ($state !== self::STATE_OCTET) {
throw new \InvalidArgumentException('Invalid characters } found');
}
$partialItem = new PartialItem($list->last(), Partial::fromString($sequence));
$list->list[$partialItem->getName()] = $partialItem;

$crlf = '';
$bytes->next();
$crlf .= $bytes->current();
$bytes->next();
$crlf .= $bytes->current();
$sequence = '';
$state = self::STATE_NAME;
break;
case '{':
if ($state !== self::STATE_NONE) {
throw new \InvalidArgumentException('Invalid character { found');
}

if ($crlf !== "\r\n") {
throw new \UnexpectedValueException('Octet is expected to be followed by a CRLF');
}
$state = self::STATE_OCTET;
break;
case '}':
if ($state !== self::STATE_OCTET) {
throw new \InvalidArgumentException('Invalid characters } found');
}

$list->size = (int)\substr($sequence, 1, -1);
$sequence = '';
$crlf = '';
$bytes->next();
$crlf .= $bytes->current();
$bytes->next();
$crlf .= $bytes->current();

$remainingBodyBytes = $list->size;
$state = self::STATE_BODY;
break;
case ' ':
if ($sequence === ' ') {
$state = self::STATE_NONE;
}
if ($crlf !== "\r\n") {
throw new \UnexpectedValueException('Octet is expected to be followed by a CRLF');
}

if ($state === self::STATE_NONE) {
$sequence = '';
}
$list->size = (int)\substr($sequence, 1, -1);
$sequence = '';

if ($state === self::STATE_NAME) {
$nameItem = new NameItem(\substr($sequence, 0, -1));
$list->list[$nameItem->getName()] = $nameItem;
$remainingBodyBytes = $list->size;
$state = self::STATE_BODY;
break;
case ' ':
if ($sequence === ' ') {
$state = self::STATE_NONE;
}

$sequence = '';
$state = self::STATE_NONE;
}
if ($state === self::STATE_NONE) {
$sequence = '';
}

break;
}
if ($state === self::STATE_NAME) {
$nameItem = new NameItem(\substr($sequence, 0, -1));
$list->list[$nameItem->getName()] = $nameItem;

$sequence = '';
$state = self::STATE_NONE;
}

break;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ public static function fromString(string $response): self
\array_fill(0, \count($advertisements), true)
);

/** @var array<string, bool>|false $list */
if ($list === false) {
throw new \UnexpectedValueException('Cannot create capability list');
}

return new self($list);
}
}
1 change: 1 addition & 0 deletions src/Protocol/PlainTcpConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public function connect(): void
);

if ($resource === false) {
/** @var int $errorCode */
throw new ConnectionRefusedException(
\sprintf('Could not create plain tcp connection. %s.', $errorMessage),
$errorCode
Expand Down
1 change: 1 addition & 0 deletions src/Protocol/SecureConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public function connect(): void
);

if ($resource === false) {
/** @var int $errorCode */
throw new ConnectionRefusedException(
\sprintf('Could not create secure connection. %s.', $errorMessage),
$errorCode
Expand Down
5 changes: 0 additions & 5 deletions src/Protocol/Smtp/Backend/ArrayBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ public function __construct(array $addresses, \ArrayAccess $backend)
\array_fill(0, \count($addresses), true)
);

/** @var array<string, bool>|false $addresses */
if ($addresses === false) {
throw new \UnexpectedValueException('Cannot combine arrays');
}

$this->addresses = $addresses;
$this->backend = $backend;
}
Expand Down
1 change: 1 addition & 0 deletions src/Quotation/FixedQuotation.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ private function prepareBody(\DOMDocument $document): \DOMElement
$body = $document->createElement('body');
$html->appendChild($body);
$body->appendChild($document->documentElement);
/** @phpstan-ignore-next-line */
if ($document->documentElement instanceof \DOMElement) {
$document->removeChild($document->documentElement);
}
Expand Down
6 changes: 1 addition & 5 deletions src/Stream/AsciiEncodedStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,13 @@ public function detach()
return $handle;
}

/**
* @return int|null
*/
public function getSize(): ?int
public function getSize(): int
{
return \strlen($this->text);
}

/**
* @return int
* @throws \RuntimeException
*/
public function tell(): int
{
Expand Down
Loading