Merge branch '4.4' into 5.0

* 4.4:
  [Mailer] fix typos
  [Messenger] fix typo
  [DI] Unknown env prefix not regornized as such
  [DI] Fix support for multiple tags for locators and iterators
  [PhpUnitBridge] Fix some errors when using serialized deprecations
  Fix HTTP client config handling
This commit is contained in:
Nicolas Grekas 2020-02-04 20:55:45 +01:00
commit 74a42d102f
15 changed files with 401 additions and 90 deletions

View File

@ -99,7 +99,19 @@ class DeprecationErrorHandler
return \call_user_func(self::getPhpUnitErrorHandler(), $type, $msg, $file, $line, $context);
}
$deprecations[] = [error_reporting(), $msg, $file];
$trace = debug_backtrace();
$filesStack = [];
foreach ($trace as $line) {
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
continue;
}
if (isset($line['file'])) {
$filesStack[] = $line['file'];
}
}
$deprecations[] = [error_reporting(), $msg, $file, $filesStack];
return null;
});

View File

@ -44,6 +44,8 @@ class Deprecation
*/
private static $internalPaths = [];
private $originalFilesStack;
/**
* @param string $message
* @param string $file
@ -64,6 +66,7 @@ class Deprecation
$this->message = $parsedMsg['deprecation'];
$this->originClass = $parsedMsg['class'];
$this->originMethod = $parsedMsg['method'];
$this->originalFilesStack = $parsedMsg['files_stack'];
// If the deprecation has been triggered via
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
// then we need to use the serialized information to determine
@ -162,6 +165,24 @@ class Deprecation
return false !== strpos($this->triggeringFile, \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR.'phpunit'.\DIRECTORY_SEPARATOR);
}
private function getOriginalFilesStack(): array
{
if (null === $this->originalFilesStack) {
$this->originalFilesStack = [];
foreach ($this->trace as $line) {
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
continue;
}
if (!isset($line['file'])) {
continue;
}
$this->originalFilesStack[] = $line['file'];
}
}
return $this->originalFilesStack;
}
/**
* Tells whether both the calling package and the called package are vendor
* packages.
@ -178,14 +199,8 @@ class Deprecation
return self::TYPE_UNDETERMINED;
}
$erroringFile = $erroringPackage = null;
foreach ($this->trace as $line) {
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
continue;
}
if (!isset($line['file'])) {
continue;
}
$file = $line['file'];
foreach ($this->getOriginalFilesStack() as $file) {
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
continue;
}

View File

@ -11,12 +11,32 @@
namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler;
use Composer\Autoload\ClassLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5;
class DeprecationTest extends TestCase
{
public static function setUpBeforeClass(): void
{
$vendorDir = self::getVendorDir();
mkdir($vendorDir.'/myfakevendor/myfakepackage1', 0777, true);
mkdir($vendorDir.'/myfakevendor/myfakepackage2');
touch($vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php');
touch($vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile2.php');
touch($vendorDir.'/myfakevendor/myfakepackage2/MyFakeFile.php');
}
private static function getVendorDir(): string
{
$reflection = new \ReflectionClass(ClassLoader::class);
return \dirname($reflection->getFileName(), 2);
}
public function testItCanDetermineTheClassWhereTheDeprecationHappened()
{
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
@ -118,12 +138,135 @@ class DeprecationTest extends TestCase
$this->assertTrue($deprecation->isMuted());
}
public function providerGetTypeDetectsSelf(): array
{
foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$v = \dirname(\dirname($r->getFileName()));
if (file_exists($v.'/composer/installed.json')) {
$loader = require $v.'/autoload.php';
$reflection = new \ReflectionClass($loader);
$prop = $reflection->getProperty('prefixDirsPsr4');
$prop->setAccessible(true);
$currentValue = $prop->getValue($loader);
$currentValue['Symfony\\Bridge\\PhpUnit\\'] = [realpath(__DIR__.'/../..')];
$prop->setValue($loader, $currentValue);
}
}
}
return [
'not_from_vendors_file' => [Deprecation::TYPE_SELF, '', 'MyClass1', ''],
'nonexistent_file' => [Deprecation::TYPE_UNDETERMINED, '', 'MyClass1', 'dummy_vendor_path'],
'serialized_trace_with_nonexistent_triggering_file' => [
Deprecation::TYPE_UNDETERMINED,
serialize([
'class' => '',
'method' => '',
'deprecation' => '',
'triggering_file' => 'dummy_vendor_path',
'files_stack' => [],
]),
SymfonyTestsListenerForV5::class,
'',
],
];
}
/**
* @dataProvider providerGetTypeDetectsSelf
*/
public function testGetTypeDetectsSelf(string $expectedType, string $message, string $traceClass, string $file): void
{
$trace = [
['class' => 'MyClass1', 'function' => 'myMethod'],
['class' => $traceClass, 'function' => 'myMethod'],
];
$deprecation = new Deprecation($message, $trace, $file);
$this->assertEquals($expectedType, $deprecation->getType());
}
public function providerGetTypeUsesRightTrace(): array
{
$vendorDir = self::getVendorDir();
return [
'no_file_in_stack' => [Deprecation::TYPE_DIRECT, '', [['function' => 'myfunc1'], ['function' => 'myfunc2']]],
'files_in_stack_from_various_packages' => [
Deprecation::TYPE_INDIRECT,
'',
[
['function' => 'myfunc1', 'file' => $vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php'],
['function' => 'myfunc2', 'file' => $vendorDir.'/myfakevendor/myfakepackage2/MyFakeFile.php'],
],
],
'serialized_stack_files_from_same_package' => [
Deprecation::TYPE_DIRECT,
serialize([
'deprecation' => 'My deprecation message',
'class' => 'MyClass',
'method' => 'myMethod',
'files_stack' => [
$vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php',
$vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile2.php',
],
]),
[['function' => 'myfunc1'], ['class' => SymfonyTestsListenerForV5::class, 'method' => 'mymethod']],
],
'serialized_stack_files_from_various_packages' => [
Deprecation::TYPE_INDIRECT,
serialize([
'deprecation' => 'My deprecation message',
'class' => 'MyClass',
'method' => 'myMethod',
'files_stack' => [
$vendorDir.'/myfakevendor/myfakepackage1/MyFakeFile1.php',
$vendorDir.'/myfakevendor/myfakepackage2/MyFakeFile.php',
],
]),
[['function' => 'myfunc1'], ['class' => SymfonyTestsListenerForV5::class, 'method' => 'mymethod']],
],
];
}
/**
* @dataProvider providerGetTypeUsesRightTrace
*/
public function testGetTypeUsesRightTrace(string $expectedType, string $message, array $trace): void
{
$deprecation = new Deprecation(
$message,
$trace,
self::getVendorDir().'/myfakevendor/myfakepackage2/MyFakeFile.php'
);
$this->assertEquals($expectedType, $deprecation->getType());
}
/**
* This method is here to simulate the extra level from the piece of code
* triggering an error to the error handler
* triggering an error to the error handler.
*/
public function debugBacktrace(): array
{
return debug_backtrace();
}
private static function removeDir($dir): void
{
$files = glob($dir.'/*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
} else {
self::removeDir($file);
}
}
rmdir($dir);
}
public static function tearDownAfterClass(): void
{
self::removeDir(self::getVendorDir().'/myfakevendor');
}
}

View File

@ -1265,7 +1265,7 @@ class Configuration implements ConfigurationInterface
if (!\is_array($config)) {
return [];
}
if (!isset($config['host'])) {
if (!isset($config['host'], $config['value']) || \count($config) > 2) {
return $config;
}
@ -1374,7 +1374,7 @@ class Configuration implements ConfigurationInterface
if (!\is_array($config)) {
return [];
}
if (!isset($config['key'])) {
if (!isset($config['key'], $config['value']) || \count($config) > 2) {
return $config;
}
@ -1404,7 +1404,7 @@ class Configuration implements ConfigurationInterface
if (!\is_array($config)) {
return [];
}
if (!isset($config['host'])) {
if (!isset($config['host'], $config['value']) || \count($config) > 2) {
return $config;
}

View File

@ -0,0 +1,22 @@
<?php
$container->loadFromExtension('framework', [
'http_client' => [
'default_options' => [
'resolve' => [
'host' => '127.0.0.1',
],
],
'scoped_clients' => [
'foo' => [
'base_uri' => 'http://example.com',
'query' => [
'key' => 'foo',
],
'resolve' => [
'host' => '127.0.0.1',
],
],
],
],
]);

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:http-client>
<framework:default-options>
<framework:resolve host="host">127.0.0.1</framework:resolve>
</framework:default-options>
<framework:scoped-client name="foo" base-uri="http://example.com">
<framework:query key="key">foo</framework:query>
<framework:resolve host="host">127.0.0.1</framework:resolve>
</framework:scoped-client>
</framework:http-client>
</framework:config>
</container>

View File

@ -0,0 +1,12 @@
framework:
http_client:
default_options:
resolve:
host: 127.0.0.1
scoped_clients:
foo:
base_uri: http://example.com
query:
key: foo
resolve:
host: 127.0.0.1

View File

@ -1358,6 +1358,21 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertSame($expected, $container->getDefinition('foo')->getArgument(2));
}
public function testHttpClientWithQueryParameterKey()
{
$container = $this->createContainerFromFile('http_client_xml_key');
$expected = [
'key' => 'foo',
];
$this->assertSame($expected, $container->getDefinition('foo')->getArgument(2)['query']);
$expected = [
'host' => '127.0.0.1',
];
$this->assertSame($expected, $container->getDefinition('foo')->getArgument(2)['resolve']);
}
public function testHttpClientFullDefaultOptions()
{
$container = $this->createContainerFromFile('http_client_full_default_options');

View File

@ -54,78 +54,81 @@ trait PriorityTaggedServiceTrait
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) {
$class = $r = null;
$priority = 0;
if (isset($attributes[0]['priority'])) {
$priority = $attributes[0]['priority'];
} elseif ($defaultPriorityMethod) {
$class = $container->getDefinition($serviceId)->getClass();
$class = $container->getParameterBag()->resolveValue($class) ?: null;
if (($r = $container->getReflectionClass($class)) && $r->hasMethod($defaultPriorityMethod)) {
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
}
$defaultPriority = null;
$defaultIndex = null;
if (!$rm->isPublic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
}
foreach ($attributes as $attribute) {
$index = $priority = null;
$priority = $rm->invoke(null);
if (isset($attribute['priority'])) {
$priority = $attribute['priority'];
} elseif (null === $defaultPriority && $defaultPriorityMethod) {
$class = $container->getDefinition($serviceId)->getClass();
$class = $container->getParameterBag()->resolveValue($class) ?: null;
if (!\is_int($priority)) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId));
if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultPriorityMethod)) {
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
}
if (!$rm->isPublic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
}
$defaultPriority = $rm->invoke(null);
if (!\is_int($defaultPriority)) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId));
}
}
}
}
if (null === $indexAttribute && !$needsIndexes) {
$services[$priority][] = new Reference($serviceId);
$priority = $priority ?? $defaultPriority ?? 0;
continue;
}
if (null !== $indexAttribute && isset($attribute[$indexAttribute])) {
$index = $attribute[$indexAttribute];
} elseif (null === $defaultIndex && null === $indexAttribute && !$needsIndexes) {
// With partially associative array, insertion to get next key is simpler.
$services[$priority][] = null;
end($services[$priority]);
$defaultIndex = key($services[$priority]);
} elseif (null === $defaultIndex && $defaultIndexMethod) {
$class = $container->getDefinition($serviceId)->getClass();
$class = $container->getParameterBag()->resolveValue($class) ?: null;
if (!$class) {
$class = $container->getDefinition($serviceId)->getClass();
$class = $container->getParameterBag()->resolveValue($class) ?: null;
}
if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultIndexMethod)) {
if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
}
if (null !== $indexAttribute && isset($attributes[0][$indexAttribute])) {
$services[$priority][$attributes[0][$indexAttribute]] = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $attributes[0][$indexAttribute]);
if (!$rm->isPublic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
}
continue;
}
$defaultIndex = $rm->invoke(null);
if (!$r && !$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $serviceId));
}
if (!\is_string($defaultIndex)) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return a string, got %s: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, \gettype($defaultIndex), $tagName, $serviceId, $indexAttribute));
}
}
$class = $r->name;
if (!$r->hasMethod($defaultIndexMethod)) {
if ($needsIndexes) {
$services[$priority][$serviceId] = new TypedReference($serviceId, $class);
continue;
$defaultIndex = $defaultIndex ?? $serviceId;
}
throw new InvalidArgumentException(sprintf('Method "%s::%s()" not found: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
$index = $index ?? $defaultIndex;
$reference = null;
if (!$class || 'stdClass' === $class) {
$reference = new Reference($serviceId);
} elseif ($index === $serviceId) {
$reference = new TypedReference($serviceId, $class);
} else {
$reference = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, \is_string($index) ? $index : null);
}
$services[$priority][$index] = $reference;
}
if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
}
if (!$rm->isPublic()) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
}
$key = $rm->invoke(null);
if (!\is_string($key)) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return a string, got %s: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, \gettype($key), $tagName, $serviceId, $indexAttribute));
}
$services[$priority][$key] = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $key);
}
if ($services) {

View File

@ -126,9 +126,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
}
if (false !== $i || 'string' !== $prefix) {
if (null === $env = $getEnv($name)) {
return null;
}
$env = $getEnv($name);
} elseif (isset($_ENV[$name])) {
$env = $_ENV[$name];
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
@ -173,12 +171,18 @@ class EnvVarProcessor implements EnvVarProcessorInterface
throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name));
}
if (null === $env = $this->container->getParameter("env($name)")) {
return null;
}
$env = $this->container->getParameter("env($name)");
}
}
if (null === $env) {
if (!isset($this->getProvidedTypes()[$prefix])) {
throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix));
}
return null;
}
if (!is_scalar($env)) {
throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to %s.', $name, $prefix));
}

View File

@ -314,6 +314,32 @@ class IntegrationTest extends TestCase
$this->assertSame(['bar_tab_class_with_defaultmethod' => $container->get(BarTagClass::class), 'foo' => $container->get(FooTagClass::class)], $param);
}
public function testTaggedIteratorWithMultipleIndexAttribute()
{
$container = new ContainerBuilder();
$container->register(BarTagClass::class)
->setPublic(true)
->addTag('foo_bar', ['foo' => 'bar'])
->addTag('foo_bar', ['foo' => 'bar_duplicate'])
;
$container->register(FooTagClass::class)
->setPublic(true)
->addTag('foo_bar')
->addTag('foo_bar')
;
$container->register(FooBarTaggedClass::class)
->addArgument(new TaggedIteratorArgument('foo_bar', 'foo'))
->setPublic(true)
;
$container->compile();
$s = $container->get(FooBarTaggedClass::class);
$param = iterator_to_array($s->getParam()->getIterator());
$this->assertSame(['bar' => $container->get(BarTagClass::class), 'bar_duplicate' => $container->get(BarTagClass::class), 'foo_tag_class' => $container->get(FooTagClass::class)], $param);
}
public function testTaggedServiceWithDefaultPriorityMethod()
{
$container = new ContainerBuilder();
@ -350,7 +376,7 @@ class IntegrationTest extends TestCase
->addTag('foo_bar')
;
$container->register('foo_bar_tagged', FooBarTaggedClass::class)
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo')))
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', null, true)))
->setPublic(true)
;
@ -369,6 +395,40 @@ class IntegrationTest extends TestCase
$this->assertSame(['bar' => $container->get('bar_tag'), 'foo_tag_class' => $container->get('foo_tag')], $same);
}
public function testTaggedServiceLocatorWithMultipleIndexAttribute()
{
$container = new ContainerBuilder();
$container->register('bar_tag', BarTagClass::class)
->setPublic(true)
->addTag('foo_bar', ['foo' => 'bar'])
->addTag('foo_bar', ['foo' => 'bar_duplicate'])
;
$container->register('foo_tag', FooTagClass::class)
->setPublic(true)
->addTag('foo_bar')
->addTag('foo_bar')
;
$container->register('foo_bar_tagged', FooBarTaggedClass::class)
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', null, true)))
->setPublic(true)
;
$container->compile();
$s = $container->get('foo_bar_tagged');
/** @var ServiceLocator $serviceLocator */
$serviceLocator = $s->getParam();
$this->assertTrue($s->getParam() instanceof ServiceLocator, sprintf('Wrong instance, should be an instance of ServiceLocator, %s given', \is_object($serviceLocator) ? \get_class($serviceLocator) : \gettype($serviceLocator)));
$same = [
'bar' => $serviceLocator->get('bar'),
'bar_duplicate' => $serviceLocator->get('bar_duplicate'),
'foo_tag_class' => $serviceLocator->get('foo_tag_class'),
];
$this->assertSame(['bar' => $container->get('bar_tag'), 'bar_duplicate' => $container->get('bar_tag'), 'foo_tag_class' => $container->get('foo_tag')], $same);
}
public function testTaggedServiceLocatorWithIndexAttributeAndDefaultMethod()
{
$container = new ContainerBuilder();
@ -381,7 +441,7 @@ class IntegrationTest extends TestCase
->addTag('foo_bar', ['foo' => 'foo'])
;
$container->register('foo_bar_tagged', FooBarTaggedClass::class)
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', 'getFooBar')))
->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo_bar', 'foo', 'getFooBar', true)))
->setPublic(true)
;

View File

@ -9,6 +9,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
use Symfony\Component\DependencyInjection\EnvVarProcessor;
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
class EnvVarProcessorTest extends TestCase
{
@ -595,4 +596,17 @@ CSV;
$this->assertSame(2, $index);
}
public function testGetEnvInvalidPrefixWithDefault()
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Unsupported env var prefix');
$processor = new EnvVarProcessor(new Container());
$processor->getEnv('unknown', 'default::FAKE', function ($name) {
$this->assertSame('default::FAKE', $name);
return null;
});
}
}

View File

@ -16,7 +16,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\SmtpEnvelope;
use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport;
use Symfony\Component\Mailer\Transport\AbstractHttpTransport;
use Symfony\Component\Mime\Address;
use Symfony\Contracts\HttpClient\HttpClientInterface;

View File

@ -57,7 +57,7 @@ class MandrillHttpTransportTest extends TestCase
$body = json_decode($options['body'], true);
$message = $body['raw_message'];
$this->assertSame('KEY', $body['key']);
$this->assertSame('Saif Eddin <saif.gmati@symfony.com>', $body['to'][0]);
$this->assertSame('saif.gmati@symfony.com', $body['to'][0]);
$this->assertSame('Fabien <fabpot@symfony.com>', $body['from_email']);
$this->assertStringContainsString('Subject: Hello!', $message);

View File

@ -47,7 +47,9 @@ class MandrillHttpTransport extends AbstractHttpTransport
$response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/api/1.0/messages/send-raw.json', [
'json' => [
'key' => $this->key,
'to' => $this->getRecipients($envelope->getRecipients()),
'to' => array_map(function (Address $recipient): string {
return $recipient->getAddress();
}, $envelope->getRecipients()),
'from_email' => $envelope->getSender()->toString(),
'raw_message' => $message->toString(),
],
@ -71,14 +73,4 @@ class MandrillHttpTransport extends AbstractHttpTransport
{
return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : '');
}
/**
* @return string[]
*/
private function getRecipients(Envelope $envelope): array
{
return array_map(function (Address $recipient): string {
return $recipient->getAddress();
}, $envelope->getRecipients());
}
}