Skip to content

Commit 29543f8

Browse files
committed
Support tracing for Doctrine DBAL 2.10+
1 parent 79837b5 commit 29543f8

18 files changed

+540
-109
lines changed

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
"symfony/security-core": "^3.4.43||^4.4.11||^5.0.11"
3333
},
3434
"require-dev": {
35-
"doctrine/doctrine-bundle": "^2.2",
36-
"friendsofphp/php-cs-fixer": "^2.17",
35+
"doctrine/dbal": "^2.10||^3.0",
36+
"doctrine/doctrine-bundle": "^1.12||^2.0",
37+
"friendsofphp/php-cs-fixer": "^2.18",
3738
"jangregor/phpstan-prophecy": "^0.8",
3839
"monolog/monolog": "^1.3||^2.0",
3940
"phpspec/prophecy": "!=1.11.0",

phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ parameters:
1010
- tests/End2End/App
1111
dynamicConstantNames:
1212
- Symfony\Component\HttpKernel\Kernel::VERSION
13+
- Doctrine\DBAL\Version::VERSION

psalm-baseline.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<files psalm-version="4.3.0@6f916553a8de3c5c2483162b6cc01b21b24e4d1d">
3+
<file src="src/Tracing/Doctrine/DBAL/Legacy/TracingDriverConnectionForV2_11.php">
4+
<MethodSignatureMismatch occurrences="3">
5+
<code>$sql</code>
6+
<code>$sql</code>
7+
<code>TracingDriverConnectionForV2_11</code>
8+
</MethodSignatureMismatch>
9+
<ParamNameMismatch occurrences="1">
10+
<code>$value</code>
11+
</ParamNameMismatch>
12+
<UnimplementedInterfaceMethod occurrences="1">
13+
<code>TracingDriverConnectionForV2_11</code>
14+
</UnimplementedInterfaceMethod>
15+
</file>
16+
<file src="src/Tracing/Doctrine/DBAL/TracingDriver.php">
17+
<TooManyArguments occurrences="1">
18+
<code>getSchemaManager</code>
19+
</TooManyArguments>
20+
<UndefinedClass occurrences="1">
21+
<code>ExceptionConverter</code>
22+
</UndefinedClass>
23+
</file>
24+
</files>

psalm.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
77
xmlns="https://getpsalm.org/schema/config"
88
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
9+
errorBaseline="psalm-baseline.xml"
910
>
1011
<projectFiles>
1112
<directory name="src" />

src/DependencyInjection/Compiler/DbalTracingDriverPass.php

Lines changed: 0 additions & 58 deletions
This file was deleted.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\DependencyInjection\Compiler;
6+
7+
use Jean85\PrettyVersions;
8+
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\ConnectionConfigurator;
9+
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
10+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
11+
use Symfony\Component\DependencyInjection\ContainerBuilder;
12+
use Symfony\Component\DependencyInjection\Definition;
13+
use Symfony\Component\DependencyInjection\Reference;
14+
15+
final class DbalTracingPass implements CompilerPassInterface
16+
{
17+
/**
18+
* This is the format used by the DoctrineBundle bundle to register the
19+
* services for each connection.
20+
*/
21+
private const CONNECTION_SERVICE_NAME_FORMAT = 'doctrine.dbal.%s_connection';
22+
23+
/**
24+
* This is the format used by the DoctrineBundle bundle to register the
25+
* services for each connection's configuration.
26+
*/
27+
private const CONNECTION_CONFIG_SERVICE_NAME_FORMAT = 'doctrine.dbal.%s_connection.configuration';
28+
29+
/**
30+
* {@inheritdoc}
31+
*/
32+
public function process(ContainerBuilder $container): void
33+
{
34+
if (!$container->hasParameter('doctrine.connections')) {
35+
return;
36+
}
37+
38+
/** @var string[] $connections */
39+
$connections = $container->getParameter('doctrine.connections');
40+
41+
/** @var string[] $connectionsToTrace */
42+
$connectionsToTrace = $container->getParameter('sentry.tracing.dbal.connections');
43+
44+
foreach ($connectionsToTrace as $connectionName) {
45+
if (!\in_array(sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName), $connections, true)) {
46+
continue;
47+
}
48+
49+
if (version_compare(PrettyVersions::getVersion('doctrine/dbal')->getPrettyVersion(), '3.0.0', '>=')) {
50+
$this->configureConnectionForDoctrineDBALVersion3($container, $connectionName);
51+
} else {
52+
$this->configureConnectionForDoctrineDBALVersion2($container, $connectionName);
53+
}
54+
}
55+
}
56+
57+
private function configureConnectionForDoctrineDBALVersion3(ContainerBuilder $container, string $connectionName): void
58+
{
59+
$configurationDefinition = $container->getDefinition(sprintf(self::CONNECTION_CONFIG_SERVICE_NAME_FORMAT, $connectionName));
60+
$setMiddlewaresMethodCallArguments = $this->getSetMiddlewaresMethodCallArguments($configurationDefinition);
61+
$setMiddlewaresMethodCallArguments[0] = array_merge($setMiddlewaresMethodCallArguments[0] ?? [], [new Reference(TracingDriverMiddleware::class)]);
62+
63+
$configurationDefinition
64+
->removeMethodCall('setMiddlewares')
65+
->addMethodCall('setMiddlewares', $setMiddlewaresMethodCallArguments);
66+
}
67+
68+
private function configureConnectionForDoctrineDBALVersion2(ContainerBuilder $container, string $connectionName): void
69+
{
70+
$connectionDefinition = $container->getDefinition(sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName));
71+
$connectionDefinition->setConfigurator([new Reference(ConnectionConfigurator::class), 'configure']);
72+
}
73+
74+
/**
75+
* @return mixed[]
76+
*/
77+
private function getSetMiddlewaresMethodCallArguments(Definition $definition): array
78+
{
79+
foreach ($definition->getMethodCalls() as $methodCall) {
80+
if ('setMiddlewares' === $methodCall[0]) {
81+
return $methodCall[1];
82+
}
83+
}
84+
85+
return [];
86+
}
87+
}

src/DependencyInjection/Configuration.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,8 @@ private function addDistributedTracingSection(ArrayNodeDefinition $rootNode): vo
154154
->addDefaultsIfNotSet()
155155
->children()
156156
->arrayNode('dbal')
157-
->addDefaultsIfNotSet()
158-
->fixXmlConfig('connection')
159157
->canBeEnabled()
158+
->fixXmlConfig('connection')
160159
->children()
161160
->arrayNode('connections')
162161
->defaultValue(class_exists(DoctrineBundle::class) ? ['%doctrine.default_connection%'] : [])

src/Resources/config/services.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
<argument type="service" id="Sentry\State\HubInterface" />
6868
</service>
6969

70+
<service id="Sentry\SentryBundle\Tracing\Doctrine\DBAL\ConnectionConfigurator" class="Sentry\SentryBundle\Tracing\Doctrine\DBAL\ConnectionConfigurator">
71+
<argument type="service" id="Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware" />
72+
</service>
73+
7074
<service id="Sentry\Integration\RequestFetcherInterface" class="Sentry\SentryBundle\Integration\RequestFetcher">
7175
<argument type="service" id="Symfony\Component\HttpFoundation\RequestStack" />
7276
<argument type="service" id="Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface" on-invalid="null" />

src/SentryBundle.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Sentry\SentryBundle;
66

7-
use Sentry\SentryBundle\DependencyInjection\Compiler\DbalTracingDriverPass;
7+
use Sentry\SentryBundle\DependencyInjection\Compiler\DbalTracingPass;
88
use Symfony\Component\DependencyInjection\ContainerBuilder;
99
use Symfony\Component\HttpKernel\Bundle\Bundle;
1010

@@ -16,6 +16,6 @@ public function build(ContainerBuilder $container): void
1616
{
1717
parent::build($container);
1818

19-
$container->addCompilerPass(new DbalTracingDriverPass());
19+
$container->addCompilerPass(new DbalTracingPass());
2020
}
2121
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Tracing\Doctrine\DBAL\Compatibility;
6+
7+
/**
8+
* @internal
9+
*/
10+
interface ExceptionConverterDriverInterface
11+
{
12+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Tracing\Doctrine\DBAL\Compatibility;
6+
7+
use Doctrine\DBAL\Driver as DriverInterface;
8+
9+
/**
10+
* @internal
11+
*/
12+
interface MiddlewareInterface
13+
{
14+
public function wrap(DriverInterface $driver): DriverInterface;
15+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Tracing\Doctrine\DBAL;
6+
7+
use Doctrine\DBAL\Connection;
8+
9+
/**
10+
* @internal
11+
*/
12+
final class ConnectionConfigurator
13+
{
14+
/**
15+
* @var TracingDriverMiddleware
16+
*/
17+
private $tracingDriverMiddleware;
18+
19+
/**
20+
* Constructor.
21+
*
22+
* @param TracingDriverMiddleware $tracingDriverMiddleware The driver middleware
23+
*/
24+
public function __construct(TracingDriverMiddleware $tracingDriverMiddleware)
25+
{
26+
$this->tracingDriverMiddleware = $tracingDriverMiddleware;
27+
}
28+
29+
/**
30+
* Configures the given connecton by wrapping its driver into an instance
31+
* of the {@see TracingDriver} class. This is done using the reflection,
32+
* and as such should be limited only to the versions of Doctrine DBAL that
33+
* are lower than 3.0. Since 3.0 onwards, the concept of driver middlewares
34+
* has been introduced which allows the same thing we're doing here, but in
35+
* a more appropriate and "legal" way.
36+
*
37+
* @param Connection $connection The connection to configure
38+
*/
39+
public function configure(Connection $connection): void
40+
{
41+
$reflectionProperty = new \ReflectionProperty($connection, '_driver');
42+
$reflectionProperty->setAccessible(true);
43+
$reflectionProperty->setValue($connection, $this->tracingDriverMiddleware->wrap($reflectionProperty->getValue($connection)));
44+
$reflectionProperty->setAccessible(false);
45+
}
46+
}

0 commit comments

Comments
 (0)