• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daycry / doctrine / 27071543208

06 Jun 2026 07:20PM UTC coverage: 93.219% (+0.8%) from 92.41%
27071543208

push

github

web-flow
Merge pull request #17 from daycry/development

Development

210 of 218 new or added lines in 9 files covered. (96.33%)

866 of 929 relevant lines covered (93.22%)

18.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.92
/src/Logging/QueryLoggerMiddleware.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Daycry\Doctrine\Logging;
6

7
use Doctrine\DBAL\Driver;
8
use Doctrine\DBAL\Driver\API\ExceptionConverter;
9
use Doctrine\DBAL\Driver\Connection;
10
use Doctrine\DBAL\Driver\Middleware;
11
use Doctrine\DBAL\Driver\Result;
12
use Doctrine\DBAL\Driver\Statement;
13
use Doctrine\DBAL\ParameterType;
14
use Doctrine\DBAL\Platforms\AbstractPlatform;
15
use Doctrine\DBAL\ServerVersionProvider;
16
use Psr\Log\LoggerInterface;
17

18
/**
19
 * DBAL middleware that logs executed queries to a PSR-3 logger, optionally only
20
 * those slower than a configured threshold. Unlike the Debug Toolbar collector
21
 * this runs in any environment, giving production a slow-query log.
22
 */
23
class QueryLoggerMiddleware implements Middleware
24
{
25
    public function __construct(
4 ✔
26
        protected LoggerInterface $logger,
27
        protected float $slowThreshold = 0.0,
28
        protected string $level = 'info',
29
    ) {
30
    }
4 ✔
31

32
    /**
33
     * Emit a log record when the query is at least as slow as the threshold.
34
     *
35
     * @param array<int|string, mixed> $params
36
     */
37
    public static function record(LoggerInterface $logger, string $level, float $threshold, string $sql, array $params, float $durationSeconds): void
3 ✔
38
    {
39
        if ($durationSeconds < $threshold) {
3 ✔
40
            return;
1 ✔
41
        }
42

43
        $logger->log($level, 'Doctrine query', [
2 ✔
44
            'sql'         => $sql,
2 ✔
45
            'params'      => $params,
2 ✔
46
            'duration_ms' => round($durationSeconds * 1000, 3),
2 ✔
47
        ]);
2 ✔
48
    }
49

50
    public function wrap(Driver $driver): Driver
4 ✔
51
    {
52
        $logger    = $this->logger;
4 ✔
53
        $threshold = $this->slowThreshold;
4 ✔
54
        $level     = $this->level;
4 ✔
55

56
        return new class ($driver, $logger, $threshold, $level) implements Driver {
4 ✔
57
            public function __construct(
58
                private readonly Driver $driver,
59
                private readonly LoggerInterface $logger,
60
                private readonly float $threshold,
61
                private readonly string $level,
62
            ) {
63
            }
4 ✔
64

65
            public function connect(array $params): Connection
66
            {
67
                $conn = $this->driver->connect($params);
4 ✔
68

69
                return new class ($conn, $this->logger, $this->threshold, $this->level) implements Connection {
4 ✔
70
                    public function __construct(
71
                        private readonly Connection $conn,
72
                        private readonly LoggerInterface $logger,
73
                        private readonly float $threshold,
74
                        private readonly string $level,
75
                    ) {
76
                    }
4 ✔
77

78
                    public function prepare(string $sql): Statement
79
                    {
80
                        return new class ($this->conn->prepare($sql), $sql, $this->logger, $this->threshold, $this->level) implements Statement {
1 ✔
81
                            /**
82
                             * @var array<int|string, mixed>
83
                             */
84
                            private array $params = [];
85

86
                            public function __construct(
87
                                private readonly Statement $stmt,
88
                                private readonly string $sql,
89
                                private readonly LoggerInterface $logger,
90
                                private readonly float $threshold,
91
                                private readonly string $level,
92
                            ) {
93
                            }
1 ✔
94

95
                            public function bindValue(int|string $param, mixed $value, ParameterType $type): void
96
                            {
97
                                $this->params[$param] = $value;
1 ✔
98
                                $this->stmt->bindValue($param, $value, $type);
1 ✔
99
                            }
100

101
                            public function execute(): Result
102
                            {
103
                                $start  = microtime(true);
1 ✔
104
                                $result = $this->stmt->execute();
1 ✔
105
                                QueryLoggerMiddleware::record($this->logger, $this->level, $this->threshold, $this->sql, $this->params, microtime(true) - $start);
1 ✔
106

107
                                return $result;
1 ✔
108
                            }
109
                        };
1 ✔
110
                    }
111

112
                    public function query(string $sql): Result
113
                    {
114
                        $start  = microtime(true);
4 ✔
115
                        $result = $this->conn->query($sql);
4 ✔
116
                        QueryLoggerMiddleware::record($this->logger, $this->level, $this->threshold, $sql, [], microtime(true) - $start);
3 ✔
117

118
                        return $result;
3 ✔
119
                    }
120

121
                    public function exec(string $sql): int|string
122
                    {
123
                        $start  = microtime(true);
1 ✔
124
                        $result = $this->conn->exec($sql);
1 ✔
125
                        QueryLoggerMiddleware::record($this->logger, $this->level, $this->threshold, $sql, [], microtime(true) - $start);
1 ✔
126

127
                        return $result;
1 ✔
128
                    }
129

130
                    public function beginTransaction(): void
131
                    {
132
                        $this->conn->beginTransaction();
1 ✔
133
                    }
134

135
                    public function commit(): void
136
                    {
137
                        $this->conn->commit();
1 ✔
138
                    }
139

140
                    public function rollBack(): void
141
                    {
142
                        $this->conn->rollBack();
1 ✔
143
                    }
144

145
                    public function lastInsertId(): int|string
146
                    {
147
                        return $this->conn->lastInsertId();
1 ✔
148
                    }
149

150
                    public function getNativeConnection()
151
                    {
152
                        return $this->conn->getNativeConnection();
1 ✔
153
                    }
154

155
                    public function quote(string $value): string
156
                    {
157
                        return $this->conn->quote($value);
1 ✔
158
                    }
159

160
                    public function getServerVersion(): string
161
                    {
162
                        return $this->conn->getServerVersion();
2 ✔
163
                    }
164

165
                    /**
166
                     * @param array<int|string, mixed> $arguments
167
                     */
168
                    public function __call(string $name, array $arguments): mixed
169
                    {
170
                        /** @var mixed */
NEW
171
                        return $this->conn->{$name}(...$arguments);
×
172
                    }
173
                };
4 ✔
174
            }
175

176
            public function getDatabasePlatform(ServerVersionProvider $versionProvider): AbstractPlatform
177
            {
178
                return $this->driver->getDatabasePlatform($versionProvider);
4 ✔
179
            }
180

181
            public function getExceptionConverter(): ExceptionConverter
182
            {
183
                return $this->driver->getExceptionConverter();
1 ✔
184
            }
185
        };
4 ✔
186
    }
187
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc