Skip to content

Commit ed22040

Browse files
committed
Allow userland middlewares
1 parent 543ec0f commit ed22040

File tree

9 files changed

+471
-73
lines changed

9 files changed

+471
-73
lines changed

Attribute/AsMiddleware.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Doctrine\Bundle\DoctrineBundle\Attribute;
4+
5+
use Attribute;
6+
7+
#[Attribute(Attribute::TARGET_CLASS)]
8+
class AsMiddleware
9+
{
10+
/** @param string[] $connections */
11+
public function __construct(
12+
public array $connections = [],
13+
) {
14+
}
15+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
4+
5+
use Doctrine\Bundle\DoctrineBundle\Middleware\ConnectionNameAwareInterface;
6+
use Symfony\Component\DependencyInjection\ChildDefinition;
7+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
10+
use function array_keys;
11+
use function array_search;
12+
use function is_subclass_of;
13+
use function sprintf;
14+
15+
final class MiddlewaresPass implements CompilerPassInterface
16+
{
17+
public function process(ContainerBuilder $container): void
18+
{
19+
$middlewareAbstractDefs = [];
20+
$middlewareConnections = [];
21+
foreach ($container->findTaggedServiceIds('doctrine.middleware') as $id => $tags) {
22+
$middlewareAbstractDefs[$id] = $container->getDefinition($id);
23+
// When a def has doctrine.middleware tags with connection attributes equal to connection names
24+
// registration of this middleware is limited to the connections with these names
25+
foreach ($tags as $tag) {
26+
if (! isset($tag['connection'])) {
27+
continue;
28+
}
29+
30+
$middlewareConnections[$id][] = $tag['connection'];
31+
}
32+
}
33+
34+
foreach (array_keys($container->getParameter('doctrine.connections')) as $name) {
35+
$middlewareDefs = [];
36+
foreach ($middlewareAbstractDefs as $id => $abstractDef) {
37+
if (isset($middlewareConnections[$id]) && ! in_array($name, $middlewareConnections[$id], true)) {
38+
continue;
39+
}
40+
41+
$middlewareDefs[] = $childDef = $container->setDefinition(
42+
sprintf('%s.%s', $id, $name),
43+
new ChildDefinition($id)
44+
);
45+
46+
if (! is_subclass_of($abstractDef->getClass(), ConnectionNameAwareInterface::class)) {
47+
continue;
48+
}
49+
50+
$childDef->addMethodCall('setConnectionName', [$name]);
51+
}
52+
53+
$container
54+
->getDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name))
55+
->addMethodCall('setMiddlewares', [$middlewareDefs]);
56+
}
57+
}
58+
}

DependencyInjection/DoctrineExtension.php

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
44

55
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
6+
use Doctrine\Bundle\DoctrineBundle\Attribute\AsMiddleware;
67
use Doctrine\Bundle\DoctrineBundle\CacheWarmer\DoctrineMetadataCacheWarmer;
78
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\ImportDoctrineCommand;
89
use Doctrine\Bundle\DoctrineBundle\Dbal\ManagerRegistryAwareConnectionProvider;
@@ -13,8 +14,9 @@
1314
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
1415
use Doctrine\DBAL\Connection;
1516
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
16-
use Doctrine\DBAL\Driver\Middleware;
17+
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
1718
use Doctrine\DBAL\Logging\LoggerChain;
19+
use Doctrine\DBAL\Logging\Middleware;
1820
use Doctrine\DBAL\Sharding\PoolingShardConnection;
1921
use Doctrine\DBAL\Sharding\PoolingShardManager;
2022
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
@@ -44,7 +46,6 @@
4446
use Symfony\Component\DependencyInjection\Alias;
4547
use Symfony\Component\DependencyInjection\ChildDefinition;
4648
use Symfony\Component\DependencyInjection\ContainerBuilder;
47-
use Symfony\Component\DependencyInjection\ContainerInterface;
4849
use Symfony\Component\DependencyInjection\Definition;
4950
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
5051
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@@ -65,6 +66,8 @@
6566
use function sprintf;
6667
use function str_replace;
6768

69+
use const PHP_VERSION_ID;
70+
6871
/**
6972
* DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
7073
*/
@@ -143,9 +146,33 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
143146
$container->setParameter('doctrine.connections', $connections);
144147
$container->setParameter('doctrine.default_connection', $this->defaultConnection);
145148

149+
$connWithLogging = [];
146150
foreach ($config['connections'] as $name => $connection) {
151+
if ($connection['logging']) {
152+
$connWithLogging[] = $name;
153+
}
154+
147155
$this->loadDbalConnection($name, $connection, $container);
148156
}
157+
158+
/** @psalm-suppress UndefinedClass */
159+
$container->registerForAutoconfiguration(MiddlewareInterface::class)->addTag('doctrine.middleware');
160+
161+
if (PHP_VERSION_ID >= 80000 && method_exists(ContainerBuilder::class, 'registerAttributeForAutoconfiguration')) {
162+
$container->registerAttributeForAutoconfiguration(AsMiddleware::class, static function (ChildDefinition $definition, AsMiddleware $attribute) {
163+
if ($attribute->connections === []) {
164+
$definition->addTag('doctrine.middleware');
165+
166+
return;
167+
}
168+
169+
foreach ($attribute->connections as $connName) {
170+
$definition->addTag('doctrine.middleware', ['connection' => $connName]);
171+
}
172+
});
173+
}
174+
175+
$this->useMiddlewaresIfAvailable($container, $connWithLogging);
149176
}
150177

151178
/**
@@ -160,7 +187,6 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder
160187
$configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
161188
$logger = null;
162189
if ($connection['logging']) {
163-
$this->useMiddlewaresIfAvailable($connection, $container, $name, $configuration);
164190
$logger = new Reference('doctrine.dbal.logger');
165191
}
166192

@@ -1073,25 +1099,24 @@ private function createArrayAdapterCachePool(ContainerBuilder $container, string
10731099
return $id;
10741100
}
10751101

1076-
/** @param array<string, mixed> $connection */
1077-
protected function useMiddlewaresIfAvailable(array $connection, ContainerBuilder $container, string $name, Definition $configuration): void
1102+
/** @param string[] $connWithLogging */
1103+
private function useMiddlewaresIfAvailable(ContainerBuilder $container, array $connWithLogging): void
10781104
{
10791105
/** @psalm-suppress UndefinedClass */
1080-
if (! interface_exists(Middleware::class)) {
1106+
if (! class_exists(Middleware::class)) {
10811107
return;
10821108
}
10831109

10841110
$container
10851111
->getDefinition('doctrine.dbal.logger')
10861112
->replaceArgument(0, null);
10871113

1088-
$loggingMiddlewareDef = $container->setDefinition(
1089-
sprintf('doctrine.dbal.%s_connection.logging_middleware', $name),
1090-
new ChildDefinition('doctrine.dbal.logging_middleware')
1091-
);
1092-
$loggingMiddlewareDef->addArgument(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE));
1093-
$loggingMiddlewareDef->addTag('monolog.logger', ['channel' => 'doctrine']);
1114+
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
1115+
$loader->load('middlewares.xml');
10941116

1095-
$configuration->addMethodCall('setMiddlewares', [[$loggingMiddlewareDef]]);
1117+
$loggingMiddlewareAbstractDef = $container->getDefinition('doctrine.dbal.logging_middleware');
1118+
foreach ($connWithLogging as $connName) {
1119+
$loggingMiddlewareAbstractDef->addTag('doctrine.middleware', ['connection' => $connName]);
1120+
}
10961121
}
10971122
}

DoctrineBundle.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DbalSchemaFilterPass;
88
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
99
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\IdGeneratorPass;
10+
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\MiddlewaresPass;
1011
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\RemoveProfilerControllerPass;
1112
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
1213
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\WellKnownSchemaFilterPass;
1314
use Doctrine\Common\Util\ClassUtils;
15+
use Doctrine\DBAL\Driver\Middleware;
1416
use Doctrine\ORM\EntityManagerInterface;
1517
use Doctrine\ORM\Proxy\Autoloader;
1618
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\DoctrineValidationPass;
@@ -26,6 +28,7 @@
2628
use function assert;
2729
use function class_exists;
2830
use function clearstatcache;
31+
use function interface_exists;
2932
use function spl_autoload_unregister;
3033

3134
class DoctrineBundle extends Bundle
@@ -60,6 +63,11 @@ public function build(ContainerBuilder $container)
6063
$container->addCompilerPass(new CacheSchemaSubscriberPass(), PassConfig::TYPE_BEFORE_REMOVING, -10);
6164
$container->addCompilerPass(new RemoveProfilerControllerPass());
6265

66+
/** @psalm-suppress UndefinedClass */
67+
if (interface_exists(Middleware::class)) {
68+
$container->addCompilerPass(new MiddlewaresPass());
69+
}
70+
6371
if (! class_exists(RegisterUidTypePass::class)) {
6472
return;
6573
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
4+
5+
interface ConnectionNameAwareInterface
6+
{
7+
public function setConnectionName(string $name): void;
8+
}

Resources/config/dbal.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,6 @@
3636
<argument type="service" id="debug.stopwatch" on-invalid="null" />
3737
</service>
3838

39-
<service id="doctrine.dbal.logging_middleware" class="Doctrine\DBAL\Logging\Middleware" abstract="true">
40-
</service>
41-
4239
<service id="data_collector.doctrine" class="%doctrine.data_collector.class%" public="false">
4340
<tag name="data_collector" template="@Doctrine/Collector/db.html.twig" id="db" priority="250" />
4441
<argument type="service" id="doctrine" />

Resources/config/middlewares.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="doctrine.dbal.logging_middleware" class="Doctrine\DBAL\Logging\Middleware" abstract="true">
9+
<argument type="service" id="logger" on-invalid="null" />
10+
<tag name="monolog.logger" channel="doctrine" />
11+
<tag name="doctrine.middleware" />
12+
</service>
13+
</services>
14+
</container>

0 commit comments

Comments
 (0)