Skip to content

Commit 9583584

Browse files
l-voostrolucky
authored andcommitted
Replace DbalLogger by DebugMiddleware
1 parent f9339b2 commit 9583584

File tree

10 files changed

+704
-40
lines changed

10 files changed

+704
-40
lines changed

DataCollector/DoctrineDataCollector.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Doctrine\Persistence\ManagerRegistry;
1414
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
1515
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector as BaseCollector;
16+
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
1617
use Symfony\Component\HttpFoundation\Request;
1718
use Symfony\Component\HttpFoundation\Response;
1819
use Throwable;
@@ -64,12 +65,18 @@ class DoctrineDataCollector extends BaseCollector
6465
/** @var bool */
6566
private $shouldValidateSchema;
6667

67-
public function __construct(ManagerRegistry $registry, bool $shouldValidateSchema = true)
68+
/** @psalm-suppress UndefinedClass */
69+
public function __construct(ManagerRegistry $registry, bool $shouldValidateSchema = true, ?DebugDataHolder $debugDataHolder = null)
6870
{
6971
$this->registry = $registry;
7072
$this->shouldValidateSchema = $shouldValidateSchema;
7173

72-
parent::__construct($registry);
74+
if ($debugDataHolder === null) {
75+
parent::__construct($registry);
76+
} else {
77+
/** @psalm-suppress TooManyArguments */
78+
parent::__construct($registry, $debugDataHolder);
79+
}
7380
}
7481

7582
/**

DependencyInjection/DoctrineExtension.php

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
1717
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
1818
use Doctrine\DBAL\Logging\LoggerChain;
19-
use Doctrine\DBAL\Logging\Middleware;
2019
use Doctrine\DBAL\Sharding\PoolingShardConnection;
2120
use Doctrine\DBAL\Sharding\PoolingShardManager;
2221
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
@@ -34,6 +33,7 @@
3433
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
3534
use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerWorkerSubscriber;
3635
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
36+
use Symfony\Bridge\Doctrine\Middleware\Debug\Middleware as SfDebugMiddleware;
3737
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
3838
use Symfony\Bridge\Doctrine\SchemaListener\DoctrineDbalCacheAdapterSchemaSubscriber;
3939
use Symfony\Bridge\Doctrine\SchemaListener\MessengerTransportDoctrineSchemaSubscriber;
@@ -116,9 +116,6 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
116116
{
117117
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
118118
$loader->load('dbal.xml');
119-
$chainLogger = $container->getDefinition('doctrine.dbal.logger.chain');
120-
$logger = new Reference('doctrine.dbal.logger');
121-
$chainLogger->addArgument([$logger]);
122119

123120
if (class_exists(ImportCommand::class)) {
124121
$container->register('doctrine.database_import_command', ImportDoctrineCommand::class)
@@ -147,12 +144,22 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
147144
$container->setParameter('doctrine.connections', $connections);
148145
$container->setParameter('doctrine.default_connection', $this->defaultConnection);
149146

150-
$connWithLogging = [];
147+
$connWithLogging = [];
148+
$connWithProfiling = [];
149+
$connWithBacktrace = [];
151150
foreach ($config['connections'] as $name => $connection) {
152151
if ($connection['logging']) {
153152
$connWithLogging[] = $name;
154153
}
155154

155+
if ($connection['profiling']) {
156+
$connWithProfiling[] = $name;
157+
158+
if ($connection['profiling_collect_backtrace']) {
159+
$connWithBacktrace[] = $name;
160+
}
161+
}
162+
156163
$this->loadDbalConnection($name, $connection, $container);
157164
}
158165

@@ -173,7 +180,7 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
173180
});
174181
}
175182

176-
$this->useMiddlewaresIfAvailable($container, $connWithLogging);
183+
$this->useMiddlewaresIfAvailable($container, $connWithLogging, $connWithProfiling, $connWithBacktrace);
177184
}
178185

179186
/**
@@ -187,7 +194,9 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder
187194
{
188195
$configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
189196
$logger = null;
190-
if ($connection['logging']) {
197+
198+
/** @psalm-suppress UndefinedClass */
199+
if (! interface_exists(MiddlewareInterface::class) && $connection['logging']) {
191200
$logger = new Reference('doctrine.dbal.logger');
192201
}
193202

@@ -196,7 +205,7 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder
196205
$dataCollectorDefinition = $container->getDefinition('data_collector.doctrine');
197206
$dataCollectorDefinition->replaceArgument(1, $connection['profiling_collect_schema_errors']);
198207

199-
if ($connection['profiling']) {
208+
if (! $this->isSfDebugMiddlewareAvailable() && $connection['profiling']) {
200209
$profilingAbstractId = $connection['profiling_collect_backtrace'] ?
201210
'doctrine.dbal.logger.backtrace' :
202211
'doctrine.dbal.logger.profiling';
@@ -1116,24 +1125,50 @@ private function createArrayAdapterCachePool(ContainerBuilder $container, string
11161125
return $id;
11171126
}
11181127

1119-
/** @param string[] $connWithLogging */
1120-
private function useMiddlewaresIfAvailable(ContainerBuilder $container, array $connWithLogging): void
1121-
{
1128+
/**
1129+
* @param string[] $connWithLogging
1130+
* @param string[] $connWithProfiling
1131+
* @param string[] $connWithBacktrace
1132+
*/
1133+
private function useMiddlewaresIfAvailable(
1134+
ContainerBuilder $container,
1135+
array $connWithLogging,
1136+
array $connWithProfiling,
1137+
array $connWithBacktrace
1138+
): void {
11221139
/** @psalm-suppress UndefinedClass */
1123-
if (! class_exists(Middleware::class)) {
1140+
if (! interface_exists(MiddlewareInterface::class)) {
11241141
return;
11251142
}
11261143

1144+
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
1145+
$loader->load('middlewares.xml');
1146+
11271147
$container
11281148
->getDefinition('doctrine.dbal.logger')
11291149
->replaceArgument(0, null);
11301150

1131-
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
1132-
$loader->load('middlewares.xml');
1133-
11341151
$loggingMiddlewareAbstractDef = $container->getDefinition('doctrine.dbal.logging_middleware');
11351152
foreach ($connWithLogging as $connName) {
11361153
$loggingMiddlewareAbstractDef->addTag('doctrine.middleware', ['connection' => $connName]);
11371154
}
1155+
1156+
if ($this->isSfDebugMiddlewareAvailable()) {
1157+
$container->getDefinition('doctrine.debug_data_holder')->replaceArgument(0, $connWithBacktrace);
1158+
$debugMiddlewareAbstractDef = $container->getDefinition('doctrine.dbal.debug_middleware');
1159+
foreach ($connWithProfiling as $connName) {
1160+
$debugMiddlewareAbstractDef
1161+
->addTag('doctrine.middleware', ['connection' => $connName]);
1162+
}
1163+
} else {
1164+
$container->removeDefinition('doctrine.dbal.debug_middleware');
1165+
$container->removeDefinition('doctrine.debug_data_holder');
1166+
}
1167+
}
1168+
1169+
private function isSfDebugMiddlewareAvailable(): bool
1170+
{
1171+
/** @psalm-suppress UndefinedClass */
1172+
return interface_exists(MiddlewareInterface::class) && class_exists(SfDebugMiddleware::class);
11381173
}
11391174
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
4+
5+
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
6+
use Symfony\Bridge\Doctrine\Middleware\Debug\Query;
7+
8+
use function array_slice;
9+
use function debug_backtrace;
10+
use function in_array;
11+
12+
use const DEBUG_BACKTRACE_IGNORE_ARGS;
13+
14+
/** @psalm-suppress MissingDependency */
15+
class BacktraceDebugDataHolder extends DebugDataHolder
16+
{
17+
/** @var string[] */
18+
private $connWithBacktraces;
19+
20+
/** @var array<string, array<string, mixed>[]> */
21+
private $backtraces = [];
22+
23+
/** @param string[] $connWithBacktraces */
24+
public function __construct(array $connWithBacktraces)
25+
{
26+
$this->connWithBacktraces = $connWithBacktraces;
27+
}
28+
29+
public function reset(): void
30+
{
31+
parent::reset();
32+
33+
$this->backtraces = [];
34+
}
35+
36+
public function addQuery(string $connectionName, Query $query): void
37+
{
38+
parent::addQuery($connectionName, $query);
39+
40+
if (! in_array($connectionName, $this->connWithBacktraces, true)) {
41+
return;
42+
}
43+
44+
// array_slice to skip middleware calls in the trace
45+
$this->backtraces[$connectionName][] = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 2);
46+
}
47+
48+
/** @return array<string, array<string, mixed>[]> */
49+
public function getData(): array
50+
{
51+
$dataWithBacktraces = [];
52+
53+
$data = parent::getData();
54+
foreach ($data as $connectionName => $dataForConn) {
55+
$dataWithBacktraces[$connectionName] = $this->getDataForConnection($connectionName, $dataForConn);
56+
}
57+
58+
return $dataWithBacktraces;
59+
}
60+
61+
/**
62+
* @param mixed[][] $dataForConn
63+
*
64+
* @return mixed[][]
65+
*/
66+
private function getDataForConnection(string $connectionName, array $dataForConn): array
67+
{
68+
$data = [];
69+
70+
foreach ($dataForConn as $idx => $record) {
71+
$data[] = $this->addBacktracesIfAvailable($connectionName, $record, $idx);
72+
}
73+
74+
return $data;
75+
}
76+
77+
/**
78+
* @param mixed[] $record
79+
*
80+
* @return mixed[]
81+
*/
82+
private function addBacktracesIfAvailable(string $connectionName, array $record, int $idx): array
83+
{
84+
if (! isset($this->backtraces[$connectionName])) {
85+
return $record;
86+
}
87+
88+
$record['backtrace'] = $this->backtraces[$connectionName][$idx];
89+
90+
return $record;
91+
}
92+
}

Middleware/DebugMiddleware.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
4+
5+
use Doctrine\DBAL\Driver as DriverInterface;
6+
use Doctrine\DBAL\Driver\Middleware;
7+
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
8+
use Symfony\Bridge\Doctrine\Middleware\Debug\Driver;
9+
use Symfony\Component\Stopwatch\Stopwatch;
10+
11+
class DebugMiddleware implements Middleware, ConnectionNameAwareInterface
12+
{
13+
/** @var DebugDataHolder */
14+
private $debugDataHolder;
15+
16+
/** @var Stopwatch|null */
17+
private $stopwatch;
18+
19+
/** @var string */
20+
private $connectionName = 'default';
21+
22+
public function __construct(DebugDataHolder $debugDataHolder, ?Stopwatch $stopwatch)
23+
{
24+
$this->debugDataHolder = $debugDataHolder;
25+
$this->stopwatch = $stopwatch;
26+
}
27+
28+
public function setConnectionName(string $name): void
29+
{
30+
$this->connectionName = $name;
31+
}
32+
33+
public function wrap(DriverInterface $driver): DriverInterface
34+
{
35+
return new Driver($driver, $this->debugDataHolder, $this->stopwatch, $this->connectionName);
36+
}
37+
}

Resources/config/middlewares.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,12 @@
99
<argument type="service" id="logger" />
1010
<tag name="monolog.logger" channel="doctrine" />
1111
</service>
12+
<service id="doctrine.debug_data_holder" class="Doctrine\Bundle\DoctrineBundle\Middleware\BacktraceDebugDataHolder">
13+
<argument type="collection" />
14+
</service>
15+
<service id="doctrine.dbal.debug_middleware" class="Doctrine\Bundle\DoctrineBundle\Middleware\DebugMiddleware" abstract="true">
16+
<argument type="service" id="doctrine.debug_data_holder" />
17+
<argument type="service" id="debug.stopwatch" on-invalid="null" />
18+
</service>
1219
</services>
1320
</container>

Tests/ContainerTest.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\Common\EventManager;
1111
use Doctrine\DBAL\Configuration as DBALConfiguration;
1212
use Doctrine\DBAL\Connection;
13+
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
1314
use Doctrine\DBAL\Logging\LoggerChain;
1415
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
1516
use Doctrine\DBAL\Types\Type;
@@ -21,6 +22,7 @@
2122
use Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer;
2223
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector;
2324
use Symfony\Bridge\Doctrine\Logger\DbalLogger;
25+
use Symfony\Bridge\Doctrine\Middleware\Debug\Middleware as SfDebugMiddleware;
2426
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
2527
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator;
2628
use Symfony\Bridge\Doctrine\Validator\DoctrineLoader;
@@ -32,16 +34,22 @@
3234

3335
class ContainerTest extends TestCase
3436
{
37+
/** @group legacy */
3538
public function testContainerWithDbalOnly(): void
3639
{
3740
$kernel = new DbalTestKernel();
3841
$kernel->boot();
3942

4043
$container = $kernel->getContainer();
41-
$this->assertInstanceOf(
42-
LoggerChain::class,
43-
$container->get('doctrine.dbal.logger.chain.default')
44-
);
44+
45+
/** @psalm-suppress UndefinedClass */
46+
if (! interface_exists(MiddlewareInterface::class)) {
47+
$this->assertInstanceOf(
48+
LoggerChain::class,
49+
$container->get('doctrine.dbal.logger.chain.default')
50+
);
51+
}
52+
4553
if (class_exists(ImportCommand::class)) {
4654
self::assertTrue($container->has('doctrine.database_import_command'));
4755
} else {
@@ -57,7 +65,11 @@ public function testContainer(): void
5765

5866
$container = $this->createXmlBundleTestContainer();
5967

60-
$this->assertInstanceOf(DbalLogger::class, $container->get('doctrine.dbal.logger'));
68+
/** @psalm-suppress UndefinedClass */
69+
if (! interface_exists(MiddlewareInterface::class) || ! class_exists(SfDebugMiddleware::class)) {
70+
$this->assertInstanceOf(DbalLogger::class, $container->get('doctrine.dbal.logger'));
71+
}
72+
6173
$this->assertInstanceOf(DoctrineDataCollector::class, $container->get('data_collector.doctrine'));
6274
$this->assertInstanceOf(DBALConfiguration::class, $container->get('doctrine.dbal.default_connection.configuration'));
6375
$this->assertInstanceOf(EventManager::class, $container->get('doctrine.dbal.default_connection.event_manager'));

Tests/DependencyInjection/AbstractDoctrineExtensionTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
1313
use Doctrine\DBAL\Configuration;
1414
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
15+
use Doctrine\DBAL\Driver\Middleware;
1516
use Doctrine\DBAL\DriverManager;
1617
use Doctrine\ORM\EntityManager;
1718
use Doctrine\ORM\EntityManagerInterface;
@@ -447,6 +448,11 @@ public function testLoadMultipleConnections(): void
447448

448449
public function testLoadLogging(): void
449450
{
451+
/** @psalm-suppress UndefinedClass */
452+
if (interface_exists(Middleware::class)) {
453+
$this->markTestSkipped('This test requires Debug middleware to not be activated');
454+
}
455+
450456
$container = $this->loadContainer('dbal_logging');
451457

452458
$definition = $container->getDefinition('doctrine.dbal.log_connection.configuration');

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy