-
-
Notifications
You must be signed in to change notification settings - Fork 455
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
289 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler; | ||
|
||
use Doctrine\Bundle\DoctrineBundle\Middleware\ConnectionNameAwareInterface; | ||
use Symfony\Component\DependencyInjection\ChildDefinition; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
|
||
use function array_keys; | ||
use function is_subclass_of; | ||
use function sprintf; | ||
|
||
final class MiddlewaresPass implements CompilerPassInterface | ||
{ | ||
/** @var string */ | ||
private $connectionDefsParam; | ||
|
||
/** @var string */ | ||
private $middlewareTag; | ||
|
||
public function __construct( | ||
string $connectionDefsParam = 'doctrine.connections', | ||
string $middlewareTag = 'doctrine.middleware' | ||
) { | ||
$this->connectionDefsParam = $connectionDefsParam; | ||
$this->middlewareTag = $middlewareTag; | ||
} | ||
|
||
public function process(ContainerBuilder $container): void | ||
{ | ||
$middlewareAbstractDefs = []; | ||
foreach (array_keys($container->findTaggedServiceIds($this->middlewareTag)) as $id) { | ||
$middlewareAbstractDefs[$id] = $container->getDefinition($id); | ||
} | ||
|
||
foreach ($container->getParameter($this->connectionDefsParam) as $name => $id) { | ||
$middlewareDefs = []; | ||
foreach ($middlewareAbstractDefs as $id => $abstractDef) { | ||
$middlewareDefs[] = $childDef = $container->setDefinition( | ||
sprintf('%s.%s', $id, $name), | ||
new ChildDefinition($id) | ||
); | ||
|
||
if (! is_subclass_of($abstractDef->getClass(), ConnectionNameAwareInterface::class)) { | ||
continue; | ||
} | ||
|
||
$childDef->addMethodCall('setConnectionName', [$name]); | ||
} | ||
|
||
$container | ||
->getDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name)) | ||
->addMethodCall('setMiddlewares', [$middlewareDefs]); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\Middleware; | ||
|
||
interface ConnectionNameAwareInterface | ||
{ | ||
public function setConnectionName(string $name): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" ?> | ||
|
||
<container xmlns="http://symfony.com/schema/dic/services" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> | ||
|
||
<services> | ||
<service id="doctrine.dbal.logging_middleware" class="Doctrine\DBAL\Logging\Middleware" abstract="true"> | ||
<argument type="service" id="logger" on-invalid="null" /> | ||
<tag name="monolog.logger" channel="doctrine" /> | ||
<tag name="doctrine.middleware" /> | ||
</service> | ||
</services> | ||
</container> |
171 changes: 171 additions & 0 deletions
171
Tests/DependencyInjection/Compiler/MiddlewarePassTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Compiler; | ||
|
||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\MiddlewaresPass; | ||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; | ||
use Doctrine\Bundle\DoctrineBundle\Middleware\ConnectionNameAwareInterface; | ||
use Doctrine\DBAL\Driver; | ||
use Doctrine\DBAL\Driver\Middleware; | ||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; | ||
|
||
use function interface_exists; | ||
use function sprintf; | ||
|
||
class MiddlewarePassTest extends TestCase | ||
{ | ||
/** @return array<string, mixed[]> */ | ||
public function provideAddMiddleware(): array | ||
{ | ||
return [ | ||
'not connection name aware' => [Middleware1::class, false], | ||
'connection name aware' => [Middleware2::class, true], | ||
]; | ||
} | ||
|
||
/** @dataProvider provideAddMiddleware */ | ||
public function testAddMiddleware(string $middlewareClass, bool $connectionNameAware): void | ||
{ | ||
/** @psalm-suppress UndefinedClass */ | ||
if (! interface_exists(Middleware::class)) { | ||
$this->markTestSkipped(sprintf('%s needed to run this test', Middleware::class)); | ||
} | ||
|
||
$container = $this->createContainer(static function (ContainerBuilder $container) use ($middlewareClass) { | ||
$container | ||
->register('middleware', $middlewareClass) | ||
->setAbstract(true) | ||
->addTag('doctrine.middleware'); | ||
|
||
$container | ||
->setAlias('conf_conn1', 'doctrine.dbal.conn1_connection.configuration') | ||
->setPublic(true); // Avoid removal and inlining | ||
|
||
$container | ||
->setAlias('conf_conn2', 'doctrine.dbal.conn2_connection.configuration') | ||
->setPublic(true); // Avoid removal and inlining | ||
}); | ||
|
||
$this->assertMiddlewareInjected('conn1', $middlewareClass, $connectionNameAware, $container); | ||
$this->assertMiddlewareInjected('conn2', $middlewareClass, $connectionNameAware, $container); | ||
} | ||
|
||
public function testAddMiddlewareWithAutoconfigure(): void | ||
{ | ||
/** @psalm-suppress UndefinedClass */ | ||
if (! interface_exists(Middleware::class)) { | ||
$this->markTestSkipped(sprintf('%s needed to run this test', Middleware::class)); | ||
} | ||
|
||
$container = $this->createContainer(static function (ContainerBuilder $container) { | ||
/** @psalm-suppress UndefinedClass */ | ||
$container | ||
->register('middleware', Middleware3::class) | ||
->setAutoconfigured(true); | ||
|
||
$container | ||
->setAlias('conf_conn1', 'doctrine.dbal.conn1_connection.configuration') | ||
->setPublic(true); // Avoid removal and inlining | ||
|
||
$container | ||
->setAlias('conf_conn2', 'doctrine.dbal.conn2_connection.configuration') | ||
->setPublic(true); // Avoid removal and inlining | ||
}); | ||
|
||
/** @psalm-suppress UndefinedClass */ | ||
$this->assertMiddlewareInjected('conn1', Middleware3::class, false, $container); | ||
/** @psalm-suppress UndefinedClass */ | ||
$this->assertMiddlewareInjected('conn2', Middleware3::class, false, $container); | ||
} | ||
|
||
private function createContainer(callable $func): ContainerBuilder | ||
{ | ||
$container = new ContainerBuilder(new ParameterBag(['kernel.debug' => false])); | ||
|
||
$container->registerExtension(new DoctrineExtension()); | ||
$container->loadFromExtension('doctrine', [ | ||
'dbal' => [ | ||
'connections' => [ | ||
'conn1' => ['url' => 'mysql://user:pass@server1.tld:3306/db1'], | ||
'conn2' => ['url' => 'mysql://user:pass@server2.tld:3306/db2'], | ||
], | ||
], | ||
]); | ||
|
||
$container->addCompilerPass(new MiddlewaresPass()); | ||
|
||
$func($container); | ||
|
||
$container->compile(); | ||
|
||
return $container; | ||
} | ||
|
||
private function assertMiddlewareInjected( | ||
string $connName, | ||
string $middlewareClass, | ||
bool $connectionNameAware, | ||
ContainerBuilder $container | ||
): void { | ||
$calls = $container->getDefinition('conf_' . $connName)->getMethodCalls(); | ||
$middlewareFound = []; | ||
foreach ($calls as $call) { | ||
if ($call[0] !== 'setMiddlewares' || ! isset($call[1][0])) { | ||
continue; | ||
} | ||
|
||
foreach ($call[1][0] as $middlewareDefs) { | ||
if ($middlewareDefs->getClass() !== $middlewareClass) { | ||
continue; | ||
} | ||
|
||
$middlewareFound[] = $middlewareDefs; | ||
} | ||
} | ||
|
||
$this->assertCount(1, $middlewareFound, sprintf( | ||
'Middleware not injected in doctrine.dbal.%s_connection.configuration', | ||
$connName | ||
)); | ||
|
||
$callsFound = []; | ||
foreach ($middlewareFound[0]->getMethodCalls() as $call) { | ||
if ($call[0] !== 'setConnectionName') { | ||
continue; | ||
} | ||
|
||
$callsFound[] = $call; | ||
} | ||
|
||
$this->assertCount($connectionNameAware ? 1 : 0, $callsFound); | ||
if (! $connectionNameAware) { | ||
return; | ||
} | ||
|
||
$this->assertSame($call[1][0] ?? null, $connName); | ||
} | ||
} | ||
|
||
class Middleware1 | ||
{ | ||
} | ||
|
||
class Middleware2 implements ConnectionNameAwareInterface | ||
{ | ||
public function setConnectionName(string $name): void | ||
{ | ||
} | ||
} | ||
|
||
/** @psalm-suppress UndefinedClass */ | ||
if (interface_exists(Middleware::class)) { | ||
class Middleware3 implements Middleware | ||
{ | ||
public function wrap(Driver $driver): Driver | ||
{ | ||
return $driver; | ||
} | ||
} | ||
} |
Oops, something went wrong.