Skip to content
Open
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
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
}
},
"require": {
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"php": "~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0",
"ext-fileinfo": "*",
"ext-json": "*",
"dotkernel/dot-event": "^4.0",
"laminas/laminas-servicemanager": "^3.22 || ^4.0",
"laminas/laminas-stdlib": "^3.19",
"symfony/mailer": "^v7.1.6"
},
"require-dev": {
Expand Down
4 changes: 3 additions & 1 deletion src/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,9 @@ private function createAddresses(array $addresses, ?string $name = null): array
{
$createdAddresses = [];
foreach ($addresses as $address) {
$createdAddresses[] = new Address($address, $name ?? '');
$createdAddresses[] = $address instanceof Address
? $address
: new Address($address, $name ?? '');
}

return $createdAddresses;
Expand Down
14 changes: 5 additions & 9 deletions src/Event/MailEventListenerAwareTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Laminas\EventManager\EventManagerAwareTrait;

use function array_search;

trait MailEventListenerAwareTrait
{
use EventManagerAwareTrait;
Expand All @@ -22,16 +24,10 @@ public function detachListener(MailEventListenerInterface $listener): void
{
$listener->detach($this->getEventManager());

$idx = 0;
foreach ($this->listeners as $l) {
if ($l === $listener) {
break;
}

$idx++;
$idx = array_search($listener, $this->listeners, true);
if ($idx !== false) {
unset($this->listeners[$idx]);
}

unset($this->listeners[$idx]);
}

public function clearListeners(): void
Expand Down
2 changes: 1 addition & 1 deletion src/Factory/AbstractMailFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function canCreate(ContainerInterface $container, $requestedName): bool
{
$parts = explode('.', $requestedName);

if ($parts[0] !== self::DOT_MAIL_PART || $parts[1] !== static::SPECIFIC_PART || count($parts) !== 3) {
if (count($parts) !== 3 || $parts[0] !== self::DOT_MAIL_PART || $parts[1] !== static::SPECIFIC_PART) {
return false;
}

Expand Down
9 changes: 5 additions & 4 deletions src/Factory/MailServiceAbstractFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ protected function createTransport(ContainerInterface $container): TransportInte
return $this->setupTransportConfig($transport);
} else {
throw new InvalidArgumentException(
'Provided mail_adapter service does not return a ' . TransportInterface::class . ' instance'
'Provided transport service does not return a ' . TransportInterface::class . ' instance'
);
}
}
Expand All @@ -169,9 +169,9 @@ protected function createTransport(ContainerInterface $container): TransportInte
//the adapter is not valid - throw exception
throw new InvalidArgumentException(
sprintf(
'mail_adapter must be an instance of %s or string, "%s" provided',
'"transport" must be a known alias, a container service or a class implementing %s; "%s" provided',
TransportInterface::class,
gettype($adapter)
$adapter
)
);
}
Expand Down Expand Up @@ -230,7 +230,8 @@ protected function getListenerObject(ContainerInterface $container, string $type

if (! $listener instanceof MailEventListenerInterface) {
throw new RuntimeException(sprintf(
'Mail event listener must be an instance of `%s`, but `%s was given`',
'Mail event listener "%s" must be an instance of `%s`, but `%s` was given',
$type,
MailEventListenerInterface::class,
is_object($listener) ? $listener::class : gettype($listener)
));
Expand Down
6 changes: 3 additions & 3 deletions src/Options/MailOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public function getTransport(): string|TransportInterface

public function setTransport(string|TransportInterface $transport): void
{
if (is_string($transport) && array_key_exists(strtolower($transport), $this->transportMap)) {
$transport = $this->transportMap[$transport];
foreach ($transport as $class) {
$key = is_string($transport) ? strtolower($transport) : null;
if ($key !== null && array_key_exists($key, $this->transportMap)) {
foreach ($this->transportMap[$key] as $class) {
if (class_exists($class)) {
$transport = $class;
break;
Expand Down
2 changes: 1 addition & 1 deletion src/Options/SmtpOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function setPort(int $port): static
{
if ($port < 1) {
throw new InvalidArgumentException(sprintf(
'Port must be greater than 1; received "%d"',
'Port must be greater than 0; received "%d"',
$port
));
}
Expand Down
19 changes: 19 additions & 0 deletions test/EmailTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,23 @@ public function testEnsureValidity(): void
$this->expectException(LogicException::class);
$subject->ensureValidity();
}

public function testAddressObjectsInArraysAreKept(): void
{
$subject = new Email();

$subject->addTo([new Address('first@example.com', 'First'), 'second@example.com'], 'Ignored');
$subject->setCc([new Address('cc@example.com', 'Copy')]);

$to = $subject->getTo();
$this->assertCount(2, $to);
$this->assertSame('first@example.com', $to[0]->getAddress());
$this->assertSame('First', $to[0]->getName());
$this->assertSame('second@example.com', $to[1]->getAddress());
$this->assertSame('Ignored', $to[1]->getName());

$cc = $subject->getCc();
$this->assertCount(1, $cc);
$this->assertSame('Copy', $cc[0]->getName());
}
}
19 changes: 19 additions & 0 deletions test/Event/MailEventListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,23 @@ public function testListenerDetachingAndClearing(): void
$listeners = $listenersProperty->getValue($trait);
$this->assertEmpty($listeners);
}

/**
* @throws Exception
* @throws ReflectionException
*/
public function testDetachingUnknownListenerKeepsOthers(): void
{
$trait = new class {
use MailEventListenerAwareTrait;
};
$attached = $this->createMock(MailEventListenerInterface::class);
$unknown = $this->createMock(MailEventListenerInterface::class);

$trait->attachListener($attached);
$trait->detachListener($unknown);

$listenersProperty = (new ReflectionObject($trait))->getProperty('listeners');
$this->assertSame([$attached], $listenersProperty->getValue($trait));
}
}
25 changes: 25 additions & 0 deletions test/Factory/AbstractMailFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
use Dot\Mail\Factory\AbstractMailFactory;
use Dot\Mail\Options\MailOptions;
use DotTest\Mail\CommonTrait;
use ErrorException;
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

use function restore_error_handler;
use function set_error_handler;

class AbstractMailFactoryTest extends TestCase
{
use CommonTrait;
Expand Down Expand Up @@ -77,4 +81,25 @@ public function testWillNotCreateRequestedNameFromWrongParts(): void
$result = $this->subject->canCreate($container, $requestedName);
$this->assertFalse($result);
}

/**
* @throws Exception
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function testWillNotCreateRequestedNameWithoutParts(): void
{
$container = $this->createMock(ContainerInterface::class);

set_error_handler(static function (int $severity, string $message): never {
throw new ErrorException($message, 0, $severity);
});

try {
$this->assertFalse($this->subject->canCreate($container, 'dot-mail'));
$this->assertFalse($this->subject->canCreate($container, 'dot-mail.testPart'));
} finally {
restore_error_handler();
}
}
}
2 changes: 2 additions & 0 deletions test/Factory/MailServiceAbstractFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ public function testGenerateServiceWithoutProvidedAdapter(): void
->willReturn($this->smtpOptions);

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Mail event listener "Invalid Listener Test" must be an instance of');

$mailService = ($this->subject)($this->container, $requestedName);

Expand Down Expand Up @@ -226,6 +227,7 @@ public function testGenerateServiceInvalidAdapter(): void
]);

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessageMatches('/"transport" must be a known alias.*"test" provided/');
($this->subject)($this->container, $requestedName);
}

Expand Down
15 changes: 15 additions & 0 deletions test/Options/MailOptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Dot\Mail\Options\MessageOptions;
use Dot\Mail\Options\SmtpOptions;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Transport\SendmailTransport;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;

class MailOptionsTest extends TestCase
Expand All @@ -35,4 +36,18 @@ public function testGettersAndSetters(): void
$this->assertContainsOnlyInstancesOf(SmtpOptions::class, [$subject->getSmtpOptions()]);
$this->assertSame($eventListeners, $subject->getEventListeners());
}

public function testTransportAliasIsCaseInsensitive(): void
{
$subject = new MailOptions();

$subject->setTransport('ESMTP');
$this->assertSame(EsmtpTransport::class, $subject->getTransport());

$subject->setTransport('SendMail');
$this->assertSame(SendmailTransport::class, $subject->getTransport());

$subject->setTransport('Custom\\Transport');
$this->assertSame('Custom\\Transport', $subject->getTransport());
}
}
12 changes: 12 additions & 0 deletions test/Options/SmtpOptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@ public function testSmtpGettersAndSetters(): void
$this->expectException(InvalidArgumentException::class);
$subject->setPort(0);
}

public function testPortBoundaries(): void
{
$subject = new SmtpOptions();

$subject->setPort(1);
$this->assertSame(1, $subject->getPort());

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Port must be greater than 0; received "0"');
$subject->setPort(0);
}
}
Loading