Merge branch '5.0'

* 5.0:
  [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:57:28 +01:00
commit 0db2b0a4bc
14 changed files with 400 additions and 89 deletions

View File

@ -96,7 +96,19 @@ class DeprecationErrorHandler
return \call_user_func(self::getPhpUnitErrorHandler(), $type, $msg, $file, $line, $context); 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; return null;
}); });

View File

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

View File

@ -11,12 +11,32 @@
namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler; namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler;
use Composer\Autoload\ClassLoader;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5;
class DeprecationTest extends TestCase 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() public function testItCanDetermineTheClassWhereTheDeprecationHappened()
{ {
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__); $deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
@ -118,12 +138,135 @@ class DeprecationTest extends TestCase
$this->assertTrue($deprecation->isMuted()); $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 * 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 public function debugBacktrace(): array
{ {
return debug_backtrace(); 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

@ -1279,7 +1279,7 @@ class Configuration implements ConfigurationInterface
if (!\is_array($config)) { if (!\is_array($config)) {
return []; return [];
} }
if (!isset($config['host'])) { if (!isset($config['host'], $config['value']) || \count($config) > 2) {
return $config; return $config;
} }
@ -1388,7 +1388,7 @@ class Configuration implements ConfigurationInterface
if (!\is_array($config)) { if (!\is_array($config)) {
return []; return [];
} }
if (!isset($config['key'])) { if (!isset($config['key'], $config['value']) || \count($config) > 2) {
return $config; return $config;
} }
@ -1418,7 +1418,7 @@ class Configuration implements ConfigurationInterface
if (!\is_array($config)) { if (!\is_array($config)) {
return []; return [];
} }
if (!isset($config['host'])) { if (!isset($config['host'], $config['value']) || \count($config) > 2) {
return $config; 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

@ -1360,6 +1360,21 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertSame($expected, $container->getDefinition('foo')->getArgument(2)); $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() public function testHttpClientFullDefaultOptions()
{ {
$container = $this->createContainerFromFile('http_client_full_default_options'); $container = $this->createContainerFromFile('http_client_full_default_options');

View File

@ -54,78 +54,81 @@ trait PriorityTaggedServiceTrait
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) { foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) {
$class = $r = null; $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)) { $defaultPriority = null;
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) { $defaultIndex = null;
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
}
if (!$rm->isPublic()) { foreach ($attributes as $attribute) {
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId)); $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)) { if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultPriorityMethod)) {
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 (!($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) { $priority = $priority ?? $defaultPriority ?? 0;
$services[$priority][] = new Reference($serviceId);
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) { if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultIndexMethod)) {
$class = $container->getDefinition($serviceId)->getClass(); if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) {
$class = $container->getParameterBag()->resolveValue($class) ?: null; 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])) { if (!$rm->isPublic()) {
$services[$priority][$attributes[0][$indexAttribute]] = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $attributes[0][$indexAttribute]); 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)) { if (!\is_string($defaultIndex)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $serviceId)); 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; $defaultIndex = $defaultIndex ?? $serviceId;
if (!$r->hasMethod($defaultIndexMethod)) {
if ($needsIndexes) {
$services[$priority][$serviceId] = new TypedReference($serviceId, $class);
continue;
} }
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) { if ($services) {

View File

@ -126,9 +126,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
} }
if (false !== $i || 'string' !== $prefix) { if (false !== $i || 'string' !== $prefix) {
if (null === $env = $getEnv($name)) { $env = $getEnv($name);
return null;
}
} elseif (isset($_ENV[$name])) { } elseif (isset($_ENV[$name])) {
$env = $_ENV[$name]; $env = $_ENV[$name];
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { } 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)); throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name));
} }
if (null === $env = $this->container->getParameter("env($name)")) { $env = $this->container->getParameter("env($name)");
return null;
}
} }
} }
if (null === $env) {
if (!isset($this->getProvidedTypes()[$prefix])) {
throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix));
}
return null;
}
if (!is_scalar($env)) { if (!is_scalar($env)) {
throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to %s.', $name, $prefix)); 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); $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() public function testTaggedServiceWithDefaultPriorityMethod()
{ {
$container = new ContainerBuilder(); $container = new ContainerBuilder();
@ -350,7 +376,7 @@ class IntegrationTest extends TestCase
->addTag('foo_bar') ->addTag('foo_bar')
; ;
$container->register('foo_bar_tagged', FooBarTaggedClass::class) $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) ->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); $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() public function testTaggedServiceLocatorWithIndexAttributeAndDefaultMethod()
{ {
$container = new ContainerBuilder(); $container = new ContainerBuilder();
@ -381,7 +441,7 @@ class IntegrationTest extends TestCase
->addTag('foo_bar', ['foo' => 'foo']) ->addTag('foo_bar', ['foo' => 'foo'])
; ;
$container->register('foo_bar_tagged', FooBarTaggedClass::class) $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) ->setPublic(true)
; ;

View File

@ -9,6 +9,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface; use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
use Symfony\Component\DependencyInjection\EnvVarProcessor; use Symfony\Component\DependencyInjection\EnvVarProcessor;
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
class EnvVarProcessorTest extends TestCase class EnvVarProcessorTest extends TestCase
{ {
@ -595,4 +596,17 @@ CSV;
$this->assertSame(2, $index); $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\Exception\TransportException;
use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\SmtpEnvelope; 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\Component\Mime\Address;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;

View File

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