Skip to content

Commit de3f94a

Browse files
committed
Middleware: add debug tracy integration
1 parent 84bf9f5 commit de3f94a

11 files changed

+506
-143
lines changed
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Nettrine\DBAL\Middleware\Debug;
4+
5+
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
6+
use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware;
7+
use Doctrine\DBAL\Driver\Result;
8+
9+
/**
10+
* @see https://github.com/symfony/doctrine-bridge
11+
* @internal
12+
*/
13+
final class DebugConnection extends AbstractConnectionMiddleware
14+
{
15+
16+
public function __construct(
17+
ConnectionInterface $connection,
18+
private DebugStack $stack,
19+
private string $connectionName,
20+
)
21+
{
22+
parent::__construct($connection);
23+
}
24+
25+
public function prepare(string $sql): DebugStatement
26+
{
27+
return new DebugStatement(
28+
parent::prepare($sql),
29+
$this->stack,
30+
$this->connectionName,
31+
$sql
32+
);
33+
}
34+
35+
public function query(string $sql): Result
36+
{
37+
$this->stack->addQuery($this->connectionName, $query = new DebugQuery($sql));
38+
$query->start();
39+
40+
try {
41+
return parent::query($sql);
42+
} finally {
43+
$query->stop();
44+
}
45+
}
46+
47+
public function exec(string $sql): int|string
48+
{
49+
$this->stack->addQuery($this->connectionName, $query = new DebugQuery($sql));
50+
$query->start();
51+
52+
try {
53+
$affectedRows = parent::exec($sql);
54+
} finally {
55+
$query->stop();
56+
}
57+
58+
return $affectedRows;
59+
}
60+
61+
public function beginTransaction(): void
62+
{
63+
$this->stack->addQuery($this->connectionName, $query = new DebugQuery('"START TRANSACTION"'));
64+
$query->start();
65+
66+
try {
67+
parent::beginTransaction();
68+
} finally {
69+
$query->stop();
70+
}
71+
}
72+
73+
public function commit(): void
74+
{
75+
$this->stack->addQuery($this->connectionName, $query = new DebugQuery('"COMMIT"'));
76+
$query->start();
77+
78+
try {
79+
parent::commit();
80+
} finally {
81+
$query->stop();
82+
}
83+
}
84+
85+
public function rollBack(): void
86+
{
87+
$this->stack->addQuery($this->connectionName, $query = new DebugQuery('"ROLLBACK"'));
88+
$query->start();
89+
90+
try {
91+
parent::rollBack();
92+
} finally {
93+
$query->stop();
94+
}
95+
}
96+
97+
}

src/Middleware/Debug/DebugDriver.php

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Nettrine\DBAL\Middleware\Debug;
4+
5+
use Doctrine\DBAL\Driver as DriverInterface;
6+
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
7+
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
8+
9+
/**
10+
* @see https://github.com/symfony/doctrine-bridge
11+
* @internal
12+
*/
13+
final class DebugDriver extends AbstractDriverMiddleware
14+
{
15+
16+
public function __construct(
17+
DriverInterface $driver,
18+
private DebugStack $stack,
19+
private string $connectionName,
20+
)
21+
{
22+
parent::__construct($driver);
23+
}
24+
25+
/**
26+
* {@inheritDoc}
27+
*/
28+
public function connect(array $params): ConnectionInterface
29+
{
30+
$connection = parent::connect($params);
31+
32+
return new DebugConnection(
33+
$connection,
34+
$this->stack,
35+
$this->connectionName
36+
);
37+
}
38+
39+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Nettrine\DBAL\Middleware\Debug;
4+
5+
use Doctrine\DBAL\Driver as DriverInterface;
6+
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
7+
8+
/**
9+
* @see https://github.com/symfony/doctrine-bridge
10+
* @internal
11+
*/
12+
final class DebugMiddleware implements MiddlewareInterface
13+
{
14+
15+
public function __construct(
16+
private DebugStack $stack,
17+
private string $connectionName = 'default',
18+
)
19+
{
20+
}
21+
22+
public function wrap(DriverInterface $driver): DriverInterface
23+
{
24+
return new DebugDriver($driver, $this->stack, $this->connectionName);
25+
}
26+
27+
}

src/Middleware/Debug/DebugQuery.php

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Nettrine\DBAL\Middleware\Debug;
4+
5+
use Doctrine\DBAL\ParameterType;
6+
7+
/**
8+
* @see https://github.com/symfony/doctrine-bridge
9+
* @internal
10+
*/
11+
class DebugQuery
12+
{
13+
14+
/** @var array<int|string, mixed> */
15+
private array $params = [];
16+
17+
/** @var array<ParameterType|int> */
18+
private array $types = [];
19+
20+
private ?float $start = null;
21+
22+
private ?float $duration = null;
23+
24+
public function __construct(
25+
private readonly string $sql,
26+
)
27+
{
28+
}
29+
30+
public function start(): void
31+
{
32+
$this->start = microtime(true);
33+
}
34+
35+
public function stop(): void
36+
{
37+
if ($this->start !== null) {
38+
$this->duration = microtime(true) - $this->start;
39+
}
40+
}
41+
42+
public function setValue(string|int $param, mixed $value, ParameterType|int $type): void
43+
{
44+
// Numeric indexes start at 0 in profiler
45+
$idx = is_int($param) ? $param - 1 : $param;
46+
47+
$this->params[$idx] = $value;
48+
$this->types[$idx] = $type;
49+
}
50+
51+
public function getSql(): string
52+
{
53+
return $this->sql;
54+
}
55+
56+
/**
57+
* @return array<int|string, mixed>
58+
*/
59+
public function getParams(): array
60+
{
61+
return $this->params;
62+
}
63+
64+
/**
65+
* @return array<int, int|ParameterType>
66+
*/
67+
public function getTypes(): array
68+
{
69+
return $this->types;
70+
}
71+
72+
/**
73+
* Query duration in seconds.
74+
*/
75+
public function getDuration(): ?float
76+
{
77+
return $this->duration;
78+
}
79+
80+
public function __clone()
81+
{
82+
$copy = [];
83+
84+
foreach ($this->params as $param => $valueOrVariable) {
85+
$copy[$param] = $valueOrVariable;
86+
}
87+
88+
$this->params = $copy;
89+
}
90+
91+
}

src/Middleware/Debug/DebugStack.php

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Nettrine\DBAL\Middleware\Debug;
4+
5+
/**
6+
* @see https://github.com/symfony/doctrine-bridge
7+
* @internal
8+
*/
9+
class DebugStack
10+
{
11+
12+
/** @var array<string, array<int, array{sql: string, params: mixed[], types: mixed[], duration: callable|float }>> */
13+
private array $data = [];
14+
15+
public function addQuery(string $connectionName, DebugQuery $query): void
16+
{
17+
$this->data[$connectionName][] = [
18+
'sql' => $query->getSql(),
19+
'params' => $query->getParams(),
20+
'types' => $query->getTypes(),
21+
'duration' => $query->getDuration(...), // stop() may not be called at this point
22+
];
23+
}
24+
25+
/**
26+
* @return array<string, array<int, array{sql: string, params: mixed[], types: mixed[], duration: float }>>
27+
*/
28+
public function getData(): array
29+
{
30+
$data = $this->data;
31+
32+
foreach ($data as $connectionName => $queries) {
33+
foreach ($queries as $idx => $query) {
34+
if (is_callable($query['duration'])) {
35+
$data[$connectionName][$idx]['duration'] = $query['duration']();
36+
}
37+
}
38+
}
39+
40+
return $data;
41+
}
42+
43+
public function reset(): void
44+
{
45+
$this->data = [];
46+
}
47+
48+
}
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Nettrine\DBAL\Middleware\Debug;
4+
5+
use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware;
6+
use Doctrine\DBAL\Driver\Result as ResultInterface;
7+
use Doctrine\DBAL\Driver\Statement as StatementInterface;
8+
use Doctrine\DBAL\ParameterType;
9+
10+
/**
11+
* @see https://github.com/symfony/doctrine-bridge
12+
* @internal
13+
*/
14+
final class DebugStatement extends AbstractStatementMiddleware
15+
{
16+
17+
private DebugQuery $query;
18+
19+
public function __construct(
20+
StatementInterface $statement,
21+
private DebugStack $stack,
22+
private readonly string $connectionName,
23+
string $sql,
24+
)
25+
{
26+
parent::__construct($statement);
27+
28+
$this->query = new DebugQuery($sql);
29+
}
30+
31+
public function bindValue(int|string $param, mixed $value, ParameterType $type): void
32+
{
33+
$this->query->setValue($param, $value, $type);
34+
35+
parent::bindValue($param, $value, $type);
36+
}
37+
38+
public function execute(): ResultInterface
39+
{
40+
// clone to prevent variables by reference to change
41+
$this->stack->addQuery($this->connectionName, $query = clone $this->query);
42+
$query->start();
43+
44+
try {
45+
return parent::execute();
46+
} finally {
47+
$query->stop();
48+
}
49+
}
50+
51+
}

0 commit comments

Comments
 (0)